From ac94f67797067640b63edc30443871ea43ff61c3 Mon Sep 17 00:00:00 2001 From: Hikari Date: Mon, 9 Mar 2026 20:24:13 -0700 Subject: [PATCH] fix: send webhook milestone notifications silently (#45) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Adds `flags: 4096` (`MessageFlags.SUPPRESS_NOTIFICATIONS`) to the Discord webhook payload in `postMilestoneWebhook` - Milestone announcements (prestige, transcendence, apotheosis) will now appear in the channel without triggering desktop or mobile push notifications - Defines the magic number as a documented `suppressNotifications` constant for self-documentation - Updates the webhook test to assert `flags: 4096` is present in the outgoing payload Closes #41 ## Test plan - [ ] Lint passes: `pnpm lint` - [ ] Build passes: `pnpm build` - [ ] Tests pass with 100% coverage: `pnpm test` - [ ] Trigger a prestige/transcendence/apotheosis in-game and verify the Discord webhook message arrives without pinging anyone ✨ This issue was created with help from Hikari~ 🌸 Reviewed-on: https://git.nhcarrigan.com/nhcarrigan/elysium/pulls/45 Co-authored-by: Hikari Co-committed-by: Hikari --- apps/api/src/services/webhook.ts | 11 ++++++++++- apps/api/test/services/webhook.spec.ts | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/api/src/services/webhook.ts b/apps/api/src/services/webhook.ts index 72ddf1f..b7ef8cd 100644 --- a/apps/api/src/services/webhook.ts +++ b/apps/api/src/services/webhook.ts @@ -9,6 +9,12 @@ import { logger } from "./logger.js"; const discordApi = "https://discord.com/api/v10"; +/** + * Discord MessageFlags.SUPPRESS_NOTIFICATIONS — messages are delivered without + * triggering desktop or mobile push notifications. + */ +const suppressNotifications = 4096; + /** * Grants the apotheosis Discord role to the given player if configured. * Fails silently so role grant errors do not affect the game action. @@ -85,7 +91,10 @@ const postMilestoneWebhook = async( try { await fetch(webhookUrl, { - body: JSON.stringify({ content }), + body: JSON.stringify({ + content: content, + flags: suppressNotifications, + }), headers: { "Content-Type": "application/json" }, method: "POST", }); diff --git a/apps/api/test/services/webhook.spec.ts b/apps/api/test/services/webhook.spec.ts index 28680b0..a68a90a 100644 --- a/apps/api/test/services/webhook.spec.ts +++ b/apps/api/test/services/webhook.spec.ts @@ -97,9 +97,10 @@ describe("webhook service", () => { await postMilestoneWebhook("user123", "prestige", counts); const [url, options] = mockFetch.mock.calls[0] as [string, RequestInit]; expect(url).toBe("https://discord.com/webhook/abc"); - const body = JSON.parse(options.body as string) as { content: string }; + const body = JSON.parse(options.body as string) as { content: string; flags: number }; expect(body.content).toContain("<@user123>"); expect(body.content).toContain("prestiged"); + expect(body.flags).toBe(4096); }); it("posts transcendence message correctly", async () => {