mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 09:37:15 +00:00
- Added Code of Conduct with clear behavioral standards - Created Contributing guidelines with submission requirements - Implemented structured issue templates (bug reports, features, new agents, moderation) - Disabled blank issues to enforce template usage - Added automated content moderation via GitHub Actions: * Real-time scanning for hate speech, threats, and profanity * Automatic closure/locking of critical violations * Moderation alerts for maintainer review - Set up welcome system for new contributors with community guidelines - Enabled GitHub Discussions as alternative to issues for general questions - Closed and locked existing hate speech issue #30 - Blocked offending user account This creates a multi-layered defense against inappropriate content while maintaining an open, welcoming environment for legitimate contributors.
207 lines
9.1 KiB
YAML
207 lines
9.1 KiB
YAML
name: Content Moderation
|
|
|
|
on:
|
|
issues:
|
|
types: [opened, edited]
|
|
issue_comment:
|
|
types: [created, edited]
|
|
pull_request:
|
|
types: [opened, edited]
|
|
pull_request_review_comment:
|
|
types: [created, edited]
|
|
|
|
jobs:
|
|
moderate-content:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
issues: write
|
|
pull-requests: write
|
|
contents: read
|
|
|
|
steps:
|
|
- name: Check for inappropriate content
|
|
id: content-check
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
// Define inappropriate content patterns
|
|
const hateWords = [
|
|
// Racial slurs (censored for detection)
|
|
'n[i1]gg[e3]r', 'n[i1]gg[a4]', 'ch[i1]nk', 'sp[i1]c', 'k[i1]k[e3]',
|
|
'w[e3]tb[a4]ck', 'b[e3][a4]n[e3]r', 'g[o0][o0]k', 'r[a4]gh[e3][a4]d',
|
|
// Homophobic/transphobic slurs
|
|
'f[a4]gg[o0]t', 'tr[a4]nn[y1]', 'd[y1]k[e3]',
|
|
// Religious/ethnic slurs
|
|
'j[e3]w', 'r[a4]gh[e3][a4]d', 'c[a4]m[e3]lj[o0]ck[e3]y',
|
|
// General offensive terms
|
|
'r[e3]t[a4]rd', 'sp[a4]st[i1]c'
|
|
];
|
|
|
|
const profanity = [
|
|
'f[u*]ck[i1]ng?', 'sh[i1]t', 'b[i1]tch', 'c[u*]nt', 'p[i1]ss',
|
|
'd[a4]mn', 'h[e3]ll', 'cr[a4]p', '[a4]ss', 'b[a4]st[a4]rd'
|
|
];
|
|
|
|
const threats = [
|
|
'k[i1]ll y[o0]u', 'd[i1][e3]', 'murd[e3]r', 'h[a4]rm y[o0]u',
|
|
'hurt y[o0]u', 'destroy y[o0]u', 'end y[o0]u'
|
|
];
|
|
|
|
// Get content based on event type
|
|
let content = '';
|
|
let contentType = '';
|
|
let itemNumber = 0;
|
|
|
|
if (context.eventName === 'issues') {
|
|
content = context.payload.issue.title + ' ' + context.payload.issue.body;
|
|
contentType = 'issue';
|
|
itemNumber = context.payload.issue.number;
|
|
} else if (context.eventName === 'issue_comment') {
|
|
content = context.payload.comment.body;
|
|
contentType = 'issue comment';
|
|
itemNumber = context.payload.issue.number;
|
|
} else if (context.eventName === 'pull_request') {
|
|
content = context.payload.pull_request.title + ' ' + context.payload.pull_request.body;
|
|
contentType = 'pull request';
|
|
itemNumber = context.payload.pull_request.number;
|
|
} else if (context.eventName === 'pull_request_review_comment') {
|
|
content = context.payload.comment.body;
|
|
contentType = 'PR comment';
|
|
itemNumber = context.payload.pull_request.number;
|
|
}
|
|
|
|
if (!content) return;
|
|
|
|
const normalizedContent = content.toLowerCase()
|
|
.replace(/[^a-z0-9\s]/g, '')
|
|
.replace(/\s+/g, ' ');
|
|
|
|
// Check for violations
|
|
let violation = null;
|
|
let severity = 'low';
|
|
|
|
// Check hate speech (highest severity)
|
|
for (const word of hateWords) {
|
|
const regex = new RegExp(word, 'i');
|
|
if (regex.test(normalizedContent)) {
|
|
violation = 'hate speech';
|
|
severity = 'critical';
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check threats (high severity)
|
|
if (!violation) {
|
|
for (const threat of threats) {
|
|
const regex = new RegExp(threat, 'i');
|
|
if (regex.test(normalizedContent)) {
|
|
violation = 'threats';
|
|
severity = 'high';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check excessive profanity (medium severity)
|
|
if (!violation) {
|
|
let profanityCount = 0;
|
|
for (const word of profanity) {
|
|
const regex = new RegExp(word, 'gi');
|
|
const matches = normalizedContent.match(regex);
|
|
if (matches) profanityCount += matches.length;
|
|
}
|
|
if (profanityCount >= 3) {
|
|
violation = 'excessive profanity';
|
|
severity = 'medium';
|
|
}
|
|
}
|
|
|
|
// Set outputs
|
|
core.setOutput('violation', violation || 'none');
|
|
core.setOutput('severity', severity);
|
|
core.setOutput('content-type', contentType);
|
|
core.setOutput('item-number', itemNumber);
|
|
|
|
if (violation) {
|
|
console.log(`⚠️ Detected ${violation} (${severity} severity) in ${contentType} #${itemNumber}`);
|
|
}
|
|
|
|
- name: Handle critical violations
|
|
if: steps.content-check.outputs.violation != 'none' && steps.content-check.outputs.severity == 'critical'
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const itemNumber = ${{ steps.content-check.outputs.item-number }};
|
|
const contentType = '${{ steps.content-check.outputs.content-type }}';
|
|
const violation = '${{ steps.content-check.outputs.violation }}';
|
|
|
|
if (contentType === 'issue') {
|
|
// Close and lock the issue immediately
|
|
await github.rest.issues.update({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: itemNumber,
|
|
state: 'closed'
|
|
});
|
|
|
|
await github.rest.issues.lock({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: itemNumber,
|
|
lock_reason: 'off-topic'
|
|
});
|
|
|
|
// Add moderation comment
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: itemNumber,
|
|
body: `🚫 **This issue has been automatically closed and locked due to a Code of Conduct violation.**\n\n**Violation**: ${violation}\n\n**This action was taken because the content violates our community standards. Repeated violations may result in being blocked from this repository.**\n\n📖 Please review our [Code of Conduct](.github/CODE_OF_CONDUCT.md) and [Contributing Guidelines](.github/CONTRIBUTING.md).`
|
|
});
|
|
}
|
|
|
|
- name: Handle high severity violations
|
|
if: steps.content-check.outputs.violation != 'none' && steps.content-check.outputs.severity == 'high'
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const itemNumber = ${{ steps.content-check.outputs.item-number }};
|
|
const contentType = '${{ steps.content-check.outputs.content-type }}';
|
|
|
|
if (contentType === 'issue') {
|
|
// Add warning label and comment
|
|
await github.rest.issues.addLabels({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: itemNumber,
|
|
labels: ['moderation-review']
|
|
});
|
|
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: itemNumber,
|
|
body: `⚠️ **Content Warning**: This ${contentType} has been flagged for moderation review due to potentially inappropriate content.\n\nPlease ensure all content follows our [Code of Conduct](.github/CODE_OF_CONDUCT.md). A maintainer will review this shortly.`
|
|
});
|
|
}
|
|
|
|
- name: Notify maintainers
|
|
if: steps.content-check.outputs.violation != 'none'
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const violation = '${{ steps.content-check.outputs.violation }}';
|
|
const severity = '${{ steps.content-check.outputs.severity }}';
|
|
const contentType = '${{ steps.content-check.outputs.content-type }}';
|
|
const itemNumber = ${{ steps.content-check.outputs.item-number }};
|
|
|
|
// Create a moderation alert issue
|
|
await github.rest.issues.create({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
title: `[MODERATION ALERT] ${violation} detected in ${contentType} #${itemNumber}`,
|
|
body: `🚨 **Automated Moderation Alert**\n\n**Details:**\n- **Type**: ${violation} (${severity} severity)\n- **Location**: ${contentType} #${itemNumber}\n- **Action taken**: ${severity === 'critical' ? 'Automatically closed and locked' : 'Flagged for review'}\n\n**Next steps:**\n- [ ] Review the flagged content\n- [ ] Take additional action if needed\n- [ ] Consider blocking repeat offenders\n\n**Links:**\n- [View ${contentType} #${itemNumber}](https://github.com/${context.repo.owner}/${context.repo.repo}/${contentType === 'issue' ? 'issues' : 'pull'}/${itemNumber})\n\nThis alert was generated automatically by the content moderation system.`,
|
|
labels: ['moderation', 'automated-alert']
|
|
});
|
|
|
|
console.log(`📧 Created moderation alert for ${violation} in ${contentType} #${itemNumber}`); |