Pronouns labels on Bluesky

Compare changes

Choose any two refs to compare.

+2
.gitignore
···
.env
node_modules
cursor.txt
···
.env
node_modules
cursor.txt
+
*.log
+
labels.db*
-19
src/agent.ts
···
-
import { AtpAgent } from "@atproto/api";
-
import "dotenv/config";
-
-
export const getAgent = async () => {
-
const agent = new AtpAgent({
-
service: process.env.BSKY_SERVICE ?? "https://bsky.social",
-
});
-
-
await agent.login({
-
identifier: process.env.BSKY_IDENTIFIER!,
-
password: process.env.BSKY_PASSWORD!,
-
});
-
-
return agent;
-
};
-
-
AtpAgent.configure({
-
appLabelers: [process.env.DID ?? ""],
-
});
···
+2 -2
tsconfig.json
···
"compilerOptions": {
"strict": true,
"target": "ESNext",
-
"module": "ESNext",
-
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
},
···
"compilerOptions": {
"strict": true,
"target": "ESNext",
+
"module": "nodenext",
+
"moduleResolution": "nodenext",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
},
+1
.nvmrc
···
···
+
22.9.0
+7 -11
src/main.ts
···
import { DID, URL } from "./constants.js";
import fs from "node:fs";
import { Jetstream } from "@skyware/jetstream";
-
import { AppBskyFeedLike } from "@atcute/client/lexicons";
let intervalID: NodeJS.Timeout;
const cursorFile = fs.readFileSync("cursor.txt", "utf8");
-
if (cursorFile) console.log(`Initiate firehose at cursor ${cursorFile}`);
const jetstream = new Jetstream({
endpoint: URL,
···
jetstream.on("open", () => {
intervalID = setInterval(() => {
-
if (jetstream.cursor) {
-
fs.writeFile("cursor.txt", jetstream.cursor.toString(), (err) => {
-
if (err) console.log(err);
-
});
-
}
}, 60000);
});
jetstream.on("error", (err) => console.error(err));
-
jetstream.on("close", () => clearInterval(intervalID));
jetstream.onCreate("app.bsky.feed.like", (event) => {
-
const record = event.commit.record as AppBskyFeedLike.Record;
-
if (record.subject?.uri?.includes(`${DID}/app.bsky.feed.post`))
-
label(event.did, record.subject.uri.split("/").pop()!);
});
jetstream.start();
···
import { DID, URL } from "./constants.js";
import fs from "node:fs";
import { Jetstream } from "@skyware/jetstream";
let intervalID: NodeJS.Timeout;
const cursorFile = fs.readFileSync("cursor.txt", "utf8");
+
if (cursorFile) console.log(`Initiate jetstream at cursor ${cursorFile}`);
const jetstream = new Jetstream({
endpoint: URL,
···
jetstream.on("open", () => {
intervalID = setInterval(() => {
+
if (!jetstream.cursor) return;
+
fs.writeFile("cursor.txt", jetstream.cursor.toString(), (err) => {
+
if (err) console.log(err);
+
});
}, 60000);
});
jetstream.on("error", (err) => console.error(err));
jetstream.on("close", () => clearInterval(intervalID));
jetstream.onCreate("app.bsky.feed.like", (event) => {
+
if (event.commit.record.subject.uri.includes(`${DID}/app.bsky.feed.post`))
+
label(event.did, event.commit.record.subject.uri.split("/").pop()!);
});
jetstream.start();
-32
README.md
···
-
## Configuration
-
-
Run `npx @skyware/labeler setup` to convert an existing account into a labeler.
-
-
Create a `.env` file:
-
-
```Dotenv
-
DID = "did:plc:xxx"
-
SIGNING_KEY = "xxx"
-
```
-
-
A `cursor.txt` also needs to be present. It can be left empty, and will update the file every minute with a new cursor.
-
-
Create labels with `npx @skyware/labeler label add` and edit `src/constants.ts` with the related post rkeys and label IDs.
-
-
The server has to be reachable outside your local network using the URL you provided during the account setup (typically, using a reverse proxy such as [Caddy](https://caddyserver.com/)):
-
-
```Caddyfile
-
labeler.example.com {
-
reverse_proxy 127.0.0.1:4001
-
}
-
```
-
-
## Installation & Usage
-
-
```sh
-
npm i
-
```
-
-
```sh
-
npm start
-
```
···
+6
site/postcss.config.js
···
···
+
export default {
+
plugins: {
+
tailwindcss: {},
+
autoprefixer: {},
+
},
+
}
+23
site/src/index.css
···
···
+
@tailwind base;
+
@tailwind components;
+
@tailwind utilities;
+
+
html {
+
--scroll-border: rgb(24 24 27);
+
--scroll-track: rgb(64 64 64);
+
--scroll-thumb: rgb(75 75 75);
+
+
scrollbar-width: thin;
+
scroll-behavior: smooth;
+
}
+
*::-webkit-scrollbar {
+
width: 5px;
+
background-color: var(--scroll-track);
+
}
+
*::-webkit-scrollbar-track {
+
border: 1px solid var(--scroll-border);
+
background-color: var(--scroll-track);
+
}
+
*::-webkit-scrollbar-thumb {
+
background-color: var(--scroll-thumb);
+
}
+15
site/src/index.tsx
···
···
+
/* @refresh reload */
+
import './index.css';
+
import { render } from 'solid-js/web';
+
+
import App from './App';
+
+
const root = document.getElementById('root');
+
+
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
+
throw new Error(
+
'Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?',
+
);
+
}
+
+
render(() => <App />, root!);
+15
site/tailwind.config.ts
···
···
+
import type { Config } from 'tailwindcss';
+
+
const config: Config = {
+
content: [
+
'./index.html',
+
'./src/**/*.{js,ts,jsx,tsx,css,md,mdx,html,json,scss}',
+
],
+
darkMode: 'class',
+
theme: {
+
extend: {},
+
},
+
plugins: [],
+
};
+
+
export default config;
+17
site/tsconfig.json
···
···
+
{
+
"compilerOptions": {
+
"target": "ESNext",
+
"module": "ESNext",
+
"strict": true,
+
"moduleResolution": "nodenext",
+
"allowSyntheticDefaultImports": true,
+
"esModuleInterop": true,
+
"jsx": "preserve",
+
"jsxImportSource": "solid-js",
+
"types": [
+
"vite/client"
+
],
+
"noEmit": true,
+
"isolatedModules": true
+
}
+
}
+20
site/vite.config.ts
···
···
+
import { defineConfig } from 'vite';
+
import solidPlugin from 'vite-plugin-solid';
+
// import devtools from 'solid-devtools/vite';
+
+
export default defineConfig({
+
plugins: [
+
/*
+
Uncomment the following line to enable solid-devtools.
+
For more info see https://github.com/thetarnav/solid-devtools/tree/main/packages/extension#readme
+
*/
+
// devtools(),
+
solidPlugin(),
+
],
+
server: {
+
port: 3000,
+
},
+
build: {
+
target: 'esnext',
+
},
+
});
site/src/assets/favicon.ico

This is a binary file and will not be displayed.

+1 -1
LICENSE
···
-
Copyright (c) 2024 Juliet Philippe <notjuliet@riseup.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
···
+
Copyright (c) 2024 Juliet Philippe <m@juli.ee>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
+6 -6
pnpm-lock.yaml
···
version: 16.4.7
devDependencies:
'@types/node':
-
specifier: ^22.10.1
-
version: 22.10.1
typescript:
specifier: ^5.7.2
version: 5.7.2
···
resolution: {integrity: sha512-5Xsjly8Crvi+vILmFkObNs5y/FeNj/mIztcm1qfNafUzwv9n9B7O5MQmmg3aZfnalmQWsYfs2BgVlaBBBni5bw==}
hasBin: true
-
'@types/node@22.10.1':
-
resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==}
'@types/ws@8.5.13':
resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
···
- bufferutil
- utf-8-validate
-
'@types/node@22.10.1':
dependencies:
undici-types: 6.20.0
'@types/ws@8.5.13':
dependencies:
-
'@types/node': 22.10.1
abstract-logging@2.0.1: {}
···
version: 16.4.7
devDependencies:
'@types/node':
+
specifier: ^22.10.2
+
version: 22.10.2
typescript:
specifier: ^5.7.2
version: 5.7.2
···
resolution: {integrity: sha512-5Xsjly8Crvi+vILmFkObNs5y/FeNj/mIztcm1qfNafUzwv9n9B7O5MQmmg3aZfnalmQWsYfs2BgVlaBBBni5bw==}
hasBin: true
+
'@types/node@22.10.2':
+
resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==}
'@types/ws@8.5.13':
resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
···
- bufferutil
- utf-8-validate
+
'@types/node@22.10.2':
dependencies:
undici-types: 6.20.0
'@types/ws@8.5.13':
dependencies:
+
'@types/node': 22.10.2
abstract-logging@2.0.1: {}
+3
site/src/constants.ts
···
"xe/xer": "3lclctffbhc2n",
"vae/vaem": "3lclcwgyqms2j",
"spoot/spoots": "3lclcwq4lik2j",
};
···
"xe/xer": "3lclctffbhc2n",
"vae/vaem": "3lclcwgyqms2j",
"spoot/spoots": "3lclcwq4lik2j",
+
"fae/faem": "3ld2lwdi2ak2o",
+
"dem/dems": "3ld2lwi6dss2o",
+
"þey/þem": "3ld2lwozoek2o",
};
+3
src/constants.ts
···
"3lclctffbhc2n": "xe-xer",
"3lclcwgyqms2j": "vae-vaem",
"3lclcwq4lik2j": "spoot-spoots",
};
···
"3lclctffbhc2n": "xe-xer",
"3lclcwgyqms2j": "vae-vaem",
"3lclcwq4lik2j": "spoot-spoots",
+
"3ld2lwdi2ak2o": "fae-faem",
+
"3ld2lwi6dss2o": "dem-dems",
+
"3ld2lwozoek2o": "thorn-ey-em",
};
+2 -2
site/src/App.tsx
···
const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 1;
const basePostUrl =
isSafari && isTouchDevice
-
? "bluesky:///profile/pronouns.diy/post/"
-
: "https://bsky.app/profile/pronouns.diy/post/";
const [search, setSearch] = createSignal("");
···
const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 1;
const basePostUrl =
isSafari && isTouchDevice
+
? "bluesky:///profile/did:plc:wkoofae5uytcm7bjncmev6n6/post/"
+
: "https://bsky.app/profile/did:plc:wkoofae5uytcm7bjncmev6n6/post/";
const [search, setSearch] = createSignal("");