Skip to content

Commit cd9bfe4

Browse files
feat: add automated spam detection workflow
1 parent 7a4280a commit cd9bfe4

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
name: '🚫 Gemini Spam Detection'
2+
3+
on:
4+
issues:
5+
types:
6+
- 'opened'
7+
- 'reopened'
8+
- 'edited'
9+
workflow_dispatch:
10+
inputs:
11+
issue_number:
12+
description: 'Issue number to check'
13+
required: true
14+
type: 'number'
15+
16+
concurrency:
17+
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number }}'
18+
cancel-in-progress: true
19+
20+
permissions:
21+
contents: 'read'
22+
id-token: 'write'
23+
issues: 'write'
24+
25+
jobs:
26+
detect-spam:
27+
if: github.repository == 'google-gemini/gemini-cli'
28+
runs-on: ubuntu-latest
29+
steps:
30+
- name: 'Checkout'
31+
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
32+
33+
- name: 'Get issue data'
34+
id: 'get_issue_data'
35+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v6
36+
with:
37+
script: |
38+
const issueNumber = context.eventName === 'workflow_dispatch'
39+
? context.payload.inputs.issue_number
40+
: context.payload.issue.number;
41+
42+
const { data: issue } = await github.rest.issues.get({
43+
owner: context.repo.owner,
44+
repo: context.repo.repo,
45+
issue_number: issueNumber,
46+
});
47+
48+
core.setOutput('title', issue.title);
49+
core.setOutput('body', issue.body || '');
50+
core.setOutput('number', issue.number);
51+
52+
- name: 'Generate GitHub App Token'
53+
id: 'generate_token'
54+
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
55+
with:
56+
app-id: '${{ secrets.APP_ID }}'
57+
private-key: '${{ secrets.PRIVATE_KEY }}'
58+
permission-issues: 'write'
59+
60+
- name: 'Run Gemini Spam Analysis'
61+
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
62+
id: 'spam_analysis'
63+
env:
64+
GITHUB_TOKEN: '' # No token needed for the model
65+
ISSUE_TITLE: '${{ steps.get_issue_data.outputs.title }}'
66+
ISSUE_BODY: '${{ steps.get_issue_data.outputs.body }}'
67+
with:
68+
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
69+
gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
70+
gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}'
71+
gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
72+
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
73+
settings: |-
74+
{
75+
"maxSessionTurns": 5,
76+
"telemetry": {
77+
"enabled": true,
78+
"target": "gcp"
79+
}
80+
}
81+
prompt: |-
82+
## Role
83+
You are a GitHub repository moderator. Your task is to detect if a GitHub issue is spam.
84+
85+
## Input
86+
Title: ${{ env.ISSUE_TITLE }}
87+
Body: ${{ env.ISSUE_BODY }}
88+
89+
## Definition of Spam
90+
- Unsolicited advertisements or promotions.
91+
- Content that is completely unrelated to the project (software engineering, CLI, Gemini).
92+
- Gibberish or nonsensical text.
93+
- Malicious links or phishing attempts.
94+
- Repeated identical posts (bot behavior).
95+
96+
## Instructions
97+
1. Analyze the title and body.
98+
2. Determine if it matches the definition of spam.
99+
3. Output a JSON object with "is_spam" (boolean) and "reason" (string).
100+
101+
Example:
102+
{"is_spam": true, "reason": "The issue contains only an advertisement for a shoe store."}
103+
{"is_spam": false, "reason": "This is a legitimate bug report about installation."}
104+
105+
- name: 'Handle Spam Issue'
106+
if: steps.spam_analysis.outputs.summary != ''
107+
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v6
108+
env:
109+
ANALYSIS_RESULT: '${{ steps.spam_analysis.outputs.summary }}'
110+
ISSUE_NUMBER: '${{ steps.get_issue_data.outputs.number }}'
111+
with:
112+
github-token: '${{ steps.generate_token.outputs.token }}'
113+
script: |
114+
const rawOutput = process.env.ANALYSIS_RESULT;
115+
let result;
116+
try {
117+
result = JSON.parse(rawOutput);
118+
} catch (e) {
119+
const match = rawOutput.match(/```json\s*([\s\S]*?)\s*```/);
120+
if (match) {
121+
result = JSON.parse(match[1]);
122+
} else {
123+
core.setFailed('Failed to parse JSON output from Gemini');
124+
return;
125+
}
126+
}
127+
128+
if (result.is_spam) {
129+
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
130+
131+
// Add 'spam' label
132+
await github.rest.issues.addLabels({
133+
owner: context.repo.owner,
134+
repo: context.repo.repo,
135+
issue_number: issueNumber,
136+
labels: ['spam']
137+
});
138+
139+
// Close the issue
140+
await github.rest.issues.update({
141+
owner: context.repo.owner,
142+
repo: context.repo.repo,
143+
issue_number: issueNumber,
144+
state: 'closed',
145+
state_reason: 'not_planned'
146+
});
147+
148+
// Comment
149+
await github.rest.issues.createComment({
150+
owner: context.repo.owner,
151+
repo: context.repo.repo,
152+
issue_number: issueNumber,
153+
body: `This issue has been automatically marked as spam and closed.\n\n**Reason:** ${result.reason}`
154+
});
155+
} else {
156+
console.log('Issue is not spam.');
157+
}

0 commit comments

Comments
 (0)