5 Commits

Author SHA1 Message Date
Juni
f5cfd73d1c Update README.md 2024-10-27 15:53:22 +00:00
Juni
052109ec4c Update README.md 2024-10-27 15:52:44 +00:00
juni
aedd4a5219 basic updates for pushing fork 2024-10-27 11:45:13 -04:00
juni
276d3b0663 fix issue with spoiler tags being sent 2024-10-25 15:42:59 -04:00
juni
b081b71e7d make work with node js instead cause serverless is stupid 2024-10-25 14:36:48 -04:00
14 changed files with 97 additions and 87 deletions

View File

@@ -1,21 +1,23 @@
# VixBluesky 🛠️ (Inspired by [FixTweet](https://github.com/FixTweet/FixTweet)) # girlcockbsky Fork of [VixBluesky](https://github.com/Rapougnac/VixBluesky) 🛠️ (Inspired by [FixTweet](https://github.com/FixTweet/FixTweet))
> [!IMPORTANT] > [!IMPORTANT]
> This is a fork of [FixBluesky](https://github.com/ThornbushHQ/FixBluesky) by [@ItsRauf](https://www.github.com/ItsRauf). > This is a fork of [VixBluesky](https://github.com/Rapougnac/VixBluesky) by [@Lexedia](https://www.github.com/Rapougnac), which is a fork of [FixBluesky](https://github.com/ThornbushHQ/FixBluesky) by [@ItsRauf](https://www.github.com/ItsRauf).
> All credits go to them for the original idea and implementation. > All credits go to them for the original idea and implementation.
Converted to nodejs cause serverless design's proprietary-ness annoys me and i wanted to make a funny joke after seeing girlcockx for twitter.
Embed Bluesky links in Discord. Embed Bluesky links in Discord.
## How To Use? ## How To Use?
#### Simply append `x` at the end of `bsky.app`. #### Simply append `girlcock` at the front of `bsky.app`.
## Direct Links ## Direct Links
You want to link to a media directly? You can prepend `r.` to the URL to get a direct link. You want to link to a media directly? You can prepend `r.` to the URL to get a direct link.
![Direct Link](./.github/README/raw-media.png)
## Authors ## Authors
- [@ItsRauf](https://www.github.com/ItsRauf) - Original author - [@ItsRauf](https://www.github.com/ItsRauf) - Original author
- [@Lexedia](https://www.github.com/Rapougnac) - [@Lexedia](https://www.github.com/Rapougnac) - Also Original author
- [@juni.pet](https://github.com/Juniteevee) - depraved critter

28
docker-compose.yaml Normal file
View File

@@ -0,0 +1,28 @@
services:
api:
build:
context: ./pkgs/api/.
dockerfile: Dockerfile
ports:
- "2598:3000"
environment:
- NODE_ENV=production
- PORT=3000
- EMPTY_REDIR=https://bsky.app/profile/juni.pet/post/3l3e53fzazy2n
restart: unless-stopped
app:
build:
context: ./pkgs/app/.
dockerfile: Dockerfile
ports:
- "2599:3000"
environment:
- BSKY_SERVICE_URL=https://bsky.social
- VIXBLUESKY_APP_DOMAIN=girlcockbsky.app
- VIXBLUESKY_API_URL=http://localhost:2598/
- BSKY_AUTH_USERNAME=YOUR_OWN_USERNAME
- BSKY_AUTH_PASSWORD=YOUR_APP_PASSWORD_SHOULD_COME_FROM_ENV
- PORT=3000
- EMPTY_REDIR=https://bsky.app/profile/juni.pet/post/3l3e53fzazy2n
restart: unless-stopped

View File

@@ -22,17 +22,18 @@ declare global {
interface ProcessEnv { interface ProcessEnv {
readonly NODE_ENV: Env; readonly NODE_ENV: Env;
readonly PORT: string | undefined; readonly PORT: string | undefined;
readonly EMPTY_REDIR: string;
} }
} }
} }
const { NODE_ENV: env = "development", PORT } = process.env; const { NODE_ENV: env = "development", PORT, EMPTY_REDIR } = process.env;
const app = fastify({ logger: envToLogger[env] }); const app = fastify({ logger: envToLogger[env] });
app.addContentTypeParser("*", (_, __, done) => done(null)); app.addContentTypeParser("*", (_, __, done) => done(null));
app.get("/", async (_, res) => res.redirect("https://bskyx.app")); app.get("/", async (_, res) => res.redirect(EMPTY_REDIR));
// serve 0 bytes favicon so browsers don't spam the server // serve 0 bytes favicon so browsers don't spam the server
app.get("/favicon.ico", (_, res) => res.send("")); app.get("/favicon.ico", (_, res) => res.send(""));

14
pkgs/app/Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
#FROM node:22-alpine
FROM node:22.2.0-slim
WORKDIR /app
COPY package*.json ./
RUN npm i -g pnpm && pnpm install
COPY . .
#RUN pnpm run dev
CMD [ "pnpm", "dev" ]

View File

@@ -1,17 +1,20 @@
{ {
"scripts": { "scripts": {
"dev": "wrangler dev src/index.ts", "dev": "tsx watch src/index.ts"
"deploy": "wrangler deploy --minify src/index.ts"
}, },
"dependencies": { "dependencies": {
"@atcute/bluesky": "^1.0.7", "@atcute/bluesky": "^1.0.7",
"@atcute/client": "^2.0.3", "@atcute/client": "^2.0.3",
"hono": "^4.5.1" "hono": "^4.5.1",
"@hono/node-server": "^1.13.2",
"node-cache": "^5.1.2"
}, },
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "^4.20230628.0", "@cloudflare/workers-types": "^4.20230628.0",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"typescript": "^5.1.6", "typescript": "^5.1.6",
"wrangler": "^3.75.0" "wrangler": "^3.75.0",
"@types/node": "^20.11.17",
"tsx": "^4.7.1"
} }
} }

View File

@@ -1,18 +0,0 @@
import { XRPC } from '@atcute/client';
import type { KVNamespace } from '@cloudflare/workers-types';
declare global {
interface Env {
Bindings: {
BSKY_SERVICE_URL: string;
BSKY_AUTH_USERNAME: string;
BSKY_AUTH_PASSWORD: string;
VIXBLUESKY_APP_DOMAIN: string;
VIXBLUESKY_API_URL: string;
bskyx: KVNamespace;
};
Variables: {
Agent: XRPC;
};
}
}

View File

@@ -1,3 +1,4 @@
import { serve } from '@hono/node-server'
import { Hono } from 'hono'; import { Hono } from 'hono';
import { XRPC, CredentialManager, AtpSessionData } from '@atcute/client'; import { XRPC, CredentialManager, AtpSessionData } from '@atcute/client';
import '@atcute/bluesky/lexicons'; import '@atcute/bluesky/lexicons';
@@ -7,32 +8,34 @@ import { getOEmbed } from './routes/getOEmbed';
import { getProfileData } from './routes/getProfileData'; import { getProfileData } from './routes/getProfileData';
import { getProfile } from './routes/getProfile'; import { getProfile } from './routes/getProfile';
import { HTTPException } from 'hono/http-exception'; import { HTTPException } from 'hono/http-exception';
const NodeCache = require( "node-cache" );
const app = new Hono<Env>(); const app = new Hono();
const myCache = new NodeCache();
app.use('*', async (c, next) => { app.use('*', async (c, next) => {
const creds = new CredentialManager({ const creds = new CredentialManager({
service: c.env.BSKY_SERVICE_URL, service: process.env.BSKY_SERVICE_URL,
onRefresh(session) { onRefresh(session) {
return c.env.bskyx.put('session', JSON.stringify(session)); return myCache.set('session', JSON.stringify(session));
}, },
onExpired(session) { onExpired(session) {
return c.env.bskyx.delete('session'); return myCache.del('session');
}, },
onSessionUpdate(session) { onSessionUpdate(session) {
return c.env.bskyx.put('session', JSON.stringify(session)); return myCache.set('session', JSON.stringify(session));
}, },
}); });
const agent = new XRPC({ handler: creds }); const agent = new XRPC({ handler: creds });
try { try {
const rawSession = await c.env.bskyx.get('session'); const rawSession = myCache.get('session');
if (rawSession) { if(rawSession) {
const session = JSON.parse(rawSession) as AtpSessionData; const session = JSON.parse(rawSession) as AtpSessionData;
await creds.resume(session); await creds.resume(session);
} else { } else {
await creds.login({ await creds.login({
identifier: c.env.BSKY_AUTH_USERNAME, identifier: process.env.BSKY_AUTH_USERNAME,
password: c.env.BSKY_AUTH_PASSWORD, password: process.env.BSKY_AUTH_PASSWORD,
}); });
} }
c.set('Agent', agent); c.set('Agent', agent);
@@ -48,7 +51,7 @@ app.use('*', async (c, next) => {
}); });
app.get('/', async (c) => { app.get('/', async (c) => {
return c.redirect('https://github.com/Rapougnac/VixBluesky'); return c.redirect(process.env.EMPTY_REDIR);
}); });
app.get('/profile/:user/post/:post', getPost); app.get('/profile/:user/post/:post', getPost);
@@ -65,4 +68,7 @@ app.get('/https://bsky.app/profile/:user/json', getProfileData);
app.get('/oembed', getOEmbed); app.get('/oembed', getOEmbed);
export default app; serve({
fetch: app.fetch,
port: process.env.PORT,
})

View File

@@ -8,7 +8,9 @@ export function parseEmbedDescription(post: AppBskyFeedDefs.PostView): string {
checkType('app.bsky.embed.recordWithMedia#view', post.embed)); checkType('app.bsky.embed.recordWithMedia#view', post.embed));
// @ts-expect-error // @ts-expect-error
const embed = post.embed.record?.record ?? post.embed.record; let embed;
if(isQuote)
embed = post.embed.record?.record ?? post.embed.record;
return isQuote return isQuote
? // @ts-expect-error ? // @ts-expect-error

View File

@@ -11,8 +11,8 @@ export const getOEmbed: Handler<Env, '/oembed'> = async (c) => {
const avatar = c.req.query('avatar'); const avatar = c.req.query('avatar');
const defaults = { const defaults = {
provider_name: 'VixBluesky', provider_name: 'girlcockbsky',
provider_url: 'https://bskyx.app/', provider_url: 'https://girlcockbsky.app/',
thumbnail_width: 1000, thumbnail_width: 1000,
thumbnail_height: 1000, thumbnail_height: 1000,
}; };
@@ -42,7 +42,7 @@ export const getOEmbed: Handler<Env, '/oembed'> = async (c) => {
const { replies, reposts, likes, description } = c.req.query(); const { replies, reposts, likes, description } = c.req.query();
return c.json({ return c.json({
...defaults, ...defaults,
provider_name: `VixBluesky\n\n🗨 ${replies} ♻️ ${reposts} 💙 ${likes}`, provider_name: `girlcockbsky\n\n🗨 ${replies} ♻️ ${reposts} 💙 ${likes}`,
description, description,
title: description, title: description,
author_name: description, author_name: description,

View File

@@ -29,7 +29,8 @@ export const getPost: Handler<
Env, Env,
'/profile/:user/post/:post' | '/https://bsky.app/profile/:user/post/:post' '/profile/:user/post/:post' | '/https://bsky.app/profile/:user/post/:post'
> = async (c) => { > = async (c) => {
const { user, post } = c.req.param(); let { user, post } = c.req.param();
post = post.replaceAll('|', '');
const isDirect = c.req.query('direct'); const isDirect = c.req.query('direct');
const agent = c.get('Agent'); const agent = c.get('Agent');
@@ -67,9 +68,9 @@ export const getPost: Handler<
<Post <Post
post={fetchedPost} post={fetchedPost}
url={c.req.path} url={c.req.path}
appDomain={c.env.VIXBLUESKY_APP_DOMAIN} appDomain={process.env.VIXBLUESKY_APP_DOMAIN}
videoMetadata={videoMetaData} videoMetadata={videoMetaData}
apiUrl={c.env.VIXBLUESKY_API_URL} apiUrl={process.env.VIXBLUESKY_API_URL}
images={images} images={images}
/>, />,
); );

View File

@@ -7,7 +7,8 @@ export const getProfile: Handler<
Env, Env,
'/profile/:user' | '/https://bsky.app/profile/:user' '/profile/:user' | '/https://bsky.app/profile/:user'
> = async (c) => { > = async (c) => {
const { user } = c.req.param(); let { user } = c.req.param();
user = user.replaceAll('|', '');
const agent = c.get('Agent'); const agent = c.get('Agent');
try { try {
var { data } = await fetchProfile(agent, { user }); var { data } = await fetchProfile(agent, { user });
@@ -21,7 +22,7 @@ export const getProfile: Handler<
<Profile <Profile
profile={data} profile={data}
url={c.req.path} url={c.req.path}
appDomain={c.env.VIXBLUESKY_APP_DOMAIN} appDomain={process.env.VIXBLUESKY_APP_DOMAIN}
/>, />,
); );
}; };

View File

@@ -1,13 +1,14 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "NodeNext",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"strict": true, "strict": true,
"lib": ["esnext"], "verbatimModuleSyntax": true,
"types": ["@cloudflare/workers-types"], "skipLibCheck": true,
"types": [
"node"
],
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "hono/jsx" "jsxImportSource": "hono/jsx",
} }
} }

View File

@@ -1,16 +0,0 @@
name = "vixbluesky"
main = "src/index.ts"
compatibility_date = "2023-01-01"
workers_dev = false
route = { pattern = "bskyx.app/*", zone_name = "bskyx.app" }
[vars]
BSKY_SERVICE_URL="https://bsky.social/"
VIXBLUESKY_APP_DOMAIN="bskyx.app"
VIXBLUESKY_API_URL="https://api.bskyx.app/"
[[kv_namespaces]]
binding = "bskyx"
id = "ee913536e1fb47dcb9fa6275725f5a6f"
preview_id = "5e180765d24346c9b238be57d9c7001f"

View File

@@ -1,15 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [
pkgs.nodejs
pkgs.python3
pkgs.python3Packages.pip
pkgs.python3Packages.virtualenv
pkgs.sqlite
pkgs.gcc
pkgs.gnumake
pkgs.pnpm
pkgs.wrangler
];
}