spamblock
spamblock
Scans your GitHub followers against configurable criteria and blocks the ones that match — cleans up follow-for-follow / star-farming spam accounts (e.g. "GIVE ME STARS TO MY REPOSITORIES AND BACK TO YOUR REPOSITORIES").
GitHub has no concept of a private profile or a follow request to approve — anyone can follow anyone at any time. Blocking is the only lever that actually prevents it (and removes the existing follow relationship), so this tool automates finding who to block instead of doing it by hand.
How it decides
Two independent criteria, configured in src/spamblock/patterns.yml:
bio_patterns:
- "give me stars"
- "follow main"
min_repo_bytes: 500
- Bio pattern — the follower's public bio contains any of the phrases above (case-insensitive substring). Near-zero false-positive risk: these phrases don't show up in a real bio by accident. A missing/nil bio never matches.
- Trivial repos — the follower has at least one non-fork repo, but every one of them has fewer than
min_repo_bytesbytes of recognized source code (via GitHub's languages API — a cheap proxy for "about this many lines", not an exact count). Catches accounts padded with placeholder/empty repos to look active. Set to0to disable. Higher false-positive risk than the bio check (a brand-new or experimental account could trip this legitimately), which is why every report says which criterion fired for each match instead of a bare yes/no.
Add a line to either list, no code changes needed. Pass --config path/to/your.yml to use a different file entirely (e.g. to keep personal patterns out of this repo).
If you run into spam that trips neither signal, that's a new criterion to add to Criteria, not something either heuristic will catch as-is.
Usage
Requires the gh CLI, already authenticated (gh auth status) — spamblock shells out to it for every GitHub API call (pagination and auth are already solved there; no reason to re-solve them).
shards install
crystal build src/spamblock.cr -o bin/spamblock
./bin/spamblock scan # report matches, no action taken
./bin/spamblock block # scan, then block every match
./bin/spamblock scan --user someone # preview someone else's followers
./bin/spamblock scan --report scan.md # also write a Markdown report to disk
Commands
| Command | Effect |
|---|---|
scan |
Scan followers against the criteria and print/report matches. Read-only — safe to run any time. |
block |
Same scan, then blocks every match via gh api -X PUT user/blocks/<login>. |
Options
| Option | Default | Effect |
|---|---|---|
--config PATH |
bundled src/spamblock/patterns.yml |
Use a different criteria file (e.g. to keep personal patterns out of this repo). |
--user LOGIN |
the authenticated gh user |
Whose followers to scan. Preview-only: block still only ever blocks on your own account's list, regardless of this flag — you can only manage your own blocks. |
--report PATH |
none (stdout only) | Also write a Markdown report to PATH with the scanned user, the criteria used, and every flagged login with its reasons (plus block status, once block has actually run). |
-h, --help |
— | Print usage and exit. |
Combine freely, e.g.:
./bin/spamblock block --user someone --config my-patterns.yml --report block.md
scan is always safe to run — it only reads. block calls gh api -X PUT user/blocks/<login> for every match, which affects your own account's block list regardless of --user (you can only manage your own blocks). --report PATH writes a Markdown file with the scanned user, the criteria used, and every flagged login with its reasons (plus block status, once block has actually run) — useful as a record of what happened, or to review before deciding to run block.
Testing
crystal spec
Specs cover the pure logic directly — Criteria (bio/repo matching) and Report (rendering). The gh-API wrapper isn't unit-tested since it needs a live authenticated session — exercise it for real with scan first (read-only) before trusting block.
spamblock
- 0
- 0
- 0
- 0
- 0
- about 1 hour ago
- July 5, 2026
MIT License
Sun, 05 Jul 2026 17:28:06 GMT