Assistant UX: Ollama model warmup, human day phrases, app-formatted times #92
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: Moderate first-time spam links | |
| # Redacts links posted by first-time contributors to any host outside a small | |
| # allowlist (GitHub, image/video hosts), then pings the maintainer. Allowlist | |
| # beats a blocklist here: an unknown throwaway host is flagged by default, so the | |
| # "here's a fix in a zip" malware pattern (issue #221) can't evade it with a new | |
| # domain or an extension-less link. See issue #222. | |
| on: | |
| issue_comment: | |
| types: [created, edited] | |
| issues: | |
| types: [opened, edited] | |
| permissions: | |
| issues: write | |
| jobs: | |
| moderate: | |
| runs-on: ubuntu-latest | |
| # Skip anything a bot posted, including this workflow's own edits/comments, | |
| # so it cannot loop on the edits it makes. | |
| if: github.event.sender.type != 'Bot' | |
| steps: | |
| - name: Redact first-time download links | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const REDACTION = '«link removed by spam moderation»'; | |
| const MAINTAINER = 'pliablepixels'; | |
| const UNTRUSTED = new Set(['FIRST_TIME_CONTRIBUTOR', 'FIRST_TIMER', 'NONE', 'MANNEQUIN']); | |
| // Hosts a first-time contributor may legitimately link. Suffix-matched, | |
| // so 'github.com' also covers gist.github.com, raw.githubusercontent.com, | |
| // etc. Deliberately excludes text-paste and file-locker hosts (pastebin, | |
| // 0x0.st, mega, dropbox, drive.google, ...): those are the payload | |
| // channel these bots use, and unknown hosts are flagged by default. | |
| const HOST_ALLOWLIST = [ | |
| 'github.com', 'githubusercontent.com', 'github.io', | |
| 'imgur.com', | |
| 'youtube.com', 'youtu.be', 'streamable.com', | |
| 'zoneminder.com', | |
| ]; | |
| const hostOf = (url) => { | |
| try { return new URL(url).hostname.toLowerCase().replace(/^www\./, ''); } | |
| catch { return ''; } | |
| }; | |
| const isAllowed = (host) => | |
| !!host && HOST_ALLOWLIST.some((h) => host === h || host.endsWith('.' + h)); | |
| // Any http(s) link whose host is not on the allowlist is flaggable. | |
| const findFlaggable = (body) => { | |
| if (!body || body.includes(REDACTION)) return []; | |
| const hits = []; | |
| const seen = new Set(); | |
| const md = /\[([^\]]*)\]\(\s*(https?:\/\/[^\s)]+)\s*\)/gi; | |
| let m; | |
| while ((m = md.exec(body)) !== null) { | |
| const [raw, label, url] = m; | |
| if (!isAllowed(hostOf(url))) { hits.push({ raw, url, label }); seen.add(url); } | |
| } | |
| const bare = /(https?:\/\/[^\s<>()\[\]]+)/gi; | |
| while ((m = bare.exec(body)) !== null) { | |
| const url = m[1].replace(/[)\].,'"]+$/, ''); | |
| if (seen.has(url)) continue; | |
| if (!isAllowed(hostOf(url))) hits.push({ raw: m[1], url, label: '' }); | |
| } | |
| return hits; | |
| }; | |
| const isComment = !!context.payload.comment; | |
| const target = isComment ? context.payload.comment : context.payload.issue; | |
| if (!target) { core.info('No comment/issue in payload.'); return; } | |
| const association = target.author_association || ''; | |
| if (!UNTRUSTED.has(association)) { | |
| core.info(`Skip: author_association=${association} is trusted.`); | |
| return; | |
| } | |
| const body = target.body || ''; | |
| const hits = findFlaggable(body); | |
| if (hits.length === 0) { core.info('No flaggable download links.'); return; } | |
| let newBody = body; | |
| for (const h of hits) newBody = newBody.split(h.raw).join(REDACTION); | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const issueNumber = context.payload.issue.number; | |
| const author = target.user.login; | |
| if (isComment) { | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: target.id, body: newBody }); | |
| } else { | |
| await github.rest.issues.update({ owner, repo, issue_number: issueNumber, body: newBody }); | |
| } | |
| const where = isComment ? `comment (${target.html_url})` : 'issue body'; | |
| const list = hits | |
| .map((h) => `- \`${h.label || '(bare link)'}\` → ${hostOf(h.url) || h.url}`) | |
| .join('\n'); | |
| const historyOf = isComment ? "comment's" : "issue's"; | |
| const message = [ | |
| `Hi @${author}, links from new accounts are automatically held for review. The following off-site link was removed from your ${isComment ? 'comment' : 'issue'} for now; a maintainer will restore it shortly if it is legitimate:`, | |
| '', | |
| list, | |
| '', | |
| `Please do not download or run anything from links posted by new accounts until a maintainer confirms them.`, | |
| '', | |
| `@${MAINTAINER} first-time contributor (@${author}, \`${association}\`) posted an off-allowlist link in the ${where}. It stays in the ${historyOf} edit history, so restore it if this is a false positive; hide/delete and report the account if it is spam.`, | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ owner, repo, issue_number: issueNumber, body: message }); | |
| core.info(`Redacted ${hits.length} link(s) from ${where} by @${author}.`); |