mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-06 01:38:01 +00:00
84 lines
4.6 KiB
YAML
84 lines
4.6 KiB
YAML
name: Issue Bot
|
|
on:
|
|
issues:
|
|
types: [opened]
|
|
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 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 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 checks: user is a maintainer');
|
|
return;
|
|
}
|
|
|
|
if (issue.user.type === 'Bot') {
|
|
core.info('Skipping checks: user is a bot');
|
|
return;
|
|
}
|
|
|
|
const common = {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: issue.number,
|
|
};
|
|
|
|
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' });
|