How the bot connects to Discord using the dfx library, handles gateway events, and calls the REST API.
The bot uses dfx (Discord Effects) — an Effect-native Discord library — for all communication with Discord. The @chat/discord package wraps dfx into reusable layers that every feature can consume.
GuildMessages — receive MESSAGE_CREATE / MESSAGE_UPDATE events in guild channels. Required for AutoThreads and NoEmbed.
MessageContent — access the content field of messages sent by other users. Without this intent the content is always an empty string for bots that are not verified.
Guilds — receive guild and channel metadata events (GUILD_CREATE, CHANNEL_CREATE, etc.). Required for the channel cache to stay warm.
DiscordGatewayLayer opens a WebSocket connection to Discord’s gateway using dfx’s DiscordIxLive. It also starts the InteractionsRegistry used by slash commands.
// packages/discord/src/DiscordGateway.tsimport { DiscordIxLive } from "dfx/gateway"import { Layer } from "effect"import { DiscordConfigLayer } from "./DiscordConfig.ts"import { DiscordApplication } from "./DiscordRest.ts"const DiscordLayer = DiscordIxLive.pipe( Layer.provideMerge(NodeHttpClient.layerUndici), Layer.provide(NodeSocket.layerWebSocketConstructor), Layer.provide(DiscordConfigLayer),)export const DiscordGatewayLayer = Layer.merge( DiscordLayer, DiscordApplication.layer,)
Features listen to gateway events via gateway.handleDispatch:
DiscordRESTMemoryLive provides an in-memory rate-limited HTTP client for the Discord REST API. It is exposed through DiscordApplication, which also fetches the bot’s own application object at startup.
Handlers are composed with Ix.builder, which chains them and attaches error handling:
const ix = Ix.builder .add(command) .add(cancel) .add(accept) .catchTagRespond("PermissionsError", (_) => Effect.succeed( Ix.response({ type: Discord.InteractionCallbackTypes.CHANNEL_MESSAGE_WITH_SOURCE, data: { flags: Discord.MessageFlags.Ephemeral, content: `You don't have permission to ${_.action} this ${_.subject}.`, }, }), ), ) .catchAllCause(Effect.logError)yield* registry.register(ix)
Ix.idStartsWith matches buttons whose custom_id starts with a prefix (used for per-user buttons like edit_{userId} and archive_{userId}), while Ix.id matches an exact custom_id.
Messages provides clean, paginated message retrieval for a channel. It handles thread-starter messages, replaces <@userId> mentions with display names, and normalises code-block markdown.