From 6ed753cf599d339be391e18634465af4f0cd7d3d Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 5 Sep 2026 14:33:20 -0700 Subject: [PATCH] Update issue-bot.yml --- .github/workflows/issue-bot.yml | 73 ++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/.github/workflows/issue-bot.yml b/.github/workflows/issue-bot.yml index 8fad4e233..cf2602110 100644 --- a/.github/workflows/issue-bot.yml +++ b/.github/workflows/issue-bot.yml @@ -5,52 +5,79 @@ on: jobs: Anti-slop: # Note: peakoss/anti-slop does not support the `issues` event yet (all of its - # issue inputs are still commented out upstream), so the honeypot check that - # the PR Bot workflow gets from the action is implemented manually here. + # issue inputs are still commented out upstream), so the checks that the PR Bot + # workflow gets from the action are implemented manually here. runs-on: ubuntu-latest permissions: issues: write steps: - - name: Check for honeypot token + - name: Check for slop signals uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const issue = context.payload.issue; if (['OWNER', 'MEMBER', 'COLLABORATOR'].includes(issue.author_association)) { - core.info('Skipping check: user is a maintainer'); + core.info('Skipping checks: user is a maintainer'); return; } if (issue.user.type === 'Bot') { - core.info('Skipping check: user is a bot'); + core.info('Skipping checks: user is a bot'); return; } - const haystack = `${issue.title}\n${issue.body ?? ''}`; - if (!haystack.toUpperCase().includes('ASLOP-PR-VERIFY')) { - core.info('Honeypot token not found'); - return; - } - - core.info('Honeypot token found, closing issue'); - const common = { owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, }; - await github.rest.issues.createComment({ - ...common, - body: - "This issue was automatically closed because it contains a marker that is only visible to " + - "automated tools, which indicates it was generated by an AI agent without being disclosed as such.\n\n" + - "Please see our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md#use-of-ai-tools) " + - "and [Code of Conduct](https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md). " + - "You are welcome to open a new issue that describes the problem you observed in your own words.", - }); + const contributing = + 'https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md#use-of-ai-tools'; + const codeOfConduct = + 'https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md'; + const newIssue = 'https://github.com/paperless-ngx/paperless-ngx/issues/new/choose'; + // Honeypot: a token only an AI agent reading the raw issue template would include. + const haystack = `${issue.title}\n${issue.body ?? ''}`; + const honeypot = haystack.toUpperCase().includes('ASLOP-PR-VERIFY'); + + // Issues opened through the form always get the template's default labels. GitHub + // applies them a second or two *after* creation, so re-read them instead of trusting + // the webhook payload, and retry before concluding that there are none. + const templateLabels = ['bug', 'unconfirmed']; + let labels = []; + for (const delay of [0, 15000, 30000]) { + if (delay) await new Promise((resolve) => setTimeout(resolve, delay)); + const { data } = await github.rest.issues.get({ ...common }); + labels = data.labels.map((label) => (typeof label === 'string' ? label : label.name)); + if (labels.some((label) => templateLabels.includes(label))) break; + } + const bypassedTemplate = !labels.some((label) => templateLabels.includes(label)); + core.info(`Labels: [${labels.join(', ')}], honeypot: ${honeypot}, bypassed template: ${bypassedTemplate}`); + + if (!honeypot && !bypassedTemplate) { + core.info('No slop signals found'); + return; + } + + // The honeypot is only ever tripped deliberately, so that message can name the cause. + // A missing template label only tells us the issue did not come from the form, which + // is reason enough to close it, but not proof of how it was written. + const body = honeypot + ? 'This issue was automatically closed because it contains a marker that is only visible to ' + + 'automated tools, which indicates it was generated by an AI agent without being disclosed as such.\n\n' + + `Please see our [contributing guidelines](${contributing}) and [Code of Conduct](${codeOfConduct}). ` + + 'You are welcome to open a new issue that describes the problem you observed in your own words.' + : 'This issue was automatically closed because it was not opened using our bug report form. ' + + 'Issues have to be created through the form so that the details we need to investigate are included.\n\n' + + `If the problem is still there, please [open a new issue](${newIssue}) using the form — that is all it takes ` + + 'to get it looked at, and no other action is needed here.\n\n' + + 'If any part of your report was written by an AI tool or agent, you must say so: undisclosed AI-generated ' + + `contributions are a violation of our [Code of Conduct](${codeOfConduct}), and such reports must describe the ` + + `behavior you observed only, without code analysis or suggested fixes. See our [contributing guidelines](${contributing}).`; + + await github.rest.issues.createComment({ ...common, body }); await github.rest.issues.addLabels({ ...common, labels: ['ai'] }); - await github.rest.issues.update({ ...common, state: 'closed', state_reason: 'not_planned' });