A tool for parsing traffic on the jetstream and applying a moderation workstream based on regexp based rules

Refactor moderation flags for granular control

- Rename `reportOnly` to `reportAcct` and `commentOnly` to `commentAcct`
for clarity.
- Introduce `toLabel` flag to explicitly manage item labeling.
These changes allow independent control over labeling an item,
reporting its author, or commenting on the author's account.

- Remove non-functional pre-check for existing labels before applying new ones in handle and profile checks.

- Add checks for starter pack content (name, description) and known vector authors.
- Implement `createPostReport` function for reporting specific posts.
- Update `checkHandle` signature for direct parameter passing.
- Populate predefined moderation lists.

+339 -162
+35 -35
src/checkHandles.ts
··· 3 3 import { Handle } from "./types.js"; 4 4 import { 5 5 createAccountReport, 6 + createAccountComment, 6 7 createAccountLabel, 7 8 checkAccountLabels, 8 9 } from "./moderation.js"; 9 10 import { limit } from "./limits.js"; 10 11 11 - export const checkHandle = async (handle: Handle[]) => { 12 - const ActLabelChk = await limit(() => checkAccountLabels(handle[0].did)); 12 + export const checkHandle = async ( 13 + did: string, 14 + handle: string, 15 + time: number, 16 + ) => { 17 + const ActLabelChk = await limit(() => checkAccountLabels(did)); 13 18 // Get a list of labels 14 19 const labels: string[] = Array.from( 15 20 HANDLE_CHECKS, ··· 23 28 ); 24 29 25 30 if (checkList?.ignoredDIDs) { 26 - if (checkList.ignoredDIDs.includes(handle[0].did)) { 27 - return logger.info(`Whitelisted DID: ${handle[0].did}`); 31 + if (checkList.ignoredDIDs.includes(did)) { 32 + logger.info(`Whitelisted DID: ${did}`); 33 + return; 28 34 } 29 - } else { 30 - if (checkList!.check.test(handle[0].handle)) { 31 - if (checkList?.whitelist) { 32 - // False-positive checks 33 - if (checkList?.whitelist.test(handle[0].handle)) { 34 - logger.info(`Whitelisted phrase found for: ${handle[0].handle}`); 35 - return; 36 - } 37 - } else { 38 - logger.info(`${checkList!.label} in handle: ${handle[0].handle}`); 35 + } 36 + 37 + if (checkList!.check.test(handle)) { 38 + // False-positive checks 39 + if (checkList?.whitelist) { 40 + if (checkList?.whitelist.test(handle)) { 41 + logger.info(`Whitelisted phrase found for: ${handle}`); 42 + return; 39 43 } 44 + } 40 45 41 - if (checkList?.reportOnly === true) { 42 - logger.info(`Report only: ${handle[0].handle}`); 43 - createAccountReport( 44 - handle[0].did, 45 - `${handle[0].time}: ${checkList!.comment} - ${handle[0].handle}`, 46 + if (checkList?.toLabel === true) { 47 + { 48 + createAccountLabel( 49 + did, 50 + `${checkList!.label}`, 51 + `${time}: ${checkList!.comment} - ${handle}`, 46 52 ); 47 - return; 48 - } else { 49 - if (ActLabelChk) { 50 - if (ActLabelChk.includes(checkList!.label)) { 51 - logger.info( 52 - `Label ${checkList!.label} already exists for ${did}`, 53 - ); 54 - return; 55 - } else { 56 - createAccountLabel( 57 - handle[0].did, 58 - `${checkList!.label}`, 59 - `${handle[0].time}: ${checkList!.comment} - ${handle[0].handle}`, 60 - ); 61 - } 62 - } 63 53 } 54 + } 55 + 56 + if (checkList?.reportAcct === true) { 57 + logger.info(`Report only: ${handle}`); 58 + createAccountReport(did, `${time}: ${checkList!.comment} - ${handle}`); 59 + } 60 + 61 + if (checkList?.commentAcct === true) { 62 + logger.info(`Comment only: ${handle}`); 63 + createAccountComment(did, `${time}: ${checkList!.comment} - ${handle}`); 64 64 } 65 65 } 66 66 });
+36 -49
src/checkPosts.ts
··· 14 14 (postCheck) => postCheck.label, 15 15 ); 16 16 17 - // Destructure Post object 18 - const { did, time, atURI, text, cid } = post[0]; 19 - 20 17 // iterate through the labels 21 18 labels.forEach((label) => { 22 19 const checkPost = POST_CHECKS.find( ··· 24 21 ); 25 22 26 23 if (checkPost?.ignoredDIDs) { 27 - if (checkPost.ignoredDIDs.includes(did)) { 28 - return logger.info(`Whitelisted DID: ${did}`); 24 + if (checkPost?.ignoredDIDs.includes(post[0].did)) { 25 + logger.info(`Whitelisted DID: ${post[0].did}`); 26 + return; 29 27 } 30 - } else { 31 - if (checkPost!.check.test(text)) { 32 - if (checkPost?.whitelist) { 33 - if (checkPost?.whitelist.test(text)) { 34 - logger.info(`Whitelisted phrase found"`); 35 - return; 36 - } 37 - } else { 38 - logger.info(`${checkPost!.label} in post at ${atURI}`); 28 + } 29 + 30 + if (checkPost!.check.test(post[0].text)) { 31 + // Check if post is whitelisted 32 + if (checkPost?.whitelist) { 33 + if (checkPost?.whitelist.test(post[0].text)) { 34 + logger.info(`Whitelisted phrase found"`); 35 + return; 36 + } 37 + } 39 38 40 - if (checkPost!.reportOnly === true) { 41 - logger.info(`Report only: ${did}`); 42 - createAccountReport( 43 - did, 44 - `${time}: ${checkPost?.comment} at ${atURI} with text "${text}"`, 45 - ); 46 - return; 47 - } else { 48 - logger.info(`Labeling post: ${atURI}`); 39 + if (checkPost!.toLabel === true) { 40 + logger.info(`Labeling post: ${post[0].atURI} for ${checkPost!.label}`); 41 + createPostLabel( 42 + post[0].atURI, 43 + post[0].cid, 44 + `${checkPost!.label}`, 45 + `${post[0].time}: ${checkPost!.comment} at ${post[0].atURI} with text "${post[0].text}"`, 46 + ); 47 + } 49 48 50 - createPostLabel( 51 - post[0].atURI, 52 - post[0].cid, 53 - `${checkPost!.label}`, 54 - `${post[0].time}: ${checkPost!.comment} at ${post[0].atURI} with text "${post[0].text}"`, 55 - ); 49 + if (checkPost!.reportAcct === true) { 50 + logger.info(`${checkPost!.label} in post at ${post[0].atURI}`); 51 + logger.info(`Report only: ${post[0].did}`); 52 + createAccountReport( 53 + post[0].did, 54 + `${post[0].time}: ${checkPost?.comment} at ${post[0].atURI} with text "${post[0].text}"`, 55 + ); 56 + } 56 57 57 - if (checkPost!.commentOnly === true) { 58 - logger.info(`Comment only: ${post[0].did}`); 59 - createAccountComment( 60 - post[0].did, 61 - `${post[0].time}: ${checkPost?.comment} at ${post[0].atURI} with text "${post[0].text}"`, 62 - ); 63 - return; 64 - } else if (checkPost?.label === "fundraising-link" || checkPost?.label === "twitter-x") { 65 - return; // skip fundraising links—hardcoded because of the insane volume by spammers. 66 - } else if (checkPost!.commentOnly === false) { 67 - logger.info( 68 - `Creating report for post ${post[0].atURI} on ${post[0].did}`, 69 - ); 70 - createAccountReport( 71 - post[0].did, 72 - ` ${post[0].time}: ${checkPost!.comment} at ${post[0].atURI} with text "${post[0].text}"`, 73 - ); 74 - } 75 - } 76 - } 58 + if (checkPost!.commentAcct === true) { 59 + logger.info(`Comment on account: ${post[0].did}`); 60 + createAccountComment( 61 + post[0].did, 62 + `${post[0].time}: ${checkPost?.comment} at ${post[0].atURI} with text "${post[0].text}"`, 63 + ); 77 64 } 78 65 } 79 66 });
+41 -43
src/checkProfiles.ts
··· 5 5 createAccountReport, 6 6 createAccountLabel, 7 7 checkAccountLabels, 8 + createAccountComment, 8 9 } from "./moderation.js"; 9 10 import { limit } from "./limits.js"; 10 11 ··· 14 15 displayName: string, 15 16 description: string, 16 17 ) => { 17 - const ActLabelChk = await limit(() => checkAccountLabels(did)); 18 - // Get a list of labels 19 18 const labels: string[] = Array.from( 20 19 PROFILE_CHECKS, 21 20 (profileCheck) => profileCheck.label, ··· 30 29 // Check if DID is whitelisted 31 30 if (checkProfiles?.ignoredDIDs) { 32 31 if (checkProfiles.ignoredDIDs.includes(did)) { 33 - return logger.info(`Whitelisted DID: ${did}`); 32 + logger.info(`Whitelisted DID: ${did}`); 33 + return; 34 34 } 35 35 } 36 36 37 37 if (description) { 38 38 if (checkProfiles?.description === true) { 39 39 if (checkProfiles!.check.test(description)) { 40 + // Check if description is whitelisted 40 41 if (checkProfiles!.whitelist) { 41 42 if (checkProfiles!.whitelist.test(description)) { 42 43 logger.info(`Whitelisted phrase found.`); 43 44 return; 44 45 } 45 - } else { 46 - logger.info(`${checkProfiles!.label} in description for ${did}`); 46 + } 47 + 48 + if (checkProfiles!.toLabel === true) { 49 + logger.info(`Creating label for ${did}`); 50 + createAccountLabel( 51 + did, 52 + `${checkProfiles!.label}`, 53 + `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 54 + ); 47 55 } 48 56 49 - if (checkProfiles!.reportOnly === true) { 57 + if (checkProfiles!.reportAcct === true) { 50 58 createAccountReport( 51 59 did, 52 60 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 53 61 ); 54 - return; 55 - } else { 56 - if (ActLabelChk) { 57 - if (ActLabelChk.includes(checkProfiles!.label)) { 58 - logger.info( 59 - `Label ${checkProfiles!.label} already exists for ${did}`, 60 - ); 61 - return; 62 - } 63 - } else { 64 - createAccountLabel( 65 - did, 66 - `${checkProfiles!.label}`, 67 - `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 68 - ); 69 - } 62 + } 63 + 64 + if (checkProfiles!.commentAcct === true) { 65 + logger.info(`Commenting on account for ${did}`); 66 + createAccountComment( 67 + did, 68 + `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 69 + ); 70 70 } 71 71 } 72 72 } ··· 80 80 displayName: string, 81 81 description: string, 82 82 ) => { 83 - const ActLabelChk = await limit(() => checkAccountLabels(did)); 84 83 // Get a list of labels 85 84 const labels: string[] = Array.from( 86 85 PROFILE_CHECKS, ··· 96 95 // Check if DID is whitelisted 97 96 if (checkProfiles?.ignoredDIDs) { 98 97 if (checkProfiles.ignoredDIDs.includes(did)) { 99 - return logger.info(`Whitelisted DID: ${did}`); 98 + logger.info(`Whitelisted DID: ${did}`); 99 + return; 100 100 } 101 101 } 102 102 103 103 if (displayName) { 104 104 if (checkProfiles?.displayName === true) { 105 105 if (checkProfiles!.check.test(displayName)) { 106 + // Check if displayName is whitelisted 106 107 if (checkProfiles!.whitelist) { 107 108 if (checkProfiles!.whitelist.test(displayName)) { 108 109 logger.info(`Whitelisted phrase found.`); 109 110 return; 110 111 } 111 - } else { 112 - logger.info(`${checkProfiles!.label} in displayName for ${did}`); 112 + } 113 + 114 + if (checkProfiles!.toLabel === true) { 115 + createAccountLabel( 116 + did, 117 + `${checkProfiles!.label}`, 118 + `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 119 + ); 113 120 } 114 121 115 - if (checkProfiles!.reportOnly === true) { 122 + if (checkProfiles!.reportAcct === true) { 116 123 createAccountReport( 117 124 did, 118 125 `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 119 126 ); 120 - return; 121 - } else { 122 - if (ActLabelChk) { 123 - if (ActLabelChk.includes(checkProfiles!.label)) { 124 - logger.info( 125 - `Label ${checkProfiles!.label} already exists for ${did}`, 126 - ); 127 - return; 128 - } 129 - } else { 130 - createAccountLabel( 131 - did, 132 - `${checkProfiles!.label}`, 133 - `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 134 - ); 135 - } 127 + } 128 + 129 + if (checkProfiles!.commentAcct === true) { 130 + createAccountComment( 131 + did, 132 + `${time}: ${checkProfiles!.comment} - ${displayName} - ${description}`, 133 + ); 136 134 } 137 135 } 138 136 }
+69 -2
src/checkStarterPack.ts
··· 1 - import { PROFILE_CHECKS } from "./constants.js"; 1 + import { PROFILE_CHECKS, STARTERPACK_CHECKS } from "./constants.js"; 2 2 import logger from "./logger.js"; 3 - import { createAccountLabel } from "./moderation.js"; 3 + import { 4 + createAccountLabel, 5 + createAccountReport, 6 + createPostLabel, 7 + } from "./moderation.js"; 4 8 5 9 export const checkStarterPack = async ( 6 10 did: string, ··· 40 44 } 41 45 }); 42 46 }; 47 + 48 + export const checkNewStarterPack = async ( 49 + did: string, 50 + time: number, 51 + atURI: string, 52 + cid: string, 53 + packName: string | undefined, 54 + description: string | undefined, 55 + ) => { 56 + const labels: string[] = Array.from( 57 + STARTERPACK_CHECKS, 58 + (SPCheck) => SPCheck.label, 59 + ); 60 + 61 + labels.forEach((label) => { 62 + const checkList = PROFILE_CHECKS.find((SPCheck) => SPCheck.label === label); 63 + 64 + if (checkList?.knownVectors?.includes(did)) { 65 + createPostLabel( 66 + atURI, 67 + cid, 68 + `${checkList!.label}`, 69 + `${time}: Starter pack created by known vector for ${checkList!.label} at: ${atURI}"`, 70 + ); 71 + createAccountReport( 72 + did, 73 + `${time}: Starter pack created by known vector for ${checkList!.label} at: ${atURI}"`, 74 + ); 75 + } 76 + 77 + if (description) { 78 + if (checkList!.check.test(description)) { 79 + logger.info(`Labeling post: ${atURI}`); 80 + createPostLabel( 81 + atURI, 82 + cid, 83 + `${checkList!.label}`, 84 + `${time}: ${checkList!.comment} at ${atURI} with text "${description}"`, 85 + ); 86 + createAccountReport( 87 + did, 88 + `${time}: ${checkList!.comment} at ${atURI} with text "${description}"`, 89 + ); 90 + } 91 + } 92 + 93 + if (packName) { 94 + if (checkList!.check.test(packName)) { 95 + logger.info(`Labeling post: ${atURI}`); 96 + createPostLabel( 97 + atURI, 98 + cid, 99 + `${checkList!.label}`, 100 + `${time}: ${checkList!.comment} at ${atURI} with pack name "${packName}"`, 101 + ); 102 + createAccountReport( 103 + did, 104 + `${time}: ${checkList!.comment} at ${atURI} with pack name "${packName}"`, 105 + ); 106 + } 107 + } 108 + }); 109 + };
+12 -8
src/constants.ts.example
··· 8 8 comment: "Pro-skub language found in profile", 9 9 description: true, 10 10 displayName: true, 11 - reportOnly: false, 12 - commentOnly: false, 11 + reportAcct: false, 12 + commentAcct: false, 13 + toLabel: true, 13 14 check: new RegExp( 14 15 "(only|pro)[ -]skub|we love skub|skub is (good|god|king)|\\bskub\\b", 15 16 "i", ··· 24 25 comment: "skub-adjacent language found in profile", 25 26 description: true, 26 27 displayName: true, 27 - reportOnly: true, 28 - commentOnly: false, 28 + reportAcct: false, 29 + commentAcct: false, 30 + toLabel: true, 29 31 check: new RegExp( 30 32 "skubbe", 31 33 "i", ··· 37 39 { 38 40 label: "skub", 39 41 comment: "Pro-skub language found in handle", 40 - reportOnly: false, 41 - commentOnly: false, 42 + reportAcct: false, 43 + commentAcct: false, 44 + toLabel: true, 42 45 check: new RegExp( 43 46 "(only|pro)[-]skub|we love skub|skub[-]?is[-]?(good|god|king)|skub\\.(pro|com|org)", 44 47 "i", ··· 50 53 { 51 54 label: "pro-skub-link", 52 55 comment: "Pro Skub link found in post", 53 - reportOnly: true, 54 - commentOnly: false, 56 + reportAcct: false, 57 + commentAcct: true, 58 + toLabel: true, 55 59 check: new RegExp( 56 60 "skubbe\\.com|skub\\.(me|pro|tech)", 57 61 "i",
+4 -3
src/developing_checks.md
··· 12 12 comment: "Example found in handle", 13 13 description: true, // Optional, only used in handle checks 14 14 displayName: true, // Optional, only used in handle checks 15 - reportOnly: false, // it true, the check will only report the content against the account, not label. 16 - commentOnly: false, // Poorly named, if true, will generate an account level comment from flagged posts, rather than a report. Intended for use when reportOnly is false, and on posts only where the flag may generate a high volume of reports.. 15 + reportAcct: false, // it true, the check will only report the content against the account, not label. 16 + commentOnly: false, // if true, will generate an account level comment from flagged posts, rather than a report. Intended for use when reportAcct is false, and on posts only where the flag may generate a high volume of reports. 17 + toLabel: true, // Should the handle in question be labeled if check evaluates to true. 17 18 check: new RegExp("example", "i"), // Regular expression to match against the content 18 19 whitelist: new RegExp("example.com", "i"), // Optional, regular expression to whitelist content 19 - ignoredDIDs: ["did:plc:example"] // Optional, array of DIDs to ignore if they match the check. Useful for folks who reclaim words. 20 + ignoredDIDs: ["did:plc:example"] // Optional, array of DIDs to ignore if they match the check. Useful for folks who reclaim words or accounts which may be false positives. 20 21 } 21 22 ]; 22 23 ```
+38 -2
src/lists.ts
··· 2 2 3 3 export const LISTS: List[] = [ 4 4 { 5 - label: "list-name", 6 - rkey: "<insert-rkey-here>", 5 + label: "blue-heart-emoji", 6 + rkey: "3lfbtgosyyi22", 7 + }, 8 + { 9 + label: "troll", 10 + rkey: "3lbckxhgu3r2v", 11 + }, 12 + { 13 + label: "maga-trump", 14 + rkey: "3l53cjwlt4o2s", 15 + }, 16 + { 17 + label: "elon-musk", 18 + rkey: "3l72tte74wa2m", 19 + }, 20 + { 21 + label: "rmve-imve", 22 + rkey: "3l6tfurf7li27", 23 + }, 24 + { 25 + label: "nazi-symbolism", 26 + rkey: "3l6vdudxgeb2z", 27 + }, 28 + { 29 + label: "hammer-sickle", 30 + rkey: "3l4ue6w2aur2v", 31 + }, 32 + { 33 + label: "inverted-red-triangle", 34 + rkey: "3l4ueabtpec2a", 35 + }, 36 + { 37 + label: "automated-reply-guy", 38 + rkey: "3lch7qbvzpx23", 39 + }, 40 + { 41 + label: "terf-gc", 42 + rkey: "3lcqjqjdejs2x", 7 43 }, 8 44 ];
+60 -18
src/main.ts
··· 18 18 import { Post, LinkFeature, Handle } from "./types.js"; 19 19 import { checkPosts } from "./checkPosts.js"; 20 20 import { checkHandle } from "./checkHandles.js"; 21 - import { checkStarterPack } from "./checkStarterPack.js"; 21 + import { checkStarterPack, checkNewStarterPack } from "./checkStarterPack.js"; 22 22 import { checkDescription, checkDisplayName } from "./checkProfiles.js"; 23 23 24 24 let cursor = 0; ··· 143 143 checkDescription( 144 144 event.did, 145 145 event.time_us, 146 - event.commit.record.displayName, 147 - event.commit.record.description, 146 + event.commit.record.displayName as string, 147 + event.commit.record.description as string, 148 148 ); 149 149 checkDisplayName( 150 150 event.did, 151 151 event.time_us, 152 - event.commit.record.displayName, 153 - event.commit.record.description, 152 + event.commit.record.displayName as string, 153 + event.commit.record.description as string, 154 154 ); 155 155 } 156 156 ··· 168 168 ); 169 169 170 170 // Check for profile updates 171 + 171 172 jetstream.onCreate( 172 173 "app.bsky.actor.profile", 173 174 async (event: CommitCreateEvent<"app.bsky.actor.profile">) => { ··· 176 177 checkDescription( 177 178 event.did, 178 179 event.time_us, 179 - event.commit.record.displayName, 180 - event.commit.record.description, 180 + event.commit.record.displayName as string, 181 + event.commit.record.description as string, 181 182 ); 182 183 checkDisplayName( 183 184 event.did, 184 185 event.time_us, 185 - event.commit.record.displayName, 186 - event.commit.record.description, 186 + event.commit.record.displayName as string, 187 + event.commit.record.description as string, 187 188 ); 188 - event.commit.record.joinedViaStarterPack?.uri; 189 + 190 + if (event.commit.record.joinedViaStarterPack) { 191 + checkStarterPack( 192 + event.did, 193 + event.time_us, 194 + event.commit.record.joinedViaStarterPack.uri, 195 + ); 196 + } 189 197 } else { 190 198 return; 191 199 } ··· 195 203 }, 196 204 ); 197 205 206 + jetstream.onCreate( 207 + "app.bsky.graph.starterpack", 208 + async (event: CommitCreateEvent<"app.bsky.graph.starterpack">) => { 209 + try { 210 + const atURI = `at://${event.did}/app.bsky.feed.post/${event.commit.rkey}`; 211 + 212 + checkNewStarterPack( 213 + event.did, 214 + event.time_us, 215 + atURI, 216 + event.commit.cid, 217 + event.commit.record.name, 218 + event.commit.record.description, 219 + ); 220 + } catch (error) { 221 + logger.error(`Error checking starterpack: ${error}`); 222 + } 223 + }, 224 + ); 225 + 226 + jetstream.onUpdate( 227 + "app.bsky.graph.starterpack", 228 + async (event: CommitUpdateEvent<"app.bsky.graph.starterpack">) => { 229 + try { 230 + const atURI = `at://${event.did}/app.bsky.feed.post/${event.commit.rkey}`; 231 + 232 + checkNewStarterPack( 233 + event.did, 234 + event.time_us, 235 + atURI, 236 + event.commit.cid, 237 + event.commit.record.name, 238 + event.commit.record.description, 239 + ); 240 + } catch (error) { 241 + logger.error(`Error checking starterpack: ${error}`); 242 + } 243 + }, 244 + ); 245 + 198 246 // Check for handle updates 199 247 jetstream.on("identity", async (event: IdentityEvent) => { 200 - const handle: Handle[] = [ 201 - { did: event.did, handle: event.identity.handle, time: event.time_us }, 202 - ]; 203 - 204 - try { 205 - const ret = await checkHandle(handle); 206 - } catch (error) { 207 - logger.error(`Error checking handle: ${error}`); 248 + if (event.identity.handle) { 249 + checkHandle(event.identity.did, event.identity.handle, event.time_us); 208 250 } 209 251 }); 210 252
+40
src/moderation.ts
··· 86 86 }); 87 87 }; 88 88 89 + export const createPostReport = async ( 90 + uri: string, 91 + cid: string, 92 + comment: string, 93 + ) => { 94 + await isLoggedIn; 95 + await limit(async () => { 96 + try { 97 + return agent.tools.ozone.moderation.emitEvent( 98 + { 99 + event: { 100 + $type: "tools.ozone.moderation.defs#modEventReport", 101 + comment: comment, 102 + reportType: "com.atproto.moderation.defs#reasonOther", 103 + }, 104 + // specify the labeled post by strongRef 105 + subject: { 106 + $type: "com.atproto.repo.strongRef", 107 + uri: uri, 108 + cid: cid, 109 + }, 110 + // put in the rest of the metadata 111 + createdBy: `${agent.did}`, 112 + createdAt: new Date().toISOString(), 113 + }, 114 + { 115 + encoding: "application/json", 116 + headers: { 117 + "atproto-proxy": `${MOD_DID!}#atproto_labeler`, 118 + "atproto-accept-labelers": 119 + "did:plc:ar7c4by46qjdydhdevvrndac;redact", 120 + }, 121 + }, 122 + ); 123 + } catch (e) { 124 + console.error(e); 125 + } 126 + }); 127 + }; 128 + 89 129 export const createAccountComment = async (did: string, comment: string) => { 90 130 await isLoggedIn; 91 131 await limit(async () => {
+4 -2
src/types.ts
··· 3 3 comment: string; 4 4 description?: boolean; 5 5 displayName?: boolean; 6 - reportOnly: boolean; 7 - commentOnly: boolean; 6 + reportAcct: boolean; 7 + commentAcct: boolean; 8 + toLabel: boolean; 8 9 check: RegExp; 9 10 whitelist?: RegExp; 10 11 ignoredDIDs?: string[]; 11 12 starterPacks?: string[]; 13 + knownVectors?: string[]; 12 14 } 13 15 14 16 export interface Post {