issue-guard.yml 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. name: Issue Guard
  2. on:
  3. issues:
  4. types: [opened]
  5. issue_comment:
  6. types: [created]
  7. pull_request_review_comment:
  8. types: [created]
  9. jobs:
  10. moderate:
  11. runs-on: ubuntu-latest
  12. permissions:
  13. issues: write
  14. pull-requests: write
  15. models: read
  16. contents: read
  17. steps:
  18. - uses: actions/checkout@v4
  19. # AI 内容审核(垃圾检测 + 链接检测 + AI 生成检测)
  20. - uses: github/ai-moderator@v1
  21. with:
  22. token: ${{ secrets.GITHUB_TOKEN }}
  23. spam-label: 'spam'
  24. ai-label: 'ai-generated'
  25. minimize-detected-comments: true
  26. enable-spam-detection: true
  27. enable-link-spam-detection: true
  28. enable-ai-detection: true
  29. # 账号年龄检查(仅针对新建 Issue)
  30. - name: Account age check
  31. if: github.event_name == 'issues'
  32. uses: actions/github-script@v7
  33. with:
  34. script: |
  35. const issue = context.payload.issue;
  36. const username = issue.user.login;
  37. // 跳过项目成员
  38. if (['OWNER', 'MEMBER', 'COLLABORATOR'].includes(issue.author_association)) {
  39. core.info(`Skip check for ${username} (${issue.author_association})`);
  40. return;
  41. }
  42. // 获取用户信息
  43. const { data: user } = await github.rest.users.getByUsername({ username });
  44. const accountAgeDays = Math.floor((Date.now() - new Date(user.created_at)) / 86400000);
  45. const isNewAccount = accountAgeDays < 30;
  46. if (!isNewAccount) {
  47. core.info(`${username} account age: ${accountAgeDays} days, OK`);
  48. return;
  49. }
  50. // 检查 AI moderator 是否已打 spam 标签
  51. const { data: labels } = await github.rest.issues.listLabelsOnIssue({
  52. ...context.repo,
  53. issue_number: issue.number
  54. });
  55. const hasSpamLabel = labels.some(l => l.name === 'spam');
  56. // 两个条件都满足:新账号 + AI 判定为垃圾 → 关闭 + 锁定
  57. if (isNewAccount && hasSpamLabel) {
  58. await github.rest.issues.createComment({
  59. ...context.repo,
  60. issue_number: issue.number,
  61. body: [
  62. '👋 你好,感谢你对本项目的关注!',
  63. '',
  64. '为维护社区环境、防止垃圾信息和自动化 bot 的干扰,我们对 Issue 进行了自动审核。',
  65. '很遗憾,你的 Issue 未能通过审核(账号注册时间较短且内容触发了自动检测)。',
  66. '',
  67. '**如有误判,还请见谅!** 你可以在账号满 30 天后重新提交,或通过项目 README 中的联系方式反馈。',
  68. '',
  69. '感谢理解与支持 🙏',
  70. ].join('\n')
  71. });
  72. await github.rest.issues.update({
  73. ...context.repo,
  74. issue_number: issue.number,
  75. state: 'closed',
  76. state_reason: 'not_planned'
  77. });
  78. await github.rest.issues.lock({
  79. ...context.repo,
  80. issue_number: issue.number,
  81. lock_reason: 'spam'
  82. });
  83. core.info(`Issue #${issue.number} closed: new account (${accountAgeDays}d) + spam detected`);
  84. }