Skip to content

Commit ec33170

Browse files
authored
chore: refactor spam filter (#672)
* chore: refactor spam filter Signed-off-by: Huamin Chen <[email protected]> * chore: refactor spam filter Signed-off-by: Huamin Chen <[email protected]> --------- Signed-off-by: Huamin Chen <[email protected]>
1 parent 099527f commit ec33170

File tree

2 files changed

+118
-47
lines changed

2 files changed

+118
-47
lines changed

.github/workflows/anti-spam-filter.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Anti-Spam Filter (Hidden Logic)
1+
name: Content Moderation
22

33
on:
44
issues:
@@ -29,23 +29,25 @@ jobs:
2929
script: |
3030
// Load and execute the spam detection script from secret
3131
const detectionScript = process.env.SPAM_DETECTION_SCRIPT;
32-
32+
3333
if (!detectionScript) {
34-
core.error("SPAM_DETECTION_SCRIPT secret not found!");
35-
core.setFailed("Spam filter not configured");
34+
core.warning("SPAM_DETECTION_SCRIPT secret not configured - skipping spam detection");
35+
core.info("To enable spam filtering, set up the SPAM_DETECTION_SCRIPT secret.");
36+
core.info("See documentation for setup instructions.");
3637
return;
3738
}
38-
39+
3940
try {
4041
// Execute the hidden script
4142
// The script has access to: github, context, core
4243
const detectSpam = eval(detectionScript);
43-
44+
4445
// Run the detection
4546
await detectSpam(github, context, core);
46-
47+
4748
} catch (err) {
4849
core.error(`Spam filter error: ${err.message}`);
49-
core.setFailed(`Filter execution failed: ${err.message}`);
50+
core.warning(`Filter execution failed - continuing without spam check`);
51+
// Don't fail the workflow, just log the error
5052
}
5153

.github/workflows/cleanup-existing-spam.yml

Lines changed: 108 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ on:
1616
required: false
1717
default: 'true'
1818
type: boolean
19+
scan_closed_issues:
20+
description: 'Scan closed issues (may find previously closed spam)'
21+
required: false
22+
default: 'false'
23+
type: boolean
1924
scan_comments:
2025
description: 'Scan issue comments'
2126
required: false
@@ -45,41 +50,46 @@ jobs:
4550
script: |
4651
const dryRun = '${{ inputs.dry_run }}' === 'true';
4752
const scanIssues = '${{ inputs.scan_issues }}' === 'true';
53+
const scanClosedIssues = '${{ inputs.scan_closed_issues }}' === 'true';
4854
const scanComments = '${{ inputs.scan_comments }}' === 'true';
4955
const maxIssues = parseInt('${{ inputs.max_issues }}') || 0;
5056
const owner = context.repo.owner;
5157
const repo = context.repo.repo;
52-
58+
5359
core.info(`Starting cleanup with hidden detection logic`);
5460
core.info(` Dry run: ${dryRun}`);
55-
core.info(` Scan issues: ${scanIssues}`);
61+
core.info(` Scan open issues: ${scanIssues}`);
62+
core.info(` Scan closed issues: ${scanClosedIssues}`);
5663
core.info(` Scan comments: ${scanComments}`);
5764
core.info(``);
58-
65+
5966
// Load detection script from secret
6067
const detectionScript = process.env.SPAM_DETECTION_SCRIPT;
61-
68+
6269
if (!detectionScript) {
6370
core.error("SPAM_DETECTION_SCRIPT secret not found!");
71+
core.error("The cleanup tool requires the SPAM_DETECTION_SCRIPT secret to be configured.");
72+
core.error("Please set up the secret before running cleanup.");
73+
core.error("See documentation for setup instructions.");
6474
core.setFailed("Spam detection not configured");
6575
return;
6676
}
67-
77+
6878
// Create analyzer function (all logic hidden in secret)
6979
let analyzeContent;
70-
80+
7181
try {
7282
// Wrap the detection script to create a reusable analyzer
7383
const analyzerWrapper = `
7484
(async function(github, context, core) {
7585
${detectionScript}
7686
})
7787
`;
78-
88+
7989
// Execute to get the detection capabilities
8090
const detectionModule = eval(analyzerWrapper);
8191
await detectionModule(github, context, core);
82-
92+
8393
// Create a mock context for analysis
8494
analyzeContent = async function(body, actor, assoc) {
8595
// Create mock event for analysis
@@ -96,87 +106,87 @@ jobs:
96106
action: 'opened'
97107
}
98108
};
99-
109+
100110
// Run detection on mock event
101111
let isSpam = false;
102112
let reason = "Clean";
103113
let score = 0;
104-
114+
105115
// Capture the detection result by checking if action would be taken
106116
const originalUpdate = github.rest.issues.update;
107117
const originalDelete = github.rest.issues.deleteComment;
108-
118+
109119
let detectionResult = { isSpam: false };
110-
120+
111121
github.rest.issues.update = async (params) => {
112122
detectionResult = { isSpam: true, reason: "Would close", score: 2 };
113123
return { data: {} };
114124
};
115-
125+
116126
github.rest.issues.deleteComment = async (params) => {
117127
detectionResult = { isSpam: true, reason: "Would delete", score: 2 };
118128
return { data: {} };
119129
};
120-
130+
121131
github.rest.issues.createComment = async () => ({ data: {} });
122132
github.rest.pulls.update = async () => ({ data: {} });
123133
github.rest.pulls.deleteReviewComment = async () => ({ data: {} });
124-
134+
125135
try {
126136
await detectionModule(github, mockContext, core);
127137
} catch (e) {
128138
// Ignore errors from mock execution
129139
}
130-
140+
131141
// Restore original functions
132142
github.rest.issues.update = originalUpdate;
133143
github.rest.issues.deleteComment = originalDelete;
134-
144+
135145
return detectionResult;
136146
};
137-
147+
138148
core.info("✅ Loaded spam detection from secret");
139149
core.info("");
140-
150+
141151
} catch (err) {
142152
core.error(`Failed to load detection: ${err.message}`);
143153
core.setFailed(`Detection script error`);
144154
return;
145155
}
146-
156+
147157
let totalScanned = 0;
148158
let totalSpam = 0;
149159
let totalClosed = 0;
150160
let totalDeleted = 0;
151-
161+
152162
// Scan issues
153163
if (scanIssues) {
154164
core.info("Scanning open issues...");
155165
let page = 1;
156-
166+
157167
while (maxIssues === 0 || totalScanned < maxIssues) {
158168
const issues = await github.rest.issues.listForRepo({
159169
owner, repo, state: 'open', per_page: 100, page
160170
});
161-
171+
162172
if (issues.data.length === 0) break;
163-
173+
164174
for (const issue of issues.data) {
165175
if (issue.pull_request) continue;
166176
if (maxIssues > 0 && totalScanned >= maxIssues) break;
167-
177+
168178
totalScanned++;
169179
const analysis = await analyzeContent(
170180
issue.body || "",
171181
issue.user?.login || "unknown",
172182
issue.author_association || "NONE"
173183
);
174-
184+
175185
if (analysis.isSpam) {
176186
totalSpam++;
177187
core.warning(`[SPAM] Issue #${issue.number} by @${issue.user?.login}`);
178188
core.warning(` Preview: ${(issue.body || "").substring(0, 150)}...`);
179-
189+
180190
if (!dryRun) {
181191
try {
182192
await github.rest.issues.update({
@@ -198,33 +208,86 @@ jobs:
198208
page++;
199209
}
200210
}
201-
211+
212+
// Scan closed issues
213+
if (scanClosedIssues) {
214+
core.info("Scanning closed issues...");
215+
let page = 1;
216+
let closedScanned = 0;
217+
218+
while (maxIssues === 0 || closedScanned < maxIssues) {
219+
const issues = await github.rest.issues.listForRepo({
220+
owner, repo, state: 'closed', per_page: 100, page
221+
});
222+
223+
if (issues.data.length === 0) break;
224+
225+
for (const issue of issues.data) {
226+
if (issue.pull_request) continue;
227+
if (maxIssues > 0 && closedScanned >= maxIssues) break;
228+
229+
closedScanned++;
230+
const analysis = await analyzeContent(
231+
issue.body || "",
232+
issue.user?.login || "unknown",
233+
issue.author_association || "NONE"
234+
);
235+
236+
if (analysis.isSpam) {
237+
totalSpam++;
238+
core.warning(`[SPAM] Closed Issue #${issue.number} by @${issue.user?.login}`);
239+
core.warning(` Preview: ${(issue.body || "").substring(0, 150)}...`);
240+
core.warning(` Already closed, but content still visible`);
241+
242+
if (!dryRun) {
243+
try {
244+
// Rewrite the closed spam issue to remove content
245+
await github.rest.issues.update({
246+
owner, repo, issue_number: issue.number,
247+
title: "[MODERATED] Content Removed",
248+
body: "**This content has been automatically moderated and removed.**\n\n" +
249+
"The original content violated our spam policy and has been hidden.\n\n" +
250+
"_This was detected during a cleanup scan of existing content._"
251+
});
252+
totalClosed++;
253+
core.notice(`✓ Moderated closed spam issue #${issue.number}`);
254+
} catch (err) {
255+
core.error(`✗ Failed to moderate issue #${issue.number}: ${err.message}`);
256+
}
257+
}
258+
}
259+
}
260+
page++;
261+
}
262+
core.info(`Scanned ${closedScanned} closed issues`);
263+
}
264+
202265
// Scan comments
203266
if (scanComments) {
204267
core.info("Scanning issue comments...");
205268
let page = 1;
206269
let commentCount = 0;
207-
270+
208271
while (page <= 10) {
209272
const comments = await github.rest.issues.listCommentsForRepo({
210273
owner, repo, per_page: 100, page, sort: 'created', direction: 'desc'
211274
});
212-
275+
213276
if (comments.data.length === 0) break;
214-
277+
215278
for (const comment of comments.data) {
216279
commentCount++;
217280
const analysis = await analyzeContent(
218281
comment.body || "",
219282
comment.user?.login || "unknown",
220283
comment.author_association || "NONE"
221284
);
222-
285+
223286
if (analysis.isSpam) {
224287
totalSpam++;
225288
core.warning(`[SPAM] Comment #${comment.id} by @${comment.user?.login}`);
226289
core.warning(` Preview: ${(comment.body || "").substring(0, 150)}...`);
227-
290+
228291
if (!dryRun) {
229292
try {
230293
await github.rest.issues.deleteComment({
@@ -240,19 +303,25 @@ jobs:
240303
}
241304
page++;
242305
}
243-
306+
244307
core.info(`Scanned ${commentCount} comments`);
245308
}
246-
309+
247310
// Summary
248311
core.notice("=".repeat(60));
249312
core.notice(`Cleanup Summary ${dryRun ? '(DRY RUN)' : '(EXECUTED)'}`);
250-
core.notice(`Total scanned: ${totalScanned} issues`);
313+
core.notice("=".repeat(60));
314+
if (scanIssues) core.notice(`✓ Scanned open issues: ${totalScanned}`);
315+
if (scanClosedIssues) core.notice(`✓ Scanned closed issues for content removal`);
316+
if (scanComments) core.notice(`✓ Scanned comments`);
317+
core.notice("");
251318
core.notice(`Total spam found: ${totalSpam}`);
252319
if (!dryRun) {
253-
core.notice(`Issues closed: ${totalClosed}`);
254-
core.notice(`Comments deleted: ${totalDeleted}`);
320+
core.notice(`Actions taken:`);
321+
core.notice(` - Issues closed/moderated: ${totalClosed}`);
322+
core.notice(` - Comments deleted: ${totalDeleted}`);
255323
} else {
256-
core.notice("DRY RUN - No actions taken. Set dry_run to 'false' to execute.");
324+
core.notice("⚠️ DRY RUN - No actions taken");
325+
core.notice(" Set dry_run to 'false' to execute cleanup");
257326
}
258327
core.notice("=".repeat(60));

0 commit comments

Comments
 (0)