discord.ts
@file Wire constants and response builders for the Discord interactions…
Overview
@file Wire constants and response builders for the Discord interactions
protocol. Kept dependency-free so test/ can exercise it without a
Worker runtime.
used by command.ts build.ts context.ts decode-devid.ts forget.ts help-me.ts ihave.ts matrix.ts ping.ts size.ts spec.ts test-request.ts test-result.ts twin.ts verify.ts who-has.ts why.ts followup.ts index.ts modal.ts testRequestContainer.ts
flowchart TD deferredMessage --> jsonResponse
API
Fexport function jsonResponse(body: unknown, status = 200): Response
Serialises an interaction response body. Discord expects exactly this shape back within the 3 second deadline, or a defer followed by a PATCH to the response webhook.
deferredMessage, deferredUpdate, handleApproach, handleExplain, message, modal, modalResponseFexport function message(content: string, opts: { ephemeral?: boolean } = {}): Response
A CHANNEL_MESSAGE_WITH_SOURCE response, **ephemeral unless told otherwise**.
The default is deliberate and is the one place the two halves of this bot
disagreed: triage replies defaulted to ephemeral, compatibility replies to
public. Nothing in the type system can catch that — it is a runtime default
behind ~50 call sites — so it resolves in the fail-safe direction. A reply
that should have been public and came out private is a visible annoyance
somebody reports; a reply that should have been private and came out public
has already been read by the channel. Say { ephemeral: false }, or use
publicMessage, to opt a reply into being visible to everyone.
Mention parsing is suppressed unconditionally: nothing this bot echoes back
should ever be able to ping, since some of what it echoes is user-typed.
onlyUsers is the single sanctioned exception.
componentHandler, handler, handler, handler, handler, handler, handler, handler · calls jsonResponseFexport function publicMessage(content: string): Response
A reply everyone in the channel can see. Exists so that "this is public" is written at the call site rather than inferred from an absent argument, which is what made the two halves' defaults disagree unnoticed.
messageFexport function invokerId(interaction: Interaction): string | null
The Discord user ID that invoked an interaction: member.user.id in a
guild, user.id in a DM. Never a username or display name.
componentHandler, handler, handler, handler, handler, handler, handler, modalHandlerFexport function optionString(interaction: Interaction, name: string, max: number): string | null
A command option's string value, trimmed and capped. Discord enforces its own limits client-side; this Worker does not trust that.
handler, handler, handler, handler, handler, handler, handler, handlerFexport function deferredMessage(opts: { ephemeral?: boolean } = {}): Response
A DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE response: the "thinking…" placeholder for work that will not finish inside the 3 second deadline.
defer, deferRich · calls jsonResponseFexport function modalResponse(modal: { custom_id: string; title: string; components: unknown[]; }): Response
A MODAL response. Must be the immediate response to the interaction — there is no deferring first and opening one after.
handler · calls jsonResponseFexport function deferredUpdate(): Response
A DEFERRED_UPDATE_MESSAGE response: for a MESSAGE_COMPONENT interaction
(a button click) whose eventual edit will not finish inside the 3 second
deadline. Unlike deferredMessage, this carries no placeholder content —
Discord shows no "thinking…" state at all, it just leaves the existing
message as-is until the follow-up edit lands.
deferUpdate · calls jsonResponseFexport function onlyUsers(ids: string[]):
Suppress everything, then re-allow specific user IDs.
The only pings this bot ever sends are to the maintainer, on a no-match or
on request, and the ID comes from configuration rather than from a field
somebody typed. parse: [] still holds, so nothing inside the user's own
text can become a mention.
onModalSubmitFexport function modal(customId: string, title: string, inputs: ModalInput[]): Response
Build and return a modal from plain field descriptions. The ergonomic half
of modalResponse, which takes an already-assembled payload.
handler · calls jsonResponseFexport function modalValue(interaction: Interaction, customId: string, max: number): string | null
One submitted modal field, trimmed and capped. null for absent or empty,
matching optionString so callers treat both the same way.
onModalSubmitUndocumented (1)
newCorrelationId