name: AI Moderator on: issues: types: [opened] issue_comment: types: [created] pull_request_review_comment: types: [created] permissions: issues: write pull-requests: write contents: read jobs: moderate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/github-script@v9 env: GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} with: script: | const fs = require('fs'); const content = context.payload.issue?.body || context.payload.comment?.body || ''; if (!content.trim()) { console.log('No content to analyze'); return; } let coc = ''; let contributing = ''; try { coc = fs.readFileSync('.github/CODE_OF_CONDUCT.md', 'utf8'); } catch (e) {} try { contributing = fs.readFileSync('CONTRIBUTING.md', 'utf8'); } catch (e) {} const prompt = [ 'You are a community moderator for the Antergos NeXT project.', '', 'Code of Conduct:', coc, '', 'Contributing Guidelines:', contributing, '', 'Analyze the following content for violations of this Code of Conduct.', 'Respond with ONLY valid JSON - no markdown, no backticks, no explanation.', '', '{"violation": true/false, "reason": "short reason or empty", "severity": 1-10}', '', 'Content:', content ].join('\n'); const model = 'llama-3.1-8b-instant'; let decision; try { const response = await fetch('https://api.groq.com/openai/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.GROQ_API_KEY}` }, body: JSON.stringify({ model, messages: [ { role: 'system', content: 'You are a helpful community moderator. Respond only with valid JSON as instructed.' }, { role: 'user', content: prompt } ], temperature: 0.1, max_tokens: 150 }) }); const data = await response.json(); const raw = data.choices?.[0]?.message?.content || '{}'; decision = JSON.parse(raw.replace(/```json|```/g, '').trim()); console.log(`Decision: ${JSON.stringify(decision)}`); } catch (err) { console.log(`API error: ${err}`); return; } if (!decision.violation) { console.log('Content deemed acceptable'); return; } const issueNumber = context.payload.issue?.number || context.payload.pull_request?.number; if (!issueNumber) { console.log('No issue/PR number found'); return; } const warning = [ '**AI Moderator Notice**', '', 'Your comment appears to violate the project\'s ' + '[Code of Conduct](' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/blob/master/.github/CODE_OF_CONDUCT.md).', '', 'Reason: ' + decision.reason, '', 'Please keep discussions respectful and constructive. Repeated violations may result in moderation actions.' ].join('\n'); try { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, body: warning }); await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, labels: ['ai-moderated'] }); console.log('Warning posted and label added'); } catch (err) { console.log(`GitHub API error: ${err}`); }