Skip to content

Delete Spam Issues

Delete Spam Issues #36

name: Delete Spam Issues
on:
# Run on a schedule to batch process spam issues
schedule:
- cron: '*/5 * * * *' # Run every 5 minutes
# Also run manually from the Actions tab
workflow_dispatch:
jobs:
delete-spam:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Delete spam issues with GraphQL
uses: actions/github-script@v7
with:
github-token: ${{ secrets.ISSUE_SPAM_PROTECTION_TOKEN }}
script: |
// Helper function to add delay between operations
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Get issue ID
async function getIssueId(issueNumber) {
const query = `
query GetIssueId($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
issue(number: $number) {
id
title
url
state
}
}
}
`;
try {
const result = await github.graphql(query, {
owner: context.repo.owner,
repo: context.repo.repo,
number: issueNumber
});
if (!result.repository?.issue?.id) {
console.error(`Could not find GraphQL ID for issue #${issueNumber}. Full response:`, JSON.stringify(result, null, 2));
return null;
}
console.log(`Found issue #${issueNumber}: "${result.repository.issue.title}" (${result.repository.issue.state})`);
return result.repository.issue.id;
} catch (error) {
console.error(`Failed to get issue ID for #${issueNumber}:`, error.message);
console.error(JSON.stringify(error, null, 2));
return null;
}
}
// Delete issue using GraphQL
async function deleteIssue(issueId, issueNumber) {
if (!issueId) {
console.error(`Cannot delete issue #${issueNumber}: No valid ID provided`);
return false;
}
const mutation = `
mutation DeleteIssue($issueId: ID!) {
deleteIssue(input: {issueId: $issueId}) {
clientMutationId
}
}
`;
try {
await github.graphql(mutation, {
issueId: issueId
});
return true;
} catch (error) {
console.error(`Failed to delete issue #${issueNumber}:`, error.message);
console.error(JSON.stringify(error, null, 2));
return false;
}
}
// Process all spam-labeled issues
console.log("Finding all issues labeled as spam...");
// Rate limiting parameters
const RATE_LIMIT_DELAY = 1000; // 1 second delay between operations
const ITEMS_PER_PAGE = 30; // Number of items per page
const MAX_PAGES = 3; // Maximum number of pages to process in one run
let page = 1;
let processedCount = 0;
let successCount = 0;
let hasMorePages = true;
// Process issues page by page with resilience
while (hasMorePages && page <= MAX_PAGES) {
try {
console.log(`Fetching page ${page} of spam issues...`);
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'all', // Get both open and closed issues
labels: 'spam',
per_page: ITEMS_PER_PAGE,
page: page
});
if (issues.data.length === 0) {
console.log("No more issues found.");
hasMorePages = false;
break;
}
console.log(`Found ${issues.data.length} spam issues on page ${page}`);
for (const issue of issues.data) {
const issueNumber = issue.number;
processedCount++;
try {
// Skip if this is a pull request
if (issue.pull_request) {
console.log(`Skipping #${issueNumber} because it's a pull request, not an issue`);
continue;
}
console.log(`Processing spam issue #${issueNumber}: "${issue.title}"`);
const issueId = await getIssueId(issueNumber);
if (issueId) {
// Add delay before deletion
await sleep(RATE_LIMIT_DELAY);
const success = await deleteIssue(issueId, issueNumber);
if (success) {
console.log(`Successfully deleted issue #${issueNumber}`);
successCount++;
}
}
} catch (error) {
console.error(`Error processing issue #${issueNumber}:`, error.message);
// Continue with next issue even if this one fails
}
// Add delay after each issue processing
await sleep(RATE_LIMIT_DELAY);
}
page++;
} catch (error) {
console.error(`Error processing page ${page}:`, error.message);
break; // Break the loop if we can't process a page
}
}
if (hasMorePages && page > MAX_PAGES) {
console.log(`Pagination limit reached (${MAX_PAGES} pages). Remaining issues will be processed in future runs.`);
}
console.log(`Summary: Successfully deleted ${successCount} out of ${processedCount} processed spam issues`);