Skip to content

Commit dddd153

Browse files
committed
chore: refactor spam filter
Signed-off-by: Huamin Chen <[email protected]>
1 parent 099527f commit dddd153

File tree

2 files changed

+109
-43
lines changed

2 files changed

+109
-43
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,21 @@ 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) {
3434
core.error("SPAM_DETECTION_SCRIPT secret not found!");
3535
core.setFailed("Spam filter not configured");
3636
return;
3737
}
38-
38+
3939
try {
4040
// Execute the hidden script
4141
// The script has access to: github, context, core
4242
const detectSpam = eval(detectionScript);
43-
43+
4444
// Run the detection
4545
await detectSpam(github, context, core);
46-
46+
4747
} catch (err) {
4848
core.error(`Spam filter error: ${err.message}`);
4949
core.setFailed(`Filter execution failed: ${err.message}`);

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

Lines changed: 105 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,43 @@ 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!");
6471
core.setFailed("Spam detection not configured");
6572
return;
6673
}
67-
74+
6875
// Create analyzer function (all logic hidden in secret)
6976
let analyzeContent;
70-
77+
7178
try {
7279
// Wrap the detection script to create a reusable analyzer
7380
const analyzerWrapper = `
7481
(async function(github, context, core) {
7582
${detectionScript}
7683
})
7784
`;
78-
85+
7986
// Execute to get the detection capabilities
8087
const detectionModule = eval(analyzerWrapper);
8188
await detectionModule(github, context, core);
82-
89+
8390
// Create a mock context for analysis
8491
analyzeContent = async function(body, actor, assoc) {
8592
// Create mock event for analysis
@@ -96,87 +103,87 @@ jobs:
96103
action: 'opened'
97104
}
98105
};
99-
106+
100107
// Run detection on mock event
101108
let isSpam = false;
102109
let reason = "Clean";
103110
let score = 0;
104-
111+
105112
// Capture the detection result by checking if action would be taken
106113
const originalUpdate = github.rest.issues.update;
107114
const originalDelete = github.rest.issues.deleteComment;
108-
115+
109116
let detectionResult = { isSpam: false };
110-
117+
111118
github.rest.issues.update = async (params) => {
112119
detectionResult = { isSpam: true, reason: "Would close", score: 2 };
113120
return { data: {} };
114121
};
115-
122+
116123
github.rest.issues.deleteComment = async (params) => {
117124
detectionResult = { isSpam: true, reason: "Would delete", score: 2 };
118125
return { data: {} };
119126
};
120-
127+
121128
github.rest.issues.createComment = async () => ({ data: {} });
122129
github.rest.pulls.update = async () => ({ data: {} });
123130
github.rest.pulls.deleteReviewComment = async () => ({ data: {} });
124-
131+
125132
try {
126133
await detectionModule(github, mockContext, core);
127134
} catch (e) {
128135
// Ignore errors from mock execution
129136
}
130-
137+
131138
// Restore original functions
132139
github.rest.issues.update = originalUpdate;
133140
github.rest.issues.deleteComment = originalDelete;
134-
141+
135142
return detectionResult;
136143
};
137-
144+
138145
core.info("✅ Loaded spam detection from secret");
139146
core.info("");
140-
147+
141148
} catch (err) {
142149
core.error(`Failed to load detection: ${err.message}`);
143150
core.setFailed(`Detection script error`);
144151
return;
145152
}
146-
153+
147154
let totalScanned = 0;
148155
let totalSpam = 0;
149156
let totalClosed = 0;
150157
let totalDeleted = 0;
151-
158+
152159
// Scan issues
153160
if (scanIssues) {
154161
core.info("Scanning open issues...");
155162
let page = 1;
156-
163+
157164
while (maxIssues === 0 || totalScanned < maxIssues) {
158165
const issues = await github.rest.issues.listForRepo({
159166
owner, repo, state: 'open', per_page: 100, page
160167
});
161-
168+
162169
if (issues.data.length === 0) break;
163-
170+
164171
for (const issue of issues.data) {
165172
if (issue.pull_request) continue;
166173
if (maxIssues > 0 && totalScanned >= maxIssues) break;
167-
174+
168175
totalScanned++;
169176
const analysis = await analyzeContent(
170177
issue.body || "",
171178
issue.user?.login || "unknown",
172179
issue.author_association || "NONE"
173180
);
174-
181+
175182
if (analysis.isSpam) {
176183
totalSpam++;
177184
core.warning(`[SPAM] Issue #${issue.number} by @${issue.user?.login}`);
178185
core.warning(` Preview: ${(issue.body || "").substring(0, 150)}...`);
179-
186+
180187
if (!dryRun) {
181188
try {
182189
await github.rest.issues.update({
@@ -198,33 +205,86 @@ jobs:
198205
page++;
199206
}
200207
}
201-
208+
209+
// Scan closed issues
210+
if (scanClosedIssues) {
211+
core.info("Scanning closed issues...");
212+
let page = 1;
213+
let closedScanned = 0;
214+
215+
while (maxIssues === 0 || closedScanned < maxIssues) {
216+
const issues = await github.rest.issues.listForRepo({
217+
owner, repo, state: 'closed', per_page: 100, page
218+
});
219+
220+
if (issues.data.length === 0) break;
221+
222+
for (const issue of issues.data) {
223+
if (issue.pull_request) continue;
224+
if (maxIssues > 0 && closedScanned >= maxIssues) break;
225+
226+
closedScanned++;
227+
const analysis = await analyzeContent(
228+
issue.body || "",
229+
issue.user?.login || "unknown",
230+
issue.author_association || "NONE"
231+
);
232+
233+
if (analysis.isSpam) {
234+
totalSpam++;
235+
core.warning(`[SPAM] Closed Issue #${issue.number} by @${issue.user?.login}`);
236+
core.warning(` Preview: ${(issue.body || "").substring(0, 150)}...`);
237+
core.warning(` Already closed, but content still visible`);
238+
239+
if (!dryRun) {
240+
try {
241+
// Rewrite the closed spam issue to remove content
242+
await github.rest.issues.update({
243+
owner, repo, issue_number: issue.number,
244+
title: "[MODERATED] Content Removed",
245+
body: "**This content has been automatically moderated and removed.**\n\n" +
246+
"The original content violated our spam policy and has been hidden.\n\n" +
247+
"_This was detected during a cleanup scan of existing content._"
248+
});
249+
totalClosed++;
250+
core.notice(`✓ Moderated closed spam issue #${issue.number}`);
251+
} catch (err) {
252+
core.error(`✗ Failed to moderate issue #${issue.number}: ${err.message}`);
253+
}
254+
}
255+
}
256+
}
257+
page++;
258+
}
259+
core.info(`Scanned ${closedScanned} closed issues`);
260+
}
261+
202262
// Scan comments
203263
if (scanComments) {
204264
core.info("Scanning issue comments...");
205265
let page = 1;
206266
let commentCount = 0;
207-
267+
208268
while (page <= 10) {
209269
const comments = await github.rest.issues.listCommentsForRepo({
210270
owner, repo, per_page: 100, page, sort: 'created', direction: 'desc'
211271
});
212-
272+
213273
if (comments.data.length === 0) break;
214-
274+
215275
for (const comment of comments.data) {
216276
commentCount++;
217277
const analysis = await analyzeContent(
218278
comment.body || "",
219279
comment.user?.login || "unknown",
220280
comment.author_association || "NONE"
221281
);
222-
282+
223283
if (analysis.isSpam) {
224284
totalSpam++;
225285
core.warning(`[SPAM] Comment #${comment.id} by @${comment.user?.login}`);
226286
core.warning(` Preview: ${(comment.body || "").substring(0, 150)}...`);
227-
287+
228288
if (!dryRun) {
229289
try {
230290
await github.rest.issues.deleteComment({
@@ -240,19 +300,25 @@ jobs:
240300
}
241301
page++;
242302
}
243-
303+
244304
core.info(`Scanned ${commentCount} comments`);
245305
}
246-
306+
247307
// Summary
248308
core.notice("=".repeat(60));
249309
core.notice(`Cleanup Summary ${dryRun ? '(DRY RUN)' : '(EXECUTED)'}`);
250-
core.notice(`Total scanned: ${totalScanned} issues`);
310+
core.notice("=".repeat(60));
311+
if (scanIssues) core.notice(`✓ Scanned open issues: ${totalScanned}`);
312+
if (scanClosedIssues) core.notice(`✓ Scanned closed issues for content removal`);
313+
if (scanComments) core.notice(`✓ Scanned comments`);
314+
core.notice("");
251315
core.notice(`Total spam found: ${totalSpam}`);
252316
if (!dryRun) {
253-
core.notice(`Issues closed: ${totalClosed}`);
254-
core.notice(`Comments deleted: ${totalDeleted}`);
317+
core.notice(`Actions taken:`);
318+
core.notice(` - Issues closed/moderated: ${totalClosed}`);
319+
core.notice(` - Comments deleted: ${totalDeleted}`);
255320
} else {
256-
core.notice("DRY RUN - No actions taken. Set dry_run to 'false' to execute.");
321+
core.notice("⚠️ DRY RUN - No actions taken");
322+
core.notice(" Set dry_run to 'false' to execute cleanup");
257323
}
258324
core.notice("=".repeat(60));

0 commit comments

Comments
 (0)