Contributor Verification Bot #2489
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Contributor Verification Bot | |
| on: | |
| pull_request: | |
| types: [opened, reopened, synchronize] | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "*/10 * * * *" | |
| jobs: | |
| check_existing_prs: | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - uses: actions/github-script@v6 | |
| name: Check all open pull requests | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const prList = await github.rest.pulls.list({ | |
| owner, | |
| repo, | |
| state: 'open', | |
| per_page: 100 | |
| }); | |
| const repoOwner = "Kritikaadhikarii"; | |
| // Fetch first page of stargazers (max 100) | |
| const stargazersResponse = await github.rest.activity.listStargazersForRepo({ | |
| owner, | |
| repo, | |
| per_page: 100 | |
| }); | |
| const stargazers = stargazersResponse.data.map(user => user.login.toLowerCase()); | |
| for (const pr of prList.data) { | |
| const username = pr.user.login.toLowerCase(); | |
| const isFork = pr.head.repo.full_name !== `${owner}/${repo}`; | |
| // Check if user starred by checking in stargazers list | |
| const hasStarred = stargazers.includes(username); | |
| let isFollowing = false; | |
| try { | |
| await github.request('GET /users/{username}/following/{target_user}', { | |
| username, | |
| target_user: repoOwner | |
| }); | |
| isFollowing = true; | |
| } catch (e) { isFollowing = false; } | |
| const allOK = isFork && hasStarred && isFollowing; | |
| const msg = allOK | |
| ? `## ✅ All Requirements Met\n@${username}, thank you for contributing!\n\n| Check | Status |\n|-------|--------|\n| Forked Repo | ✅ |\n| Starred Repo | ✅ |\n| Following @${repoOwner} | ✅ |\n\nA maintainer will review this PR and close it soon with the appropriate tag.` | |
| : `## ⚠️ Verification Failed\n@${username}, please ensure you've forked, starred, and followed @${repoOwner} before contributing.\n\n| Check | Status |\n|-------|--------|\n| Forked Repo | ${isFork ? "✅" : "❌"} |\n| Starred Repo | ${hasStarred ? "✅" : "❌"} |\n| Following @${repoOwner} | ${isFollowing ? "✅" : "❌"} |\n\nThis PR will be closed automatically. You can create a new pr after following all of the instructions.`; | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: pr.number, | |
| body: msg | |
| }); | |
| if (!allOK) { | |
| await github.rest.pulls.update({ | |
| owner, | |
| repo, | |
| pull_number: pr.number, | |
| state: 'closed' | |
| }); | |
| } else { | |
| await github.rest.issues.addLabels({ | |
| owner, | |
| repo, | |
| issue_number: pr.number, | |
| labels: ['✅ Verified Contributor'] | |
| }); | |
| } | |
| } | |
| check_new_prs: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - uses: actions/github-script@v6 | |
| name: Check single pull request | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const pr = context.payload.pull_request; | |
| const username = pr.user.login.toLowerCase(); | |
| const repoOwner = "Kritikaadhikarii"; | |
| const isFork = pr.head.repo.full_name !== `${owner}/${repo}`; | |
| // Fetch first page of stargazers (max 100) | |
| const stargazersResponse = await github.rest.activity.listStargazersForRepo({ | |
| owner, | |
| repo, | |
| per_page: 100 | |
| }); | |
| const stargazers = stargazersResponse.data.map(user => user.login.toLowerCase()); | |
| // Check star by membership | |
| const hasStarred = stargazers.includes(username); | |
| let isFollowing = false; | |
| try { | |
| await github.request('GET /users/{username}/following/{target_user}', { | |
| username, | |
| target_user: repoOwner | |
| }); | |
| isFollowing = true; | |
| } catch (e) { isFollowing = false; } | |
| const allOK = isFork && hasStarred && isFollowing; | |
| const msg = allOK | |
| ? `## ✅ All Requirements Met\n@${username}, thanks for following all the contribution requirements.\nWe’ll close this PR soon with the proper tag.` | |
| : `## ⚠️ Verification Failed\nYour PR does not meet the repository’s verification conditions.\nPlease ensure you’ve forked, starred, and followed @${repoOwner}. This PR will now be closed. You can create a new pr after following all of the instructions.`; | |
| await github.rest.issues.createComment({ | |
| owner, repo, issue_number: pr.number, body: msg | |
| }); | |
| if (!allOK) { | |
| await github.rest.pulls.update({ | |
| owner, repo, pull_number: pr.number, state: 'closed' | |
| }); | |
| } else { | |
| await github.rest.issues.addLabels({ | |
| owner, repo, issue_number: pr.number, labels: ['✅ Verified Contributor'] | |
| }); | |
| } |