A tool for parsing traffic on the jetstream and applying a moderation workstream based on regexp based rules
1import { STARTER_PACK_THRESHOLD_CONFIGS } from "../rules/starterPackThreshold.js";
2import {
3 createAccountComment,
4 createAccountLabel,
5 createAccountReport,
6} from "./accountModeration.js";
7import { logger } from "./logger.js";
8import {
9 starterPackLabelsThresholdAppliedCounter,
10 starterPackThresholdChecksCounter,
11 starterPackThresholdMetCounter,
12} from "./metrics.js";
13import {
14 getStarterPackCountInWindow,
15 trackStarterPackForAccount,
16} from "./redis.js";
17import type { StarterPackThresholdConfig } from "./types.js";
18
19function validateAndLoadConfigs(): StarterPackThresholdConfig[] {
20 if (STARTER_PACK_THRESHOLD_CONFIGS.length === 0) {
21 logger.warn(
22 { process: "STARTER_PACK_THRESHOLD" },
23 "No starter pack threshold configs found",
24 );
25 return [];
26 }
27
28 for (const config of STARTER_PACK_THRESHOLD_CONFIGS) {
29 if (config.threshold <= 0) {
30 throw new Error(
31 `Invalid starter pack threshold config: threshold must be positive`,
32 );
33 }
34 if (config.window <= 0) {
35 throw new Error(
36 `Invalid starter pack threshold config: window must be positive`,
37 );
38 }
39 }
40
41 logger.info(
42 { process: "STARTER_PACK_THRESHOLD", count: STARTER_PACK_THRESHOLD_CONFIGS.length },
43 "Loaded starter pack threshold configs",
44 );
45
46 return STARTER_PACK_THRESHOLD_CONFIGS;
47}
48
49const cachedConfigs = validateAndLoadConfigs();
50
51export function loadStarterPackThresholdConfigs(): StarterPackThresholdConfig[] {
52 return cachedConfigs;
53}
54
55export async function checkStarterPackThreshold(
56 did: string,
57 starterPackUri: string,
58 timestamp: number,
59): Promise<void> {
60 try {
61 const configs = loadStarterPackThresholdConfigs();
62
63 if (configs.length === 0) {
64 return;
65 }
66
67 starterPackThresholdChecksCounter.inc();
68
69 for (const config of configs) {
70 // Check allowlist
71 if (config.allowlist?.includes(did)) {
72 logger.debug(
73 { process: "STARTER_PACK_THRESHOLD", did, starterPackUri },
74 "Account is in allowlist, skipping threshold check",
75 );
76 continue;
77 }
78
79 await trackStarterPackForAccount(
80 did,
81 starterPackUri,
82 timestamp,
83 config.window,
84 config.windowUnit,
85 );
86
87 const count = await getStarterPackCountInWindow(
88 did,
89 config.window,
90 config.windowUnit,
91 timestamp,
92 );
93
94 logger.debug(
95 {
96 process: "STARTER_PACK_THRESHOLD",
97 did,
98 count,
99 threshold: config.threshold,
100 window: config.window,
101 windowUnit: config.windowUnit,
102 },
103 "Checked starter pack threshold",
104 );
105
106 if (count >= config.threshold) {
107 starterPackThresholdMetCounter.inc({ account_label: config.accountLabel });
108
109 logger.info(
110 {
111 process: "STARTER_PACK_THRESHOLD",
112 did,
113 starterPackUri,
114 accountLabel: config.accountLabel,
115 count,
116 threshold: config.threshold,
117 },
118 "Starter pack threshold met",
119 );
120
121 const shouldLabel = config.toLabel !== false;
122
123 const formattedComment = `${config.accountComment}\n\nThreshold: ${count.toString()}/${config.threshold.toString()} in ${config.window.toString()} ${config.windowUnit}\n\nStarter Pack: ${starterPackUri}`;
124
125 if (shouldLabel) {
126 await createAccountLabel(did, config.accountLabel, formattedComment);
127 starterPackLabelsThresholdAppliedCounter.inc({
128 account_label: config.accountLabel,
129 action: "label",
130 });
131 }
132
133 if (config.reportAcct) {
134 await createAccountReport(did, formattedComment);
135 starterPackLabelsThresholdAppliedCounter.inc({
136 account_label: config.accountLabel,
137 action: "report",
138 });
139 }
140
141 if (config.commentAcct) {
142 const atURI = `starterpack-threshold-comment:${config.accountLabel}:${timestamp.toString()}`;
143 await createAccountComment(did, formattedComment, atURI);
144 starterPackLabelsThresholdAppliedCounter.inc({
145 account_label: config.accountLabel,
146 action: "comment",
147 });
148 }
149 }
150 }
151 } catch (error) {
152 logger.error(
153 { process: "STARTER_PACK_THRESHOLD", did, starterPackUri, error },
154 "Error checking starter pack threshold",
155 );
156 throw error;
157 }
158}