Skip to content

fix(discord): de-dupe allowed_mentions roles/users to avoid silent 400#5

Merged
poterpan merged 1 commit into
mainfrom
fix/dedupe-allowed-mentions-roles
Jun 8, 2026
Merged

fix(discord): de-dupe allowed_mentions roles/users to avoid silent 400#5
poterpan merged 1 commit into
mainfrom
fix/dedupe-allowed-mentions-roles

Conversation

@poterpan

@poterpan poterpan commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Problem

When two plans share the same Discord role id (e.g. Claude Standard and Claude Premium both mapped to a single @Claude role), the billing-opened notice silently failed to send.

Root cause

sendBillingOpened builds one lines entry per active plan, then maps each line's role_id into allowed_mentions.roles without de-duping:

const roles = lines.map((l) => l.role_id).filter((r): r is string => !!r);
// two plans, one role → ["claude", "claude"]

Discord treats allowed_mentions.roles / .users as unique sets and returns HTTP 400 Invalid Form Body (code 50035) on duplicate snowflakes. createChannelMessage maps any non-2xx response to null (if (!res.ok) return null), so the failure is completely silent — the notice just never appears, no log, no throw.

Affects both the cron auto-open path and admin /發起繳費. The overdue path (sendOverdue) uses users grouped per-member so it's not hit today, but shares the same un-deduped shape.

Fix

De-dupe both arrays with Set:

  • roles in sendBillingOpened (the actual bug)
  • users in sendOverdue (defensive — kills the latent twin)

Per-plan display is unchanged: the content still renders one mention line per plan (the role is still only pinged once).

Tests

  • New regression test: two plans sharing a role → allowed_mentions.roles is ["claude"] (not ["claude","claude"]), and the content still shows the mention on both plan lines.
  • New regression test: duplicate member in overdue list → users de-duped.
  • Full worker suite: 179 passing, typecheck clean.

When two plans share one Discord role (e.g. Claude Standard + Premium both
mapped to @claude), the billing-opened notice built allowed_mentions.roles
with the role id twice. Discord treats roles/users as unique sets and 400s
on duplicate snowflakes; createChannelMessage swallows non-2xx as null, so
the notice silently never sent.

De-dupe both arrays via Set. Also hardens the overdue users array against
the same latent bug. Adds regression tests.
@poterpan poterpan merged commit b51f026 into main Jun 8, 2026
2 checks passed
@poterpan poterpan deleted the fix/dedupe-allowed-mentions-roles branch June 8, 2026 14:43
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