@molecule/api-channel-discord
Provider bond · channel · API (Node) · v1.0.2 · Apache-2.0
Discord channel bond — implements @molecule/api-channel for sendMessage, ed25519 webhook signature verification, and inbound interaction parsing.
npm install @molecule/api-channel-discordnpm · Source on GitHub · Implements @molecule/api-channel
How it works
@molecule/api-channel-discord is a provider bond on the API (Node) side: it implements the channel core interface (@molecule/api-channel) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-channel'
import { provider } from '@molecule/api-channel-discord'
setProvider('discord', provider)Works with: @molecule/api-channel, @molecule/api-secrets
Secrets: CHANNEL_DISCORD_BOT_TOKEN, CHANNEL_DISCORD_PUBLIC_KEY
Reference
Discord channel bond for molecule.dev.
Implements the ChannelProvider interface defined by
@molecule/api-channel, posting outbound messages via Discord's REST
API and verifying inbound interaction webhooks against the
application's ed25519 public key.
Quick Start
import { setProvider } from '@molecule/api-channel'
import { provider } from '@molecule/api-channel-discord'
setProvider('discord', provider)
Type
provider
Installation
npm install @molecule/api-channel-discord @molecule/api-channel @molecule/api-secrets discord.js
API
Interfaces
DiscordConfig
Configuration for the Discord channel provider.
Discord uses two distinct credentials:
botToken— used as theAuthorization: Bot <token>header for REST calls (sending messages, fetching channels, etc.). Required to send messages.publicKey— the application's hex-encoded ed25519 public key, used to verify the signature on inbound interaction webhook requests. Required to verify webhooks.
Either credential may be omitted at construction time and supplied via
environment variables (CHANNEL_DISCORD_BOT_TOKEN,
CHANNEL_DISCORD_PUBLIC_KEY).
interface DiscordConfig {
/**
* Bot token for the Discord application. Used as the `Bot <token>`
* authentication header on REST calls. Defaults to the
* `CHANNEL_DISCORD_BOT_TOKEN` environment variable.
*/
botToken?: string
/**
* Application public key (hex-encoded ed25519) used to verify inbound
* interaction webhook signatures. Defaults to the
* `CHANNEL_DISCORD_PUBLIC_KEY` environment variable.
*/
publicKey?: string
/**
* Optional override for the Discord REST API base URL. Defaults to
* `https://discord.com/api/v10`. Primarily useful for testing.
*/
apiBaseUrl?: string
/**
* Optional REST client. If provided, the bond delegates message sends to
* it (e.g. an instance of `discord.js`'s `REST`). When omitted, the bond
* falls back to a built-in `fetch` implementation. Primarily useful for
* tests that need to mock the `discord.js` client without having a live
* dependency.
*/
rest?: DiscordRestLike
/**
* Optional request timeout for built-in REST calls in milliseconds.
* Defaults to `10000`. Ignored when `rest` is supplied.
*/
timeoutMs?: number
}
DiscordRestLike
Minimal REST-client shape the provider depends on. Compatible with the
REST class from discord.js (new REST().setToken(token).post(...)).
interface DiscordRestLike {
/**
* Issues a POST request against the Discord REST API at the given route.
*
* @param route - REST route relative to the Discord API base (e.g.
* `'/channels/123/messages'`).
* @param options - Request options including a JSON-serialisable body.
* @returns The parsed JSON response.
*/
post(route: string, options: { body: unknown }): Promise<unknown>
}
ProcessEnv
Environment variables consumed by the Discord channel provider.
interface ProcessEnv {
/** Bot token used for REST authentication. */
CHANNEL_DISCORD_BOT_TOKEN: string
/** Application public key (hex-encoded) used for webhook verification. */
CHANNEL_DISCORD_PUBLIC_KEY: string
}
Types
DiscordInteractionType
Discord interaction types relevant to inbound webhook parsing.
Mirrors the subset of Discord's InteractionType enum used by this
bond:
1—PING(handshake; not converted to an InboundMessage).2—APPLICATION_COMMAND(slash command).3—MESSAGE_COMPONENT(button click, select-menu, etc.).
Any other types (modal submit, autocomplete, …) are passed through as the original payload and treated as an empty inbound message.
type DiscordInteractionType = 1 | 2 | 3 | number
Classes
DiscordChannelProvider
Concrete Discord implementation of ChannelProvider.
Functions
createProvider(config)
Convenience factory for DiscordChannelProvider.
function createProvider(config?: DiscordConfig): DiscordChannelProvider
config— Optional configuration.
Returns: A configured Discord channel provider.
Constants
channelDiscordSecretDefinitions
Secret definitions required by the Discord channel bond.
const channelDiscordSecretDefinitions: SecretDefinition[]
provider
Lazily-instantiated, default-configured Discord channel provider.
The proxy defers construction until first use so importing this module has no side effects (e.g. consuming env vars at import time).
const provider: ChannelProvider
Core Interface
Implements @molecule/api-channel interface.
Injection Notes
Requirements
Peer dependencies:
@molecule/api-channel^1.0.1discord.js^14.0.0@molecule/api-secrets^1.0.1
Environment Variables
CHANNEL_DISCORD_BOT_TOKEN(required) — Discord bot token- Setup: Create an application → Bot → Reset Token, then copy the bot token.
- Get it here: https://discord.com/developers/applications
CHANNEL_DISCORD_PUBLIC_KEY(required) — Discord public key- Setup: Your application → General Information → Public Key (verifies interaction signatures).
- Get it here: https://discord.com/developers/applications
Runtime Dependencies
@molecule/api-channel@molecule/api-secretsdiscord.js
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- Each channel-notifying flow the app defines (a Slack/Discord alert on
a new order, a status-change message) actually produces a message. The
sandbox CAPTURES channel messages instead of sending — read them with the
read_activitytool (filter type 'channel'); never mock the flow or modify production code to expose the message. - The captured message targets the configured channel/provider name and
carries the app's real content (readable text, no
undefinedplaceholders, no secrets). - A failed send (unbonded or misconfigured provider) is visible in logs/UI — never silently swallowed.