Collaborative Launch 1786032000 (Thursday August 6, 2026 16:00 UTC) #6
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: Aggregate Collaborative Hashes | |
| on: | |
| issue_comment: | |
| types: [created, edited, deleted] | |
| issues: | |
| types: [opened, edited] | |
| workflow_dispatch: | |
| inputs: | |
| issue_number: | |
| description: Issue number to refresh | |
| required: true | |
| type: string | |
| concurrency: | |
| group: hash-agg-${{ github.event.issue.number || github.event.inputs.issue_number }} | |
| cancel-in-progress: true | |
| jobs: | |
| update: | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| startsWith(github.event.issue.title, 'Collaborative Launch') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| steps: | |
| - uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const MAGIC = '<!-- hash-aggregator-id: ae7c2bfe203c2f16d6574aae0e59e71d -->'; | |
| const SKIP = '<!--meta-tag:bot-skip-->'; | |
| const BOT = 'github-actions[bot]'; | |
| const { owner, repo } = context.repo; | |
| // DrahtBot-style: ignore the bot's own comments (avoids edit loops) | |
| if (context.eventName === 'issue_comment' && | |
| context.payload.comment.user.login === BOT) { | |
| return; | |
| } | |
| function extractHash(line) { | |
| if (line.trimStart().startsWith('>')) return null; | |
| // Strip backticks/bold/italic wrappers; keyword is case-insensitive | |
| const s = line.replace(/[`*_]+/g, '').trim(); | |
| const m = s.match(/^hash:\s*([0-9a-fA-F]{64})\s*$/i); | |
| return m ? m[1].toLowerCase() : null; | |
| } | |
| function userLink(login) { | |
| return `[${login}](https://github.com/${login})`; | |
| } | |
| let issue_number, title, state; | |
| if (context.eventName === 'workflow_dispatch') { | |
| issue_number = Number(context.payload.inputs.issue_number); | |
| const issue = (await github.rest.issues.get({ owner, repo, issue_number })).data; | |
| title = issue.title; | |
| state = issue.state; | |
| } else { | |
| issue_number = context.issue.number; | |
| title = context.payload.issue.title; | |
| state = context.payload.issue.state; | |
| } | |
| if (!title.startsWith('Collaborative Launch')) return; | |
| if (state !== 'open') return; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, repo, issue_number, per_page: 100, | |
| }); | |
| // Per user: set of distinct hashes across all their comments | |
| const userHashes = new Map(); | |
| for (const c of comments) { | |
| const body = c.body || ''; | |
| if (body.includes(MAGIC) || body.includes(SKIP)) continue; | |
| const login = c.user.login; | |
| if (!userHashes.has(login)) userHashes.set(login, new Set()); | |
| for (const line of body.split('\n')) { | |
| const hash = extractHash(line); | |
| if (hash) userHashes.get(login).add(hash); | |
| } | |
| } | |
| const byHash = new Map(); | |
| for (const [user, hashes] of userHashes) { | |
| for (const hash of hashes) { | |
| if (!byHash.has(hash)) byHash.set(hash, []); | |
| byHash.get(hash).push(user); | |
| } | |
| } | |
| const rows = [...byHash.entries()] | |
| .map(([hash, users]) => ({ hash, users: users.sort() })) | |
| .sort((a, b) => b.users.length - a.users.length || a.hash.localeCompare(b.hash)); | |
| const n = [...userHashes.values()].filter(s => s.size > 0).length; | |
| let table = '| hash | users | share of matches |\n| --- | --- | --- |\n'; | |
| if (!rows.length) { | |
| table += '| _none_ | | |\n'; | |
| } else { | |
| for (const r of rows) { | |
| table += `| \`${r.hash}\` | ${r.users.map(userLink).join(', ')} | ${r.users.length}/${n} |\n`; | |
| } | |
| } | |
| const body = [ | |
| MAGIC, | |
| '### Collaborative Launch Hashes', | |
| '', | |
| table, | |
| '', | |
| 'Report with one line `HASH: <sha256>` per result.', | |
| 'If a comment should be ignored by this table, copy-paste <code><!--meta-tag:bot-skip--></code> into it.', | |
| '', | |
| ].join('\n'); | |
| // Only treat the summary as magic. Skip API write if unchanged. | |
| const magic = comments.find(c => | |
| c.user.login === BOT && (c.body || '').includes(MAGIC)); | |
| if (magic) { | |
| if (magic.body === body) return; | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: magic.id, body }); | |
| } else { | |
| await github.rest.issues.createComment({ owner, repo, issue_number, body }); | |
| } |