···11+CREATE TABLE `labeler_cursors` (
22+ `labeler_id` text,
33+ `cursor` integer NOT NULL
44+);
55+--> statement-breakpoint
66+CREATE UNIQUE INDEX `labeler_cursors_labeler_id_unique` ON `labeler_cursors` (`labeler_id`);--> statement-breakpoint
17CREATE TABLE `labels_applied` (
28 `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
39 `did` text NOT NULL,
410 `label` text NOT NULL,
511 `action` text NOT NULL,
1212+ `negated` integer DEFAULT false NOT NULL,
613 `date_applied` integer NOT NULL,
714 FOREIGN KEY (`did`) REFERENCES `watched_repos`(`did`) ON UPDATE no action ON DELETE no action
815);
-6
drizzle/0001_faithful_shard.sql
···11-CREATE TABLE `labeler_cursors` (
22- `labeler_id` text,
33- `cursor` text NOT NULL
44-);
55---> statement-breakpoint
66-CREATE UNIQUE INDEX `labeler_cursors_labeler_id_unique` ON `labeler_cursors` (`labeler_id`);
-5
drizzle/0002_greedy_dormammu.sql
···11-DROP INDEX "labeler_cursors_labeler_id_unique";--> statement-breakpoint
22-DROP INDEX "watched_repos_did_unique";--> statement-breakpoint
33-ALTER TABLE `labeler_cursors` ALTER COLUMN "cursor" TO "cursor" integer NOT NULL;--> statement-breakpoint
44-CREATE UNIQUE INDEX `labeler_cursors_labeler_id_unique` ON `labeler_cursors` (`labeler_id`);--> statement-breakpoint
55-CREATE UNIQUE INDEX `watched_repos_did_unique` ON `watched_repos` (`did`);
-1
drizzle/0003_kind_the_liberteens.sql
···11-ALTER TABLE `labels_applied` ADD `negated` integer DEFAULT 0 NOT NULL;
···44import { parse } from "smol-toml";
55import PQueue from "p-queue";
66import { labelerSubscriber } from "./handlers/lablerSubscriber.js";
77-import type { Settings } from "./types/settings.js";
77+import type { PDSConfig, Settings } from "./types/settings.js";
88import { logger } from "./logger.js";
99import { labelerCursor } from "./db/schema.js";
1010+import { backFillPds } from "./pds.js";
10111111-const queue = new PQueue({ concurrency: 2 });
1212+const labelQueue = new PQueue({ concurrency: 2 });
1313+const identityQueue = new PQueue({ concurrency: 2 });
12141315// TODO
1416// 1. Figure out a schema for settings we want. PDSs to watch.Labelers and their Labels
···2527//TODO I really really don't like this unknown to settings. Figure that out later. Cause. It does work >.>
2628const settings = parse(settingsFile) as unknown as Settings;
27293030+let pdsConfigs = Object.entries(settings.pds).map(([_, config]) => config);
3131+3232+for (const config of pdsConfigs) {
3333+ if (config.backfillAccounts) {
3434+ await backFillPds(config, identityQueue);
3535+ }
3636+}
3737+2838// Gets the last saved cursors for Labelers from db for resume
2939const lastCursors = await db.select().from(labelerCursor);
3040···3545 (cursor) => cursor.labelerId === config.host,
3646 );
3747 let lastCursor = lastCursorRow?.cursor ?? undefined;
3838- return labelerSubscriber(config, lastCursor, db, queue);
4848+ return labelerSubscriber(config, lastCursor, db, labelQueue);
3949});
40504151// Graceful shutdown
···4656 subscribers.forEach((close) => close());
47574858 logger.info("Draining the queue...");
4949- await queue.onIdle();
5959+ await labelQueue.onIdle();
50605161 logger.info("Clean shutdown complete.");
5262 process.exit(0);
+28
src/pds.ts
···11+import type PQueue from "p-queue";
22+import type { PDSConfig } from "./types/settings.js";
33+import type {} from "@atcute/atproto";
44+import { Client, simpleFetchHandler, ok } from "@atcute/client";
55+66+export const backFillPds = async (config: PDSConfig, queue: PQueue) => {
77+ const rpc = new Client({
88+ handler: simpleFetchHandler({ service: `https://${config.host}` }),
99+ });
1010+1111+ let cursor = undefined;
1212+1313+ do {
1414+ let result = await ok(
1515+ rpc.get("com.atproto.sync.listRepos", {
1616+ params: {
1717+ limit: 1000,
1818+ cursor,
1919+ },
2020+ }),
2121+ );
2222+2323+ cursor = result.cursor;
2424+ for (let repo of result.repos) {
2525+ console.log(repo.did);
2626+ }
2727+ } while (cursor);
2828+};
+5-1
src/types/settings.ts
···44 host: string;
55 emails: string[];
66 pdsAdminPassword: string;
77+ backfillAccounts: boolean;
88+ listenForNewAccounts: boolean;
79}
810911export type LabelAction = "notify" | "takedown";
···15171618export interface LabelerConfig {
1719 host: string;
2020+ // If set to true and no previous cursor is found it starts from 0
2121+ backfillLabels: boolean;
1822 labels: Record<string, LabelConfig>;
1923}
2024···2529 };
2630 };
2731 labeler: Record<string, LabelerConfig>;
2828- pds: Record<LabelAction, PDSConfig>;
3232+ pds: Record<string, PDSConfig>;
2933}
+1-1
tsconfig.json
···99 // See also https://aka.ms/tsconfig/module
1010 "module": "nodenext",
1111 "target": "esnext",
1212- "types": ["node"],
1212+ "types": ["node", "@atcute/atproto"],
1313 // For nodejs:
1414 // "lib": ["esnext"],
1515 // "types": ["node"],