Skip to content

feat(sweep): port reaction sweep from bash to TypeScript - #3

Merged
mortik merged 1 commit into
mainfrom
feat/sweep
May 27, 2026
Merged

feat(sweep): port reaction sweep from bash to TypeScript#3
mortik merged 1 commit into
mainfrom
feat/sweep

Conversation

@mortik

@mortik mortik commented May 26, 2026

Copy link
Copy Markdown
Member

Summary

  • src/sweep.ts — pure runSweep(rest, verification) over a small DiscordRestLike interface; paginates reactors, fetches each member, grants the role when missing. Mirrors the original bash script.
  • src/rest.ts — discord.js adapter for DiscordRestLike (handles the Unknown Member code 10007 by returning null instead of throwing).
  • Wires sweep into events/ready.ts so each verification is reconciled on startup when sweep.on_startup is true.
  • Shares client.rest with sweep in src/index.ts rather than spinning up a second REST instance.
  • .gitignore now excludes config/verifications.yaml so the local dev copy with real IDs stays out of commits.
  • 9 sweep unit tests with a mocked REST (empty, grant, skip, not-in-guild, pagination, exact-multiple page boundary, getMember error, addMemberRole error, mixed outcomes). 21 tests total now.

Implements step 4 of docs/exec-plans/v1.md.

Test plan

  • pnpm test — 21 tests pass (9 new in test/sweep.test.ts)
  • pnpm lint, pnpm typecheck, pnpm build clean
  • Live smoke vs the real verification message: reactors=119, granted=0, alreadyHadRole=92, notInGuild=27, errors=0 — confirms the pipeline is idempotent (no duplicate grants on re-run).

Follow-ups

  • Step 5: reaction add/remove handlers. At that point we'll enable the GuildMembers privileged intent (in the Developer Portal) and the GuildMessageReactions intent, plus the message/channel/reaction partials. Sweep-side revocation can also land then since enumerating role holders requires that intent.
  • Step 6: cron-scheduled sweep.

🤖 Generated with Claude Code

The sweep iterates configured verifications, paginates reactors via
REST, and grants the role to anyone who reacted but doesn't already
hold it. Mirrors the original one-off bash script's grant-only
behavior. Sweep-side revocation needs the privileged GuildMembers
intent (to enumerate role holders) and lands in step 5 alongside
the reaction handlers.

- src/sweep.ts: pure runSweep(rest, verification) with a small
  DiscordRestLike interface for testability
- src/rest.ts: discord.js adapter for that interface
- src/events/ready.ts: invokes the sweep for each verification on
  startup when sweep.on_startup is true
- src/index.ts: shares the discord.js REST client with the sweep
- test/sweep.test.ts: 9 unit tests covering grant/skip/notInGuild/
  errors/pagination/mixed-outcomes

Also gitignores config/verifications.yaml so the local dev copy with
real IDs doesn't get committed.

Verified end-to-end against the live verification message:
reactors=119, granted=0, alreadyHadRole=92, notInGuild=27, errors=0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mortik
mortik merged commit 0e29a3a into main May 27, 2026
1 check passed
@mortik
mortik deleted the feat/sweep branch May 27, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant