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

Configure Feed

Select the types of activity you want to include in your feed.

1. src/rules/account/age.ts:8-22 - renamed ReplyContext → InteractionContext, added quotedDid and quotedPostURI fields for quote posts, actorDid as the common actor field 2. src/rules/account/age.ts:123-149 - updated matching logic to check both replies (replyToDid/replyToPostURI) AND quotes (quotedDid/quotedPostURI) 3. src/rules/account/ageConstants.ts:3-12 - updated documentation to reflect reply/quote monitoring 4. added 4 new tests covering: - labeling when quoting monitored DID - labeling when quoting monitored post URI - not labeling when quoting different DID - matching either reply OR quote to monitored target

+213 -37
+55 -26
src/rules/account/age.ts
··· 5 import { PLC_URL } from "../../config.js"; 6 import { GLOBAL_ALLOW } from "../../constants.js"; 7 8 - interface ReplyContext { 9 - replyToDid: string; 10 - replyingDid: string; 11 - atURI: string; 12 - time: number; 13 replyToPostURI?: string; // The URI of the post being replied to (optional) 14 } 15 16 /** ··· 87 }; 88 89 /** 90 - * Checks if a reply meets age criteria and applies labels accordingly 91 */ 92 - export const checkAccountAge = async (context: ReplyContext): Promise<void> => { 93 // Skip if no checks configured 94 if (ACCOUNT_AGE_CHECKS.length === 0) { 95 return; 96 } 97 98 // Skip if DID is globally allowlisted 99 - if (GLOBAL_ALLOW.includes(context.replyingDid)) { 100 logger.debug( 101 { 102 process: "ACCOUNT_AGE", 103 - did: context.replyingDid, 104 atURI: context.atURI, 105 }, 106 "Global allowlisted DID", ··· 110 111 // Check each configuration 112 for (const check of ACCOUNT_AGE_CHECKS) { 113 - // Check if this reply matches monitored DIDs or post URIs 114 - const matchesDID = 115 - check.monitoredDIDs && check.monitoredDIDs.includes(context.replyToDid); 116 - const matchesPostURI = 117 check.monitoredPostURIs && 118 context.replyToPostURI && 119 check.monitoredPostURIs.includes(context.replyToPostURI); 120 121 - if (!matchesDID && !matchesPostURI) { 122 continue; 123 } 124 ··· 138 logger.debug( 139 { 140 process: "ACCOUNT_AGE", 141 - replyingDid: context.replyingDid, 142 replyToDid: context.replyToDid, 143 }, 144 - "Checking account age for reply to monitored DID", 145 ); 146 147 // Get account creation date 148 - const creationDate = await getAccountCreationDate(context.replyingDid); 149 if (!creationDate) { 150 logger.warn( 151 { 152 process: "ACCOUNT_AGE", 153 - replyingDid: context.replyingDid, 154 }, 155 "Could not determine creation date, skipping", 156 ); ··· 166 logger.debug( 167 { 168 process: "ACCOUNT_AGE", 169 - replyingDid: context.replyingDid, 170 creationDate: creationDate.toISOString(), 171 windowStart: windowStart.toISOString(), 172 windowEnd: windowEnd.toISOString(), ··· 178 if (creationDate >= windowStart && creationDate <= windowEnd) { 179 // Check if the label already exists to prevent duplicates 180 const labelExists = await checkAccountLabels( 181 - context.replyingDid, 182 check.label, 183 ); 184 ··· 186 logger.debug( 187 { 188 process: "ACCOUNT_AGE", 189 - replyingDid: context.replyingDid, 190 label: check.label, 191 }, 192 "Label already exists, skipping duplicate", 193 ); 194 - // Only apply one label per reply 195 return; 196 } 197 198 logger.info( 199 { 200 process: "ACCOUNT_AGE", 201 - replyingDid: context.replyingDid, 202 replyToDid: context.replyToDid, 203 atURI: context.atURI, 204 }, 205 "Labeling account created within the monitored date range", 206 ); 207 208 await createAccountLabel( 209 - context.replyingDid, 210 check.label, 211 - `${context.time}: ${check.comment} - Account created within monitored range - Reply: ${context.atURI}`, 212 ); 213 214 - // Only apply one label per reply 215 return; 216 } 217 }
··· 5 import { PLC_URL } from "../../config.js"; 6 import { GLOBAL_ALLOW } from "../../constants.js"; 7 8 + interface InteractionContext { 9 + // For replies 10 + replyToDid?: string; 11 + replyingDid?: string; 12 replyToPostURI?: string; // The URI of the post being replied to (optional) 13 + 14 + // For quote posts 15 + quotedDid?: string; // DID of the account whose post is being quoted 16 + quotedPostURI?: string; // URI of the post being quoted 17 + 18 + // Common fields (required) 19 + actorDid: string; // The DID performing the action (replying or quoting) 20 + atURI: string; // URI of the reply or quote post 21 + time: number; 22 } 23 24 /** ··· 95 }; 96 97 /** 98 + * Checks if a reply or quote post meets age criteria and applies labels accordingly 99 */ 100 + export const checkAccountAge = async ( 101 + context: InteractionContext, 102 + ): Promise<void> => { 103 // Skip if no checks configured 104 if (ACCOUNT_AGE_CHECKS.length === 0) { 105 return; 106 } 107 108 // Skip if DID is globally allowlisted 109 + if (GLOBAL_ALLOW.includes(context.actorDid)) { 110 logger.debug( 111 { 112 process: "ACCOUNT_AGE", 113 + did: context.actorDid, 114 atURI: context.atURI, 115 }, 116 "Global allowlisted DID", ··· 120 121 // Check each configuration 122 for (const check of ACCOUNT_AGE_CHECKS) { 123 + // Check if this interaction matches monitored DIDs or post URIs 124 + // For replies: check replyToDid and replyToPostURI 125 + // For quotes: check quotedDid and quotedPostURI 126 + const matchesReplyDID = 127 + check.monitoredDIDs && 128 + context.replyToDid && 129 + check.monitoredDIDs.includes(context.replyToDid); 130 + const matchesReplyPostURI = 131 check.monitoredPostURIs && 132 context.replyToPostURI && 133 check.monitoredPostURIs.includes(context.replyToPostURI); 134 + const matchesQuoteDID = 135 + check.monitoredDIDs && 136 + context.quotedDid && 137 + check.monitoredDIDs.includes(context.quotedDid); 138 + const matchesQuotePostURI = 139 + check.monitoredPostURIs && 140 + context.quotedPostURI && 141 + check.monitoredPostURIs.includes(context.quotedPostURI); 142 143 + if ( 144 + !matchesReplyDID && 145 + !matchesReplyPostURI && 146 + !matchesQuoteDID && 147 + !matchesQuotePostURI 148 + ) { 149 continue; 150 } 151 ··· 165 logger.debug( 166 { 167 process: "ACCOUNT_AGE", 168 + actorDid: context.actorDid, 169 replyToDid: context.replyToDid, 170 + quotedDid: context.quotedDid, 171 }, 172 + "Checking account age for interaction with monitored target", 173 ); 174 175 // Get account creation date 176 + const creationDate = await getAccountCreationDate(context.actorDid); 177 if (!creationDate) { 178 logger.warn( 179 { 180 process: "ACCOUNT_AGE", 181 + actorDid: context.actorDid, 182 }, 183 "Could not determine creation date, skipping", 184 ); ··· 194 logger.debug( 195 { 196 process: "ACCOUNT_AGE", 197 + actorDid: context.actorDid, 198 creationDate: creationDate.toISOString(), 199 windowStart: windowStart.toISOString(), 200 windowEnd: windowEnd.toISOString(), ··· 206 if (creationDate >= windowStart && creationDate <= windowEnd) { 207 // Check if the label already exists to prevent duplicates 208 const labelExists = await checkAccountLabels( 209 + context.actorDid, 210 check.label, 211 ); 212 ··· 214 logger.debug( 215 { 216 process: "ACCOUNT_AGE", 217 + actorDid: context.actorDid, 218 label: check.label, 219 }, 220 "Label already exists, skipping duplicate", 221 ); 222 + // Only apply one label per interaction 223 return; 224 } 225 226 logger.info( 227 { 228 process: "ACCOUNT_AGE", 229 + actorDid: context.actorDid, 230 replyToDid: context.replyToDid, 231 + quotedDid: context.quotedDid, 232 atURI: context.atURI, 233 }, 234 "Labeling account created within the monitored date range", 235 ); 236 237 await createAccountLabel( 238 + context.actorDid, 239 check.label, 240 + `${context.time}: ${check.comment} - Account created within monitored range - Interaction: ${context.atURI}`, 241 ); 242 243 + // Only apply one label per interaction 244 return; 245 } 246 }
+5 -4
src/rules/account/ageConstants.ts
··· 3 /** 4 * Account age monitoring configurations 5 * 6 - * Each configuration monitors replies to specified DIDs and labels accounts 7 - * that are newer than the threshold relative to the anchor date. 8 * 9 - * Example use case: 10 - * - Monitor replies to high-profile accounts during harassment campaigns 11 * - Flag sock puppet accounts created to participate in coordinated harassment 12 */ 13 export const ACCOUNT_AGE_CHECKS: AccountAgeCheck[] = [ 14 // Example: Monitor replies to specific accounts
··· 3 /** 4 * Account age monitoring configurations 5 * 6 + * Each configuration monitors replies and/or quote posts to specified DIDs or posts 7 + * and labels accounts that were created within a specific time window. 8 * 9 + * Example use cases: 10 + * - Monitor replies/quotes to high-profile accounts during harassment campaigns 11 * - Flag sock puppet accounts created to participate in coordinated harassment 12 + * - Detect brigading on specific controversial posts 13 */ 14 export const ACCOUNT_AGE_CHECKS: AccountAgeCheck[] = [ 15 // Example: Monitor replies to specific accounts
+153 -7
src/rules/account/tests/age.test.ts
··· 165 166 it("should skip if no checks configured", async () => { 167 await checkAccountAge({ 168 replyToDid: "did:plc:monitored", 169 - replyingDid: "did:plc:replier", 170 atURI: TEST_REPLY_URI, 171 time: TEST_TIME, 172 }); ··· 184 }); 185 186 await checkAccountAge({ 187 replyToDid: "did:plc:other", 188 replyingDid: "did:plc:replier", 189 atURI: TEST_REPLY_URI, ··· 212 }); 213 214 await checkAccountAge({ 215 - replyToDid: "did:plc:monitored", 216 replyingDid: "did:plc:inwindow", 217 atURI: TEST_REPLY_URI, 218 time: TEST_TIME, ··· 233 }); 234 235 await checkAccountAge({ 236 - replyToDid: "did:plc:monitored", 237 replyingDid: "did:plc:beforewindow", 238 atURI: TEST_REPLY_URI, 239 time: TEST_TIME, ··· 250 }); 251 252 await checkAccountAge({ 253 - replyToDid: "did:plc:monitored", 254 replyingDid: "did:plc:afterwindow", 255 atURI: TEST_REPLY_URI, 256 time: TEST_TIME, ··· 267 }); 268 269 await checkAccountAge({ 270 - replyToDid: "did:plc:monitored", 271 replyingDid: "did:plc:startofwindow", 272 atURI: TEST_REPLY_URI, 273 time: TEST_TIME, ··· 284 }); 285 286 await checkAccountAge({ 287 - replyToDid: "did:plc:monitored", 288 replyingDid: "did:plc:endofwindow", 289 atURI: TEST_REPLY_URI, 290 time: TEST_TIME, ··· 312 }); 313 314 await checkAccountAge({ 315 replyToDid: "did:plc:monitored", 316 replyingDid: "did:plc:unknown", 317 atURI: TEST_REPLY_URI, ··· 354 }); 355 356 await checkAccountAge({ 357 replyToDid: "did:plc:monitored", 358 replyingDid: "did:plc:newaccount", 359 atURI: TEST_REPLY_URI, ··· 389 (checkAccountLabels as any).mockResolvedValueOnce(true); 390 391 await checkAccountAge({ 392 replyToDid: "did:plc:monitored", 393 replyingDid: "did:plc:alreadylabeled", 394 atURI: TEST_REPLY_URI, ··· 403 expect(logger.debug).toHaveBeenCalledWith( 404 { 405 process: "ACCOUNT_AGE", 406 - replyingDid: "did:plc:alreadylabeled", 407 label: "window-reply", 408 }, 409 "Label already exists, skipping duplicate", ··· 430 (checkAccountLabels as any).mockResolvedValueOnce(false); 431 432 await checkAccountAge({ 433 replyToDid: "did:plc:monitored", 434 replyingDid: "did:plc:newlabel", 435 atURI: TEST_REPLY_URI, ··· 460 GLOBAL_ALLOW.push("did:plc:allowlisted"); 461 462 await checkAccountAge({ 463 replyToDid: "did:plc:monitored", 464 replyingDid: "did:plc:allowlisted", 465 atURI: TEST_REPLY_URI, ··· 491 }); 492 493 await checkAccountAge({ 494 replyToDid: "did:plc:monitored", 495 replyingDid: "did:plc:newaccount", 496 atURI: TEST_REPLY_URI, ··· 525 (checkAccountLabels as any).mockResolvedValueOnce(false); 526 527 await checkAccountAge({ 528 replyToDid: "did:plc:monitored", 529 replyingDid: "did:plc:newaccount", 530 atURI: TEST_REPLY_URI, ··· 559 (checkAccountLabels as any).mockResolvedValueOnce(false); 560 561 await checkAccountAge({ 562 replyToDid: "did:plc:monitored", 563 replyingDid: "did:plc:newaccount", 564 atURI: TEST_REPLY_URI, ··· 594 (checkAccountLabels as any).mockResolvedValueOnce(false); 595 596 await checkAccountAge({ 597 replyToDid: "did:plc:someone", 598 replyingDid: "did:plc:newaccount", 599 atURI: TEST_REPLY_URI, ··· 627 }); 628 629 await checkAccountAge({ 630 replyToDid: "did:plc:someone", 631 replyingDid: "did:plc:newaccount", 632 atURI: TEST_REPLY_URI, ··· 661 662 // Test matching by post URI even though DID doesn't match 663 await checkAccountAge({ 664 replyToDid: "did:plc:someone-else", 665 replyingDid: "did:plc:newaccount", 666 atURI: TEST_REPLY_URI, ··· 696 (checkAccountLabels as any).mockResolvedValueOnce(false); 697 698 await checkAccountAge({ 699 replyToDid: "did:plc:monitored", 700 replyingDid: "did:plc:newaccount", 701 atURI: TEST_REPLY_URI, ··· 706 expect(createAccountLabel).toHaveBeenCalledWith( 707 "did:plc:newaccount", 708 "window-reply", 709 expect.stringContaining("Account created within monitored range"), 710 ); 711 });
··· 165 166 it("should skip if no checks configured", async () => { 167 await checkAccountAge({ 168 + actorDid: "did:plc:replier", 169 replyToDid: "did:plc:monitored", 170 atURI: TEST_REPLY_URI, 171 time: TEST_TIME, 172 }); ··· 184 }); 185 186 await checkAccountAge({ 187 + actorDid: "did:plc:replier", 188 replyToDid: "did:plc:other", 189 replyingDid: "did:plc:replier", 190 atURI: TEST_REPLY_URI, ··· 213 }); 214 215 await checkAccountAge({ 216 + actorDid: "did:plc:inwindow", 217 + replyToDid: "did:plc:monitored", 218 replyingDid: "did:plc:inwindow", 219 atURI: TEST_REPLY_URI, 220 time: TEST_TIME, ··· 235 }); 236 237 await checkAccountAge({ 238 + actorDid: "did:plc:beforewindow", 239 + replyToDid: "did:plc:monitored", 240 replyingDid: "did:plc:beforewindow", 241 atURI: TEST_REPLY_URI, 242 time: TEST_TIME, ··· 253 }); 254 255 await checkAccountAge({ 256 + actorDid: "did:plc:afterwindow", 257 + replyToDid: "did:plc:monitored", 258 replyingDid: "did:plc:afterwindow", 259 atURI: TEST_REPLY_URI, 260 time: TEST_TIME, ··· 271 }); 272 273 await checkAccountAge({ 274 + actorDid: "did:plc:startofwindow", 275 + replyToDid: "did:plc:monitored", 276 replyingDid: "did:plc:startofwindow", 277 atURI: TEST_REPLY_URI, 278 time: TEST_TIME, ··· 289 }); 290 291 await checkAccountAge({ 292 + actorDid: "did:plc:endofwindow", 293 + replyToDid: "did:plc:monitored", 294 replyingDid: "did:plc:endofwindow", 295 atURI: TEST_REPLY_URI, 296 time: TEST_TIME, ··· 318 }); 319 320 await checkAccountAge({ 321 + actorDid: "did:plc:unknown", 322 replyToDid: "did:plc:monitored", 323 replyingDid: "did:plc:unknown", 324 atURI: TEST_REPLY_URI, ··· 361 }); 362 363 await checkAccountAge({ 364 + actorDid: "did:plc:newaccount", 365 replyToDid: "did:plc:monitored", 366 replyingDid: "did:plc:newaccount", 367 atURI: TEST_REPLY_URI, ··· 397 (checkAccountLabels as any).mockResolvedValueOnce(true); 398 399 await checkAccountAge({ 400 + actorDid: "did:plc:alreadylabeled", 401 replyToDid: "did:plc:monitored", 402 replyingDid: "did:plc:alreadylabeled", 403 atURI: TEST_REPLY_URI, ··· 412 expect(logger.debug).toHaveBeenCalledWith( 413 { 414 process: "ACCOUNT_AGE", 415 + actorDid: "did:plc:alreadylabeled", 416 label: "window-reply", 417 }, 418 "Label already exists, skipping duplicate", ··· 439 (checkAccountLabels as any).mockResolvedValueOnce(false); 440 441 await checkAccountAge({ 442 + actorDid: "did:plc:newlabel", 443 replyToDid: "did:plc:monitored", 444 replyingDid: "did:plc:newlabel", 445 atURI: TEST_REPLY_URI, ··· 470 GLOBAL_ALLOW.push("did:plc:allowlisted"); 471 472 await checkAccountAge({ 473 + actorDid: "did:plc:allowlisted", 474 replyToDid: "did:plc:monitored", 475 replyingDid: "did:plc:allowlisted", 476 atURI: TEST_REPLY_URI, ··· 502 }); 503 504 await checkAccountAge({ 505 + actorDid: "did:plc:newaccount", 506 replyToDid: "did:plc:monitored", 507 replyingDid: "did:plc:newaccount", 508 atURI: TEST_REPLY_URI, ··· 537 (checkAccountLabels as any).mockResolvedValueOnce(false); 538 539 await checkAccountAge({ 540 + actorDid: "did:plc:newaccount", 541 replyToDid: "did:plc:monitored", 542 replyingDid: "did:plc:newaccount", 543 atURI: TEST_REPLY_URI, ··· 572 (checkAccountLabels as any).mockResolvedValueOnce(false); 573 574 await checkAccountAge({ 575 + actorDid: "did:plc:newaccount", 576 replyToDid: "did:plc:monitored", 577 replyingDid: "did:plc:newaccount", 578 atURI: TEST_REPLY_URI, ··· 608 (checkAccountLabels as any).mockResolvedValueOnce(false); 609 610 await checkAccountAge({ 611 + actorDid: "did:plc:newaccount", 612 replyToDid: "did:plc:someone", 613 replyingDid: "did:plc:newaccount", 614 atURI: TEST_REPLY_URI, ··· 642 }); 643 644 await checkAccountAge({ 645 + actorDid: "did:plc:newaccount", 646 replyToDid: "did:plc:someone", 647 replyingDid: "did:plc:newaccount", 648 atURI: TEST_REPLY_URI, ··· 677 678 // Test matching by post URI even though DID doesn't match 679 await checkAccountAge({ 680 + actorDid: "did:plc:newaccount", 681 replyToDid: "did:plc:someone-else", 682 replyingDid: "did:plc:newaccount", 683 atURI: TEST_REPLY_URI, ··· 713 (checkAccountLabels as any).mockResolvedValueOnce(false); 714 715 await checkAccountAge({ 716 + actorDid: "did:plc:newaccount", 717 replyToDid: "did:plc:monitored", 718 replyingDid: "did:plc:newaccount", 719 atURI: TEST_REPLY_URI, ··· 724 expect(createAccountLabel).toHaveBeenCalledWith( 725 "did:plc:newaccount", 726 "window-reply", 727 + expect.stringContaining("Account created within monitored range"), 728 + ); 729 + }); 730 + 731 + it("should label account when quoting a monitored DID", async () => { 732 + ACCOUNT_AGE_CHECKS.push({ 733 + monitoredDIDs: ["did:plc:monitored"], 734 + anchorDate: "2025-10-15", 735 + maxAgeDays: 7, 736 + label: "quote-brigading", 737 + comment: "New account quote-posting monitored user", 738 + }); 739 + 740 + // Mock account created within window 741 + const mockDidDoc = [{ createdAt: "2025-10-18T12:00:00.000Z" }]; 742 + (global.fetch as any).mockResolvedValueOnce({ 743 + ok: true, 744 + json: async () => mockDidDoc, 745 + }); 746 + 747 + // Mock that label does NOT exist 748 + (checkAccountLabels as any).mockResolvedValueOnce(false); 749 + 750 + await checkAccountAge({ 751 + actorDid: "did:plc:quoter", 752 + quotedDid: "did:plc:monitored", 753 + atURI: "at://did:plc:quoter/app.bsky.feed.post/quote123", 754 + time: TEST_TIME, 755 + }); 756 + 757 + expect(createAccountLabel).toHaveBeenCalledWith( 758 + "did:plc:quoter", 759 + "quote-brigading", 760 + expect.stringContaining("Account created within monitored range"), 761 + ); 762 + }); 763 + 764 + it("should label account when quoting a monitored post URI", async () => { 765 + ACCOUNT_AGE_CHECKS.push({ 766 + monitoredPostURIs: [ 767 + "at://did:plc:target/app.bsky.feed.post/targeted", 768 + ], 769 + anchorDate: "2025-10-15", 770 + maxAgeDays: 7, 771 + label: "brigading-suspect", 772 + comment: "New account quoting targeted post", 773 + }); 774 + 775 + // Mock account created within window 776 + const mockDidDoc = [{ createdAt: "2025-10-18T12:00:00.000Z" }]; 777 + (global.fetch as any).mockResolvedValueOnce({ 778 + ok: true, 779 + json: async () => mockDidDoc, 780 + }); 781 + 782 + // Mock that label does NOT exist 783 + (checkAccountLabels as any).mockResolvedValueOnce(false); 784 + 785 + await checkAccountAge({ 786 + actorDid: "did:plc:quoter", 787 + quotedPostURI: "at://did:plc:target/app.bsky.feed.post/targeted", 788 + atURI: "at://did:plc:quoter/app.bsky.feed.post/quote456", 789 + time: TEST_TIME, 790 + }); 791 + 792 + expect(createAccountLabel).toHaveBeenCalledWith( 793 + "did:plc:quoter", 794 + "brigading-suspect", 795 + expect.stringContaining("Account created within monitored range"), 796 + ); 797 + }); 798 + 799 + it("should not label when quoting different DID", async () => { 800 + ACCOUNT_AGE_CHECKS.push({ 801 + monitoredDIDs: ["did:plc:monitored"], 802 + anchorDate: "2025-10-15", 803 + maxAgeDays: 7, 804 + label: "quote-brigading", 805 + comment: "New account quote-posting", 806 + }); 807 + 808 + // Mock account created within window 809 + const mockDidDoc = [{ createdAt: "2025-10-18T12:00:00.000Z" }]; 810 + (global.fetch as any).mockResolvedValueOnce({ 811 + ok: true, 812 + json: async () => mockDidDoc, 813 + }); 814 + 815 + await checkAccountAge({ 816 + actorDid: "did:plc:quoter", 817 + quotedDid: "did:plc:different", 818 + atURI: "at://did:plc:quoter/app.bsky.feed.post/quote789", 819 + time: TEST_TIME, 820 + }); 821 + 822 + expect(createAccountLabel).not.toHaveBeenCalled(); 823 + }); 824 + 825 + it("should match either reply or quote to monitored DID", async () => { 826 + ACCOUNT_AGE_CHECKS.push({ 827 + monitoredDIDs: ["did:plc:monitored"], 828 + anchorDate: "2025-10-15", 829 + maxAgeDays: 7, 830 + label: "interaction-suspect", 831 + comment: "New account interacting with monitored user", 832 + }); 833 + 834 + // Mock account created within window 835 + const mockDidDoc = [{ createdAt: "2025-10-18T12:00:00.000Z" }]; 836 + (global.fetch as any).mockResolvedValueOnce({ 837 + ok: true, 838 + json: async () => mockDidDoc, 839 + }); 840 + 841 + // Mock that label does NOT exist 842 + (checkAccountLabels as any).mockResolvedValueOnce(false); 843 + 844 + // Test quote (not reply) to monitored DID 845 + await checkAccountAge({ 846 + actorDid: "did:plc:actor", 847 + quotedDid: "did:plc:monitored", 848 + atURI: "at://did:plc:actor/app.bsky.feed.post/interaction", 849 + time: TEST_TIME, 850 + }); 851 + 852 + expect(createAccountLabel).toHaveBeenCalledWith( 853 + "did:plc:actor", 854 + "interaction-suspect", 855 expect.stringContaining("Account created within monitored range"), 856 ); 857 });