Install
Usage
- Buttons, select menus, polls, reactions and edits are native;
ctx.stream()edits one message as the text grows. - The bot keeps one reaction per message, like WhatsApp/Telegram: a new
ctx.react(emoji)replaces its previous one andctx.react('')removes it. Discord deletes reactions by emoji, so the provider remembers the emoji it set (in memory,reactionMemory, default 1000 messages); an unknown one (set before a restart, or evicted) rejects withUnsupportedContentError. - Interactions have a hard acknowledgement deadline
(
interactionsRequireAck), so the core acknowledges each one exactly once for you: a deferred ack before the handler’s first send, shortly before the ~3 s deadline, or when the handler ends, whichever comes first. Callctx.acknowledge('text')before replying to answer with an immediate text instead; once the automatic ack went out,ctx.acknowledge('text')sendstextas a normal reply andctx.acknowledge()does nothing. ctx.interaction.idis the bare interaction id: its token stays inside the provider. As a backstop, the provider also defers any interaction still unanswered afterautoAcknowledgeMs(default 2000 ms;falseturns it off). A latectx.acknowledge('text')after the ack goes out as a followup, withappIdor else the application id Discord sends on READY and on each interaction; if none is known the call rejects withProviderErrorinstead of dropping the text.- More than 25 buttons (5 rows of 5), 25 select-menu options or 10 poll
answers reject with
UnsupportedContentErrorinstead of dropping options; text over 2000 characters is split into several messages. - Each REST call times out after
requestTimeoutMs(60 s by default); file uploads afteruploadTimeoutMs(5 min). - System messages (member joins, pins, boosts…) and messages from bots are
not routed. A message with no text and no file arrives as
sticker(with the sticker image as media) orunknown(embeds only, or the Message Content intent is off), never as an empty text. - Every message the provider sends carries
allowed_mentions(default{ parse: ['users'] }, optionallowedMentions), so echoed text cannot ping@everyone,@hereor a role;SendOptions.mentions,platformData.allowed_mentionsandnativepayloads override it. A quoted reply still pings the replied author (replied_user: true; setallowedMentions.replied_user: falseto reply silently). - Ids go into REST URLs percent-encoded, and an empty,
.or..id rejects withProviderError: a crafted id never reaches another route. Native poll votes are not delivered as events. - A forwarded message carries the forwarded text and media (from its
snapshot), not a quoted reply; one with nothing readable arrives as
unknown. A native poll arrives astype: 'poll'with its question as body. - Edited messages (
MESSAGE_UPDATEwithedited_timestamp) arrive as messages with the new text and the originalref.id;isEditedMessage(ctx)from@botmesh/discordtells them apart andeditedMessages: 'ignore'drops them. Other updates (embed unfurls, pins, flags) are not routed: the provider delivers an edit once peredited_timestamp(it remembers the last one for the 1000 most recently edited messages), so pinning an edited message does not re-deliver it.
Next steps
- Channels: choosing a channel, the capability matrix and the cross-channel contract.
- Streaming replies: one message edited as the text grows.
- Package README