@chat-adapter/discord 4.16.1 → 4.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +14 -16
- package/dist/index.js +34 -45
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CardElement, BaseFormatConverter, AdapterPostableMessage, Root, Adapter,
|
|
1
|
+
import { Logger, CardElement, BaseFormatConverter, AdapterPostableMessage, Root, Adapter, ChatInstance, WebhookOptions, RawMessage, EmojiValue, FetchOptions, FetchResult, ThreadInfo, Message, FormattedContent, ListThreadsOptions, ListThreadsResult, ChannelInfo } from 'chat';
|
|
2
2
|
import { ButtonStyle, APIEmbed } from 'discord-api-types/v10';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -9,14 +9,18 @@ import { ButtonStyle, APIEmbed } from 'discord-api-types/v10';
|
|
|
9
9
|
* Discord adapter configuration.
|
|
10
10
|
*/
|
|
11
11
|
interface DiscordAdapterConfig {
|
|
12
|
-
/** Discord application ID */
|
|
13
|
-
applicationId
|
|
14
|
-
/** Discord bot token */
|
|
15
|
-
botToken
|
|
16
|
-
/**
|
|
12
|
+
/** Discord application ID. Defaults to DISCORD_APPLICATION_ID env var. */
|
|
13
|
+
applicationId?: string;
|
|
14
|
+
/** Discord bot token. Defaults to DISCORD_BOT_TOKEN env var. */
|
|
15
|
+
botToken?: string;
|
|
16
|
+
/** Logger instance for error reporting. Defaults to ConsoleLogger. */
|
|
17
|
+
logger?: Logger;
|
|
18
|
+
/** Role IDs that should trigger mention handlers (in addition to direct user mentions). Defaults to DISCORD_MENTION_ROLE_IDS env var (comma-separated). */
|
|
17
19
|
mentionRoleIds?: string[];
|
|
18
|
-
/** Discord application public key for webhook signature verification */
|
|
19
|
-
publicKey
|
|
20
|
+
/** Discord application public key for webhook signature verification. Defaults to DISCORD_PUBLIC_KEY env var. */
|
|
21
|
+
publicKey?: string;
|
|
22
|
+
/** Override bot username (optional) */
|
|
23
|
+
userName?: string;
|
|
20
24
|
}
|
|
21
25
|
/**
|
|
22
26
|
* Discord thread ID components.
|
|
@@ -136,10 +140,7 @@ declare class DiscordAdapter implements Adapter<DiscordThreadId, unknown> {
|
|
|
136
140
|
private readonly requestContext;
|
|
137
141
|
private readonly threadParentCache;
|
|
138
142
|
private static readonly THREAD_PARENT_CACHE_TTL;
|
|
139
|
-
constructor(config
|
|
140
|
-
logger: Logger;
|
|
141
|
-
userName?: string;
|
|
142
|
-
});
|
|
143
|
+
constructor(config?: DiscordAdapterConfig);
|
|
143
144
|
initialize(chat: ChatInstance): Promise<void>;
|
|
144
145
|
/**
|
|
145
146
|
* Handle incoming Discord webhook (HTTP Interactions or forwarded Gateway events).
|
|
@@ -341,9 +342,6 @@ declare class DiscordAdapter implements Adapter<DiscordThreadId, unknown> {
|
|
|
341
342
|
/**
|
|
342
343
|
* Create a Discord adapter instance.
|
|
343
344
|
*/
|
|
344
|
-
declare function createDiscordAdapter(config?:
|
|
345
|
-
logger: Logger;
|
|
346
|
-
userName?: string;
|
|
347
|
-
}>): DiscordAdapter;
|
|
345
|
+
declare function createDiscordAdapter(config?: DiscordAdapterConfig): DiscordAdapter;
|
|
348
346
|
|
|
349
347
|
export { DiscordAdapter, type DiscordAdapterConfig, DiscordFormatConverter, DiscordFormatConverter as DiscordMarkdownConverter, type DiscordThreadId, cardToDiscordPayload, cardToFallbackText, createDiscordAdapter };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
GatewayIntentBits,
|
|
21
21
|
Partials
|
|
22
22
|
} from "discord.js";
|
|
23
|
+
import { MessageType } from "discord-api-types/v9";
|
|
23
24
|
import {
|
|
24
25
|
ChannelType,
|
|
25
26
|
InteractionType
|
|
@@ -373,13 +374,34 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
373
374
|
requestContext = new AsyncLocalStorage();
|
|
374
375
|
threadParentCache = /* @__PURE__ */ new Map();
|
|
375
376
|
static THREAD_PARENT_CACHE_TTL = 5 * 60 * 1e3;
|
|
376
|
-
constructor(config) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
377
|
+
constructor(config = {}) {
|
|
378
|
+
const botToken = config.botToken ?? process.env.DISCORD_BOT_TOKEN;
|
|
379
|
+
if (!botToken) {
|
|
380
|
+
throw new ValidationError(
|
|
381
|
+
"discord",
|
|
382
|
+
"botToken is required. Set DISCORD_BOT_TOKEN or provide it in config."
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const publicKey = config.publicKey ?? process.env.DISCORD_PUBLIC_KEY;
|
|
386
|
+
if (!publicKey) {
|
|
387
|
+
throw new ValidationError(
|
|
388
|
+
"discord",
|
|
389
|
+
"publicKey is required. Set DISCORD_PUBLIC_KEY or provide it in config."
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
const applicationId = config.applicationId ?? process.env.DISCORD_APPLICATION_ID;
|
|
393
|
+
if (!applicationId) {
|
|
394
|
+
throw new ValidationError(
|
|
395
|
+
"discord",
|
|
396
|
+
"applicationId is required. Set DISCORD_APPLICATION_ID or provide it in config."
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
this.botToken = botToken;
|
|
400
|
+
this.publicKey = publicKey.trim().toLowerCase();
|
|
401
|
+
this.applicationId = applicationId;
|
|
402
|
+
this.mentionRoleIds = config.mentionRoleIds ?? (process.env.DISCORD_MENTION_ROLE_IDS ? process.env.DISCORD_MENTION_ROLE_IDS.split(",").map((id) => id.trim()) : []);
|
|
403
|
+
this.botUserId = applicationId;
|
|
404
|
+
this.logger = config.logger ?? new ConsoleLogger("info").child("discord");
|
|
383
405
|
this.userName = config.userName ?? "bot";
|
|
384
406
|
if (!HEX_64_PATTERN.test(this.publicKey)) {
|
|
385
407
|
this.logger.error("Invalid Discord public key format", {
|
|
@@ -390,11 +412,7 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
390
412
|
}
|
|
391
413
|
async initialize(chat) {
|
|
392
414
|
this.chat = chat;
|
|
393
|
-
this.logger.info("Discord adapter initialized"
|
|
394
|
-
applicationId: this.applicationId,
|
|
395
|
-
// Log full public key for debugging - it's public, not secret
|
|
396
|
-
publicKey: this.publicKey
|
|
397
|
-
});
|
|
415
|
+
this.logger.info("Discord adapter initialized");
|
|
398
416
|
}
|
|
399
417
|
/**
|
|
400
418
|
* Handle incoming Discord webhook (HTTP Interactions or forwarded Gateway events).
|
|
@@ -1408,7 +1426,8 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
1408
1426
|
/**
|
|
1409
1427
|
* Parse a Discord API message into normalized format.
|
|
1410
1428
|
*/
|
|
1411
|
-
parseDiscordMessage(
|
|
1429
|
+
parseDiscordMessage(raw, threadId) {
|
|
1430
|
+
const msg = raw.type === MessageType.ThreadStarterMessage && raw.referenced_message ? raw.referenced_message : raw;
|
|
1412
1431
|
const author = msg.author;
|
|
1413
1432
|
const isBot = author.bot ?? false;
|
|
1414
1433
|
const isMe = author.id === this.botUserId;
|
|
@@ -1417,7 +1436,7 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
1417
1436
|
threadId,
|
|
1418
1437
|
text: this.formatConverter.extractPlainText(msg.content),
|
|
1419
1438
|
formatted: this.formatConverter.toAst(msg.content),
|
|
1420
|
-
raw
|
|
1439
|
+
raw,
|
|
1421
1440
|
author: {
|
|
1422
1441
|
userId: author.id,
|
|
1423
1442
|
userName: author.username,
|
|
@@ -2167,37 +2186,7 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
2167
2186
|
}
|
|
2168
2187
|
};
|
|
2169
2188
|
function createDiscordAdapter(config) {
|
|
2170
|
-
|
|
2171
|
-
if (!botToken) {
|
|
2172
|
-
throw new ValidationError(
|
|
2173
|
-
"discord",
|
|
2174
|
-
"botToken is required. Set DISCORD_BOT_TOKEN or provide it in config."
|
|
2175
|
-
);
|
|
2176
|
-
}
|
|
2177
|
-
const publicKey = config?.publicKey ?? process.env.DISCORD_PUBLIC_KEY;
|
|
2178
|
-
if (!publicKey) {
|
|
2179
|
-
throw new ValidationError(
|
|
2180
|
-
"discord",
|
|
2181
|
-
"publicKey is required. Set DISCORD_PUBLIC_KEY or provide it in config."
|
|
2182
|
-
);
|
|
2183
|
-
}
|
|
2184
|
-
const applicationId = config?.applicationId ?? process.env.DISCORD_APPLICATION_ID;
|
|
2185
|
-
if (!applicationId) {
|
|
2186
|
-
throw new ValidationError(
|
|
2187
|
-
"discord",
|
|
2188
|
-
"applicationId is required. Set DISCORD_APPLICATION_ID or provide it in config."
|
|
2189
|
-
);
|
|
2190
|
-
}
|
|
2191
|
-
const mentionRoleIds = config?.mentionRoleIds ?? (process.env.DISCORD_MENTION_ROLE_IDS ? process.env.DISCORD_MENTION_ROLE_IDS.split(",").map((id) => id.trim()) : void 0);
|
|
2192
|
-
const resolved = {
|
|
2193
|
-
botToken,
|
|
2194
|
-
publicKey,
|
|
2195
|
-
applicationId,
|
|
2196
|
-
mentionRoleIds,
|
|
2197
|
-
logger: config?.logger ?? new ConsoleLogger("info").child("discord"),
|
|
2198
|
-
userName: config?.userName
|
|
2199
|
-
};
|
|
2200
|
-
return new DiscordAdapter(resolved);
|
|
2189
|
+
return new DiscordAdapter(config ?? {});
|
|
2201
2190
|
}
|
|
2202
2191
|
export {
|
|
2203
2192
|
DiscordAdapter,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts","../src/types.ts"],"sourcesContent":["/**\n * Discord adapter for chat-sdk.\n *\n * Uses Discord's HTTP Interactions API (not Gateway WebSocket) for\n * serverless compatibility. Webhook signature verification uses Ed25519.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n extractCard,\n extractFiles,\n NetworkError,\n toBuffer,\n ValidationError,\n} from \"@chat-adapter/shared\";\nimport type {\n ActionEvent,\n Adapter,\n AdapterPostableMessage,\n ChannelInfo,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n ListThreadsOptions,\n ListThreadsResult,\n Logger,\n RawMessage,\n ThreadInfo,\n ThreadSummary,\n WebhookOptions,\n} from \"chat\";\nimport {\n ConsoleLogger,\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n getEmoji,\n Message,\n} from \"chat\";\nimport {\n Client,\n type Message as DiscordJsMessage,\n Events,\n GatewayIntentBits,\n Partials,\n} from \"discord.js\";\nimport {\n type APIEmbed,\n type APIMessage,\n ChannelType,\n InteractionType,\n} from \"discord-api-types/v10\";\nimport {\n InteractionResponseType as DiscordInteractionResponseType,\n verifyKey,\n} from \"discord-interactions\";\nimport { cardToDiscordPayload, cardToFallbackText } from \"./cards\";\nimport { DiscordFormatConverter } from \"./markdown\";\nimport {\n type DiscordActionRow,\n type DiscordAdapterConfig,\n type DiscordCommandOption,\n type DiscordForwardedEvent,\n type DiscordGatewayEventType,\n type DiscordGatewayMessageData,\n type DiscordGatewayReactionData,\n type DiscordInteraction,\n type DiscordInteractionResponse,\n type DiscordMessagePayload,\n type DiscordRequestContext,\n type DiscordSlashCommandContext,\n type DiscordThreadId,\n InteractionResponseType,\n} from \"./types\";\n\nconst DISCORD_API_BASE = \"https://discord.com/api/v10\";\nconst DISCORD_MAX_CONTENT_LENGTH = 2000;\nconst HEX_64_PATTERN = /^[0-9a-f]{64}$/;\nconst HEX_PATTERN = /^[0-9a-f]+$/;\n\nexport class DiscordAdapter implements Adapter<DiscordThreadId, unknown> {\n readonly name = \"discord\";\n readonly userName: string;\n readonly botUserId?: string;\n\n private readonly botToken: string;\n private readonly publicKey: string;\n private readonly applicationId: string;\n private readonly mentionRoleIds: string[];\n private chat: ChatInstance | null = null;\n private readonly logger: Logger;\n private readonly formatConverter = new DiscordFormatConverter();\n private readonly requestContext =\n new AsyncLocalStorage<DiscordRequestContext>();\n private readonly threadParentCache = new Map<\n string,\n { parentId: string; expiresAt: number }\n >();\n private static readonly THREAD_PARENT_CACHE_TTL = 5 * 60 * 1000;\n\n constructor(\n config: DiscordAdapterConfig & { logger: Logger; userName?: string }\n ) {\n this.botToken = config.botToken;\n this.publicKey = config.publicKey.trim().toLowerCase();\n this.applicationId = config.applicationId;\n this.mentionRoleIds = config.mentionRoleIds ?? [];\n this.botUserId = config.applicationId; // Discord app ID is the bot's user ID\n this.logger = config.logger;\n this.userName = config.userName ?? \"bot\";\n\n // Validate public key format\n if (!HEX_64_PATTERN.test(this.publicKey)) {\n this.logger.error(\"Invalid Discord public key format\", {\n length: this.publicKey.length,\n isHex: HEX_PATTERN.test(this.publicKey),\n });\n }\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n this.logger.info(\"Discord adapter initialized\", {\n applicationId: this.applicationId,\n // Log full public key for debugging - it's public, not secret\n publicKey: this.publicKey,\n });\n }\n\n /**\n * Handle incoming Discord webhook (HTTP Interactions or forwarded Gateway events).\n */\n async handleWebhook(\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n // Get raw body bytes for signature verification\n const bodyBuffer = await request.arrayBuffer();\n const bodyBytes = new Uint8Array(bodyBuffer);\n const body = new TextDecoder().decode(bodyBytes);\n\n // Check if this is a forwarded Gateway event (uses bot token for auth)\n const gatewayToken = request.headers.get(\"x-discord-gateway-token\");\n if (gatewayToken) {\n if (gatewayToken !== this.botToken) {\n this.logger.warn(\"Invalid gateway token\");\n return new Response(\"Invalid gateway token\", { status: 401 });\n }\n this.logger.info(\"Discord forwarded Gateway event received\");\n try {\n const event = JSON.parse(body) as DiscordForwardedEvent;\n return this.handleForwardedGatewayEvent(event, options);\n } catch {\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n }\n\n this.logger.info(\"Discord webhook received\", {\n bodyLength: body.length,\n bodyBytesLength: bodyBytes.length,\n hasSignature: !!request.headers.get(\"x-signature-ed25519\"),\n hasTimestamp: !!request.headers.get(\"x-signature-timestamp\"),\n });\n\n // Verify Ed25519 signature using raw bytes\n const signature = request.headers.get(\"x-signature-ed25519\");\n const timestamp = request.headers.get(\"x-signature-timestamp\");\n\n const signatureValid = await this.verifySignature(\n bodyBytes,\n signature,\n timestamp\n );\n if (!signatureValid) {\n this.logger.warn(\"Discord signature verification failed, returning 401\");\n return new Response(\"Invalid signature\", { status: 401 });\n }\n this.logger.info(\"Discord signature verification passed\");\n\n let interaction: DiscordInteraction;\n try {\n interaction = JSON.parse(body);\n } catch {\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n this.logger.info(\"Discord interaction parsed\", {\n type: interaction.type,\n typeIsPing: interaction.type === InteractionType.Ping,\n expectedPingType: InteractionType.Ping,\n id: interaction.id,\n });\n\n // Handle PING (Discord verification)\n if (interaction.type === InteractionType.Ping) {\n // Use official discord-interactions response type\n const responseBody = JSON.stringify({\n type: DiscordInteractionResponseType.PONG,\n });\n this.logger.info(\"Discord PING received, responding with PONG\", {\n responseBody,\n responseType: DiscordInteractionResponseType.PONG,\n });\n return new Response(responseBody, {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // Handle MESSAGE_COMPONENT (button clicks)\n if (interaction.type === InteractionType.MessageComponent) {\n this.handleComponentInteraction(interaction, options);\n // ACK the interaction immediately\n return this.respondToInteraction({\n type: InteractionResponseType.DeferredUpdateMessage,\n });\n }\n\n // Handle APPLICATION_COMMAND (slash commands)\n if (interaction.type === InteractionType.ApplicationCommand) {\n this.handleApplicationCommandInteraction(interaction, options);\n return this.respondToInteraction({\n type: InteractionResponseType.DeferredChannelMessageWithSource,\n });\n }\n\n return new Response(\"Unknown interaction type\", { status: 400 });\n }\n\n /**\n * Verify Discord's Ed25519 signature using official discord-interactions library.\n */\n private async verifySignature(\n bodyBytes: Uint8Array,\n signature: string | null,\n timestamp: string | null\n ): Promise<boolean> {\n if (!(signature && timestamp)) {\n this.logger.warn(\n \"Discord signature verification failed: missing headers\",\n {\n hasSignature: !!signature,\n hasTimestamp: !!timestamp,\n }\n );\n return false;\n }\n\n try {\n // Log exactly what we're verifying\n this.logger.info(\"Discord signature verification attempt\", {\n bodyBytesLength: bodyBytes.length,\n signatureLength: signature.length,\n timestampLength: timestamp.length,\n publicKeyLength: this.publicKey.length,\n timestamp,\n signaturePrefix: signature.slice(0, 16),\n publicKey: this.publicKey,\n });\n\n // Use the official discord-interactions library for verification with raw bytes\n const isValid = await verifyKey(\n bodyBytes,\n signature,\n timestamp,\n this.publicKey\n );\n\n if (!isValid) {\n const bodyString = new TextDecoder().decode(bodyBytes);\n this.logger.warn(\n \"Discord signature verification failed: invalid signature\",\n {\n publicKeyLength: this.publicKey.length,\n signatureLength: signature.length,\n publicKeyPrefix: this.publicKey.slice(0, 8),\n publicKeySuffix: this.publicKey.slice(-8),\n timestamp,\n bodyLength: bodyBytes.length,\n bodyPrefix: bodyString.slice(0, 50),\n }\n );\n }\n\n return isValid;\n } catch (error) {\n this.logger.warn(\"Discord signature verification failed: exception\", {\n error,\n });\n return false;\n }\n }\n\n /**\n * Create a JSON response for Discord interactions.\n */\n private respondToInteraction(response: DiscordInteractionResponse): Response {\n return Response.json(response);\n }\n\n /**\n * Handle MESSAGE_COMPONENT interactions (button clicks).\n */\n private handleComponentInteraction(\n interaction: DiscordInteraction,\n options?: WebhookOptions\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring interaction\");\n return;\n }\n\n const customId = interaction.data?.custom_id;\n if (!customId) {\n this.logger.warn(\"No custom_id in component interaction\");\n return;\n }\n\n const user = interaction.member?.user || interaction.user;\n if (!user) {\n this.logger.warn(\"No user in component interaction\");\n return;\n }\n\n const interactionChannelId = interaction.channel_id;\n const guildId = interaction.guild_id || \"@me\";\n const messageId = interaction.message?.id;\n\n if (!(interactionChannelId && messageId)) {\n this.logger.warn(\"Missing channel_id or message_id in interaction\");\n return;\n }\n\n // Detect if the interaction is inside a thread channel\n // Discord channel types: 11 = public thread, 12 = private thread\n const channel = interaction.channel;\n const isThread = channel?.type === 11 || channel?.type === 12;\n const parentChannelId =\n isThread && channel?.parent_id ? channel.parent_id : interactionChannelId;\n\n const threadId = isThread\n ? this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: interactionChannelId,\n })\n : this.encodeThreadId({\n guildId,\n channelId: interactionChannelId,\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\" | \"openModal\"> & {\n adapter: DiscordAdapter;\n } = {\n actionId: customId,\n value: customId, // Discord custom_id often contains the value\n user: {\n userId: user.id,\n userName: user.username,\n fullName: user.global_name || user.username,\n isBot: user.bot ?? false,\n isMe: false,\n },\n messageId,\n threadId,\n adapter: this,\n raw: interaction,\n };\n\n this.logger.debug(\"Processing Discord button action\", {\n actionId: customId,\n messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n }\n\n /**\n * Handle APPLICATION_COMMAND interactions (slash commands).\n */\n private handleApplicationCommandInteraction(\n interaction: DiscordInteraction,\n options?: WebhookOptions\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring interaction\");\n return;\n }\n\n const commandName = interaction.data?.name;\n if (!commandName) {\n this.logger.warn(\"No command name in application command interaction\");\n return;\n }\n\n const user = interaction.member?.user || interaction.user;\n if (!user) {\n this.logger.warn(\"No user in application command interaction\");\n return;\n }\n\n const interactionChannelId = interaction.channel_id;\n if (!interactionChannelId) {\n this.logger.warn(\"Missing channel_id in application command interaction\");\n return;\n }\n\n const guildId = interaction.guild_id || \"@me\";\n const channel = interaction.channel;\n const isThread = channel?.type === 11 || channel?.type === 12;\n const parentChannelId =\n isThread && channel?.parent_id ? channel.parent_id : interactionChannelId;\n\n const channelId = isThread\n ? this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: interactionChannelId,\n })\n : this.encodeThreadId({\n guildId,\n channelId: interactionChannelId,\n });\n\n const { command, text } = this.parseSlashCommand(\n commandName,\n interaction.data?.options\n );\n\n this.logger.debug(\"Processing Discord slash command\", {\n command,\n text,\n userId: user.id,\n channelId,\n });\n\n // Keep interaction metadata in AsyncLocalStorage so event.channel.post(...)\n // can resolve Discord's deferred \"thinking...\" response natively.\n this.requestContext.run(\n {\n slashCommand: {\n channelId,\n interactionToken: interaction.token,\n initialResponseSent: false,\n },\n },\n () => {\n this.chat?.processSlashCommand(\n {\n command,\n text,\n user: {\n userId: user.id,\n userName: user.username,\n fullName: user.global_name || user.username,\n isBot: user.bot ?? false,\n isMe: user.id === this.applicationId,\n },\n adapter: this as Adapter,\n raw: interaction,\n channelId,\n },\n options\n );\n }\n );\n }\n\n /**\n * Parse a Discord slash command into a full command path and flat text.\n *\n * Subcommand and subcommand-group names are appended to the command path\n * so `/project issue create --title=\"Login fails\"` becomes:\n * command = \"/project issue create\"\n * text = \"Login fails\"\n *\n * Leaf option values are flattened into `text`. Consumers needing the full\n * option tree (names, types) can use `event.raw`.\n */\n private parseSlashCommand(\n name: string,\n options?: DiscordCommandOption[]\n ): { command: string; text: string } {\n const commandParts: string[] = [name.startsWith(\"/\") ? name : `/${name}`];\n const valueParts: string[] = [];\n\n const collect = (items: DiscordCommandOption[]): void => {\n for (const option of items) {\n if (option.value !== undefined) {\n valueParts.push(String(option.value));\n continue;\n }\n // Subcommand or subcommand-group — append name to command path\n if (option.options && option.options.length > 0) {\n commandParts.push(option.name);\n collect(option.options);\n }\n }\n };\n\n if (options && options.length > 0) {\n collect(options);\n }\n\n return {\n command: commandParts.join(\" \"),\n text: valueParts.join(\" \").trim(),\n };\n }\n\n /**\n * Handle a forwarded Gateway event received via webhook.\n */\n private async handleForwardedGatewayEvent(\n event: DiscordForwardedEvent,\n options?: WebhookOptions\n ): Promise<Response> {\n this.logger.info(\"Processing forwarded Gateway event\", {\n type: event.type,\n timestamp: event.timestamp,\n });\n\n switch (event.type) {\n case \"GATEWAY_MESSAGE_CREATE\":\n await this.handleForwardedMessage(\n event.data as DiscordGatewayMessageData,\n options\n );\n break;\n case \"GATEWAY_MESSAGE_REACTION_ADD\":\n await this.handleForwardedReaction(\n event.data as DiscordGatewayReactionData,\n true,\n options\n );\n break;\n case \"GATEWAY_MESSAGE_REACTION_REMOVE\":\n await this.handleForwardedReaction(\n event.data as DiscordGatewayReactionData,\n false,\n options\n );\n break;\n default:\n // Other Gateway events are forwarded but not processed - this is expected\n this.logger.debug(\"Forwarded Gateway event (no handler)\", {\n type: event.type,\n });\n }\n\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n /**\n * Handle a forwarded MESSAGE_CREATE event.\n */\n private async handleForwardedMessage(\n data: DiscordGatewayMessageData,\n _options?: WebhookOptions\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = data.guild_id || \"@me\";\n const channelId = data.channel_id;\n\n // Use thread info if provided, otherwise use channel\n let discordThreadId: string | undefined;\n let parentChannelId = channelId;\n\n if (data.thread) {\n discordThreadId = data.thread.id;\n parentChannelId = data.thread.parent_id;\n } else if (data.channel_type === 11 || data.channel_type === 12) {\n // Message is in a thread (11 = public, 12 = private) but we don't have parent info\n // Fetch the channel to get parent_id\n try {\n const response = await this.discordFetch(\n `/channels/${channelId}`,\n \"GET\"\n );\n const channel = (await response.json()) as { parent_id?: string };\n if (channel.parent_id) {\n discordThreadId = channelId;\n parentChannelId = channel.parent_id;\n this.logger.debug(\"Fetched thread parent for forwarded message\", {\n threadId: channelId,\n parentId: channel.parent_id,\n });\n }\n } catch (error) {\n this.logger.error(\"Failed to fetch thread parent\", {\n error: String(error),\n channelId,\n });\n }\n }\n\n // Check if bot is mentioned (by user ID or configured role IDs)\n const isUserMentioned =\n data.is_mention || data.mentions.some((m) => m.id === this.applicationId);\n const isRoleMentioned =\n this.mentionRoleIds.length > 0 &&\n data.mention_roles?.some((roleId) =>\n this.mentionRoleIds.includes(roleId)\n );\n const isMentioned = isUserMentioned || isRoleMentioned;\n\n // If mentioned and not in a thread, create one\n if (!discordThreadId && isMentioned) {\n try {\n const newThread = await this.createDiscordThread(channelId, data.id);\n discordThreadId = newThread.id;\n this.logger.debug(\"Created Discord thread for forwarded mention\", {\n channelId,\n messageId: data.id,\n threadId: newThread.id,\n });\n } catch (error) {\n this.logger.error(\"Failed to create Discord thread for mention\", {\n error: String(error),\n messageId: data.id,\n });\n }\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Convert to SDK Message format\n const chatMessage = new Message({\n id: data.id,\n threadId,\n text: data.content,\n formatted: this.formatConverter.toAst(data.content),\n author: {\n userId: data.author.id,\n userName: data.author.username,\n fullName: data.author.global_name || data.author.username,\n isBot: data.author.bot === true, // Discord returns null for non-bots\n isMe: data.author.id === this.applicationId,\n },\n metadata: {\n dateSent: new Date(data.timestamp),\n edited: false,\n },\n attachments: data.attachments.map((a) => ({\n type: this.getAttachmentType(a.content_type),\n url: a.url,\n name: a.filename,\n mimeType: a.content_type,\n size: a.size,\n })),\n raw: data,\n isMention: isMentioned,\n });\n\n try {\n await this.chat.handleIncomingMessage(this, threadId, chatMessage);\n } catch (error) {\n this.logger.error(\"Error handling forwarded message\", {\n error: String(error),\n messageId: data.id,\n });\n }\n }\n\n /**\n * Handle a forwarded REACTION_ADD or REACTION_REMOVE event.\n */\n private async handleForwardedReaction(\n data: DiscordGatewayReactionData,\n added: boolean,\n _options?: WebhookOptions\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = data.guild_id || \"@me\";\n const channelId = data.channel_id;\n\n // Check if reaction is in a thread channel\n let discordThreadId: string | undefined;\n let parentChannelId = channelId;\n\n if (\n data.channel_type === ChannelType.GuildPublicThread ||\n data.channel_type === ChannelType.GuildPrivateThread\n ) {\n const cached = this.threadParentCache.get(channelId);\n if (cached && cached.expiresAt > Date.now()) {\n discordThreadId = channelId;\n parentChannelId = cached.parentId;\n } else {\n try {\n const response = await this.discordFetch(\n `/channels/${channelId}`,\n \"GET\"\n );\n const channel = (await response.json()) as { parent_id?: string };\n if (channel.parent_id) {\n discordThreadId = channelId;\n parentChannelId = channel.parent_id;\n this.threadParentCache.set(channelId, {\n parentId: channel.parent_id,\n expiresAt: Date.now() + DiscordAdapter.THREAD_PARENT_CACHE_TTL,\n });\n }\n } catch (error) {\n this.logger.error(\"Failed to fetch thread parent for reaction\", {\n error: String(error),\n channelId,\n });\n }\n }\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Normalize emoji\n const emojiName = data.emoji.name || \"unknown\";\n const normalizedEmoji = this.normalizeDiscordEmoji(emojiName);\n\n // Get user info from either data.user (DMs) or data.member.user (guilds)\n const userInfo = data.user ?? data.member?.user;\n if (!userInfo) {\n this.logger.warn(\"Reaction event missing user info\", { data });\n return;\n }\n\n const reactionEvent = {\n adapter: this as Adapter,\n threadId,\n messageId: data.message_id,\n emoji: normalizedEmoji,\n rawEmoji: data.emoji.id ? `<:${emojiName}:${data.emoji.id}>` : emojiName,\n added,\n user: {\n userId: userInfo.id,\n userName: userInfo.username,\n fullName: userInfo.username,\n isBot: userInfo.bot === true, // Discord returns null for non-bots\n isMe: userInfo.id === this.applicationId,\n },\n raw: data,\n };\n\n this.chat.processReaction(reactionEvent);\n }\n\n /**\n * Post a message to a Discord channel or thread.\n */\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n let { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const actualThreadId = threadId;\n\n // If in a thread, post to the thread channel\n if (discordThreadId) {\n channelId = discordThreadId;\n }\n\n // Build message payload\n const payload: DiscordMessagePayload = {};\n const embeds: APIEmbed[] = [];\n const components: DiscordActionRow[] = [];\n\n // Check for card\n const card = extractCard(message);\n if (card) {\n const cardPayload = cardToDiscordPayload(card);\n embeds.push(...cardPayload.embeds);\n components.push(...cardPayload.components);\n // Fallback text (truncated to Discord's limit)\n payload.content = this.truncateContent(cardToFallbackText(card));\n } else {\n // Regular text message (truncated to Discord's limit)\n payload.content = this.truncateContent(\n convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"discord\"\n )\n );\n }\n\n if (embeds.length > 0) {\n payload.embeds = embeds;\n }\n if (components.length > 0) {\n payload.components = components;\n }\n\n // Handle file uploads\n const files = extractFiles(message);\n const slashResponse = this.tryPostSlashResponse(\n actualThreadId,\n payload,\n files\n );\n if (slashResponse) {\n return slashResponse;\n }\n if (files.length > 0) {\n return this.postMessageWithFiles(\n channelId,\n actualThreadId,\n payload,\n files\n );\n }\n\n this.logger.debug(\"Discord API: POST message\", {\n channelId,\n contentLength: payload.content?.length || 0,\n embedCount: embeds.length,\n componentCount: components.length,\n });\n\n const response = await this.discordFetch(\n `/channels/${channelId}/messages`,\n \"POST\",\n payload\n );\n\n const result = (await response.json()) as APIMessage;\n\n this.logger.debug(\"Discord API: POST message response\", {\n messageId: result.id,\n });\n\n return {\n id: result.id,\n threadId: actualThreadId,\n raw: result,\n };\n }\n\n private tryPostSlashResponse(\n threadId: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<RawMessage<unknown>> | undefined {\n const slashContext = this.requestContext.getStore()?.slashCommand;\n if (!slashContext || slashContext.channelId !== threadId) {\n return undefined;\n }\n return this.postSlashCommandResponse(\n slashContext,\n threadId,\n payload,\n files\n );\n }\n\n private async postSlashCommandResponse(\n slashContext: DiscordSlashCommandContext,\n threadId: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<RawMessage<unknown>> {\n const isInitialResponse = !slashContext.initialResponseSent;\n // Set flag before awaiting to prevent concurrent post() calls from both\n // trying to PATCH @original instead of the second being a followup POST.\n slashContext.initialResponseSent = true;\n\n const path = isInitialResponse\n ? `/webhooks/${this.applicationId}/${slashContext.interactionToken}/messages/@original`\n : `/webhooks/${this.applicationId}/${slashContext.interactionToken}?wait=true`;\n const method = isInitialResponse ? \"PATCH\" : \"POST\";\n\n this.logger.debug(\n \"Discord interaction webhook: responding to slash command\",\n {\n threadId,\n isInitialResponse,\n hasFiles: files.length > 0,\n }\n );\n\n const response =\n files.length > 0\n ? await this.discordInteractionFetchWithFiles(\n path,\n method,\n payload,\n files\n )\n : await this.discordInteractionFetch(path, method, payload);\n\n const result = (await response.json()) as APIMessage;\n\n return {\n id: result.id,\n threadId,\n raw: result,\n };\n }\n\n /**\n * Create a Discord thread from a message.\n */\n private async createDiscordThread(\n channelId: string,\n messageId: string\n ): Promise<{ id: string; name: string }> {\n const threadName = `Thread ${new Date().toLocaleString()}`;\n\n this.logger.debug(\"Discord API: POST thread\", {\n channelId,\n messageId,\n threadName,\n });\n\n const response = await this.discordFetch(\n `/channels/${channelId}/messages/${messageId}/threads`,\n \"POST\",\n {\n name: threadName,\n auto_archive_duration: 1440, // 24 hours\n }\n );\n\n const result = (await response.json()) as { id: string; name: string };\n\n this.logger.debug(\"Discord API: POST thread response\", {\n threadId: result.id,\n threadName: result.name,\n });\n\n return result;\n }\n\n /**\n * Truncate content to Discord's maximum length.\n */\n private truncateContent(content: string): string {\n if (content.length <= DISCORD_MAX_CONTENT_LENGTH) {\n return content;\n }\n // Truncate and add ellipsis\n return `${content.slice(0, DISCORD_MAX_CONTENT_LENGTH - 3)}...`;\n }\n\n /**\n * Post a message with file attachments.\n */\n private async postMessageWithFiles(\n channelId: string,\n threadId: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<RawMessage<unknown>> {\n const formData = new FormData();\n\n // Add JSON payload\n formData.append(\"payload_json\", JSON.stringify(payload));\n\n // Add files\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const buffer = await toBuffer(file.data, {\n platform: \"discord\" as \"slack\",\n });\n if (!buffer) {\n continue;\n }\n const blob = new Blob([new Uint8Array(buffer)], {\n type: file.mimeType || \"application/octet-stream\",\n });\n formData.append(`files[${i}]`, blob, file.filename);\n }\n\n const response = await fetch(\n `${DISCORD_API_BASE}/channels/${channelId}/messages`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bot ${this.botToken}`,\n },\n body: formData,\n }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new NetworkError(\n \"discord\",\n `Failed to post message: ${response.status} ${error}`\n );\n }\n\n const result = (await response.json()) as APIMessage;\n\n return {\n id: result.id,\n threadId,\n raw: result,\n };\n }\n\n private async discordInteractionFetch(\n path: string,\n method: string,\n body?: unknown\n ): Promise<Response> {\n const response = await fetch(`${DISCORD_API_BASE}${path}`, {\n method,\n headers: body ? { \"Content-Type\": \"application/json\" } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.logger.error(\"Discord interaction API error\", {\n path,\n method,\n status: response.status,\n error: errorText,\n });\n throw new NetworkError(\n \"discord\",\n `Discord interaction API error: ${response.status} ${errorText}`\n );\n }\n\n return response;\n }\n\n private async discordInteractionFetchWithFiles(\n path: string,\n method: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<Response> {\n const formData = new FormData();\n formData.append(\"payload_json\", JSON.stringify(payload));\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const buffer = await toBuffer(file.data, {\n platform: \"discord\",\n });\n if (!buffer) {\n continue;\n }\n const blob = new Blob([new Uint8Array(buffer)], {\n type: file.mimeType || \"application/octet-stream\",\n });\n formData.append(`files[${i}]`, blob, file.filename);\n }\n\n const response = await fetch(`${DISCORD_API_BASE}${path}`, {\n method,\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.logger.error(\"Discord interaction API error\", {\n path,\n method,\n status: response.status,\n error: errorText,\n });\n throw new NetworkError(\n \"discord\",\n `Discord interaction API error: ${response.status} ${errorText}`\n );\n }\n\n return response;\n }\n\n /**\n * Edit an existing Discord message.\n */\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n // Use thread channel ID if in a thread, otherwise use channel ID\n const targetChannelId = discordThreadId || channelId;\n\n // Build message payload\n const payload: DiscordMessagePayload = {};\n const embeds: APIEmbed[] = [];\n const components: DiscordActionRow[] = [];\n\n // Check for card\n const card = extractCard(message);\n if (card) {\n const cardPayload = cardToDiscordPayload(card);\n embeds.push(...cardPayload.embeds);\n components.push(...cardPayload.components);\n // Fallback text (truncated to Discord's limit)\n payload.content = this.truncateContent(cardToFallbackText(card));\n } else {\n // Regular text message (truncated to Discord's limit)\n payload.content = this.truncateContent(\n convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"discord\"\n )\n );\n }\n\n if (embeds.length > 0) {\n payload.embeds = embeds;\n }\n if (components.length > 0) {\n payload.components = components;\n }\n\n this.logger.debug(\"Discord API: PATCH message\", {\n channelId: targetChannelId,\n messageId,\n contentLength: payload.content?.length || 0,\n });\n\n const response = await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}`,\n \"PATCH\",\n payload\n );\n\n const result = (await response.json()) as APIMessage;\n\n this.logger.debug(\"Discord API: PATCH message response\", {\n messageId: result.id,\n });\n\n return {\n id: result.id,\n threadId,\n raw: result,\n };\n }\n\n /**\n * Delete a Discord message.\n */\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const targetChannelId = discordThreadId || channelId;\n\n this.logger.debug(\"Discord API: DELETE message\", {\n channelId: targetChannelId,\n messageId,\n });\n\n await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}`,\n \"DELETE\"\n );\n\n this.logger.debug(\"Discord API: DELETE message response\", { ok: true });\n }\n\n /**\n * Add a reaction to a Discord message.\n */\n async addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const targetChannelId = discordThreadId || channelId;\n const emojiEncoded = this.encodeEmoji(emoji);\n\n this.logger.debug(\"Discord API: PUT reaction\", {\n channelId: targetChannelId,\n messageId,\n emoji: emojiEncoded,\n });\n\n await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}/reactions/${emojiEncoded}/@me`,\n \"PUT\"\n );\n\n this.logger.debug(\"Discord API: PUT reaction response\", { ok: true });\n }\n\n /**\n * Remove a reaction from a Discord message.\n */\n async removeReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const targetChannelId = discordThreadId || channelId;\n const emojiEncoded = this.encodeEmoji(emoji);\n\n this.logger.debug(\"Discord API: DELETE reaction\", {\n channelId: targetChannelId,\n messageId,\n emoji: emojiEncoded,\n });\n\n await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}/reactions/${emojiEncoded}/@me`,\n \"DELETE\"\n );\n\n this.logger.debug(\"Discord API: DELETE reaction response\", { ok: true });\n }\n\n /**\n * Encode an emoji for use in Discord API URLs.\n */\n private encodeEmoji(emoji: EmojiValue | string): string {\n const emojiStr = defaultEmojiResolver.toDiscord\n ? defaultEmojiResolver.toDiscord(emoji)\n : String(emoji);\n // URL-encode the emoji for the API path\n return encodeURIComponent(emojiStr);\n }\n\n /**\n * Start typing indicator in a Discord channel or thread.\n */\n async startTyping(threadId: string, _status?: string): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n // Use thread channel ID if in a thread, otherwise use channel ID\n const targetChannelId = discordThreadId || channelId;\n\n this.logger.debug(\"Discord API: POST typing\", {\n channelId: targetChannelId,\n });\n\n await this.discordFetch(`/channels/${targetChannelId}/typing`, \"POST\");\n }\n\n /**\n * Fetch messages from a Discord channel or thread.\n * If threadId includes a Discord thread ID, fetches from that thread channel.\n */\n async fetchMessages(\n threadId: string,\n options: FetchOptions = {}\n ): Promise<FetchResult<unknown>> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n // Use thread channel ID if in a thread, otherwise use channel ID\n const targetChannelId = discordThreadId || channelId;\n\n const limit = options.limit || 50;\n const direction = options.direction ?? \"backward\";\n\n const params = new URLSearchParams();\n params.set(\"limit\", String(limit));\n\n // Handle pagination cursor\n if (options.cursor) {\n if (direction === \"backward\") {\n params.set(\"before\", options.cursor);\n } else {\n params.set(\"after\", options.cursor);\n }\n }\n\n this.logger.debug(\"Discord API: GET messages\", {\n channelId: targetChannelId,\n limit,\n direction,\n cursor: options.cursor,\n });\n\n const response = await this.discordFetch(\n `/channels/${targetChannelId}/messages?${params.toString()}`,\n \"GET\"\n );\n\n const rawMessages = (await response.json()) as APIMessage[];\n\n this.logger.debug(\"Discord API: GET messages response\", {\n messageCount: rawMessages.length,\n });\n\n // Discord returns messages in reverse chronological order (newest first)\n // For consistency, reverse to chronological order (oldest first)\n const sortedMessages = [...rawMessages].reverse();\n\n const messages = sortedMessages.map((msg) =>\n this.parseDiscordMessage(msg, threadId)\n );\n\n // Determine next cursor\n let nextCursor: string | undefined;\n if (rawMessages.length === limit) {\n if (direction === \"backward\") {\n // For backward, cursor is the oldest message ID in the batch\n const oldest = rawMessages.at(-1);\n nextCursor = oldest?.id;\n } else {\n // For forward, cursor is the newest message ID in the batch\n const newest = rawMessages[0];\n nextCursor = newest?.id;\n }\n }\n\n return {\n messages,\n nextCursor,\n };\n }\n\n /**\n * Fetch thread/channel information.\n */\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { channelId, guildId } = this.decodeThreadId(threadId);\n\n this.logger.debug(\"Discord API: GET channel\", { channelId });\n\n const response = await this.discordFetch(`/channels/${channelId}`, \"GET\");\n const channel = (await response.json()) as {\n id: string;\n name?: string;\n type: ChannelType;\n };\n\n return {\n id: threadId,\n channelId,\n channelName: channel.name,\n isDM:\n channel.type === ChannelType.DM || channel.type === ChannelType.GroupDM,\n metadata: {\n guildId,\n channelType: channel.type,\n raw: channel,\n },\n };\n }\n\n /**\n * Open a DM with a user.\n */\n async openDM(userId: string): Promise<string> {\n this.logger.debug(\"Discord API: POST DM channel\", { userId });\n\n const response = await this.discordFetch(\"/users/@me/channels\", \"POST\", {\n recipient_id: userId,\n });\n\n const dmChannel = (await response.json()) as {\n id: string;\n type: ChannelType;\n };\n\n this.logger.debug(\"Discord API: POST DM channel response\", {\n channelId: dmChannel.id,\n });\n\n return this.encodeThreadId({\n guildId: \"@me\",\n channelId: dmChannel.id,\n });\n }\n\n /**\n * Check if a thread is a DM.\n */\n isDM(threadId: string): boolean {\n const { guildId } = this.decodeThreadId(threadId);\n return guildId === \"@me\";\n }\n\n /**\n * Encode platform data into a thread ID string.\n */\n encodeThreadId(platformData: DiscordThreadId): string {\n const threadPart = platformData.threadId ? `:${platformData.threadId}` : \"\";\n return `discord:${platformData.guildId}:${platformData.channelId}${threadPart}`;\n }\n\n /**\n * Decode thread ID string back to platform data.\n */\n decodeThreadId(threadId: string): DiscordThreadId {\n const parts = threadId.split(\":\");\n if (parts.length < 3 || parts[0] !== \"discord\") {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord thread ID: ${threadId}`\n );\n }\n\n return {\n guildId: parts[1] as string,\n channelId: parts[2] as string,\n threadId: parts[3],\n };\n }\n\n /**\n * Parse a Discord message into normalized format.\n */\n parseMessage(raw: unknown): Message<unknown> {\n const msg = raw as APIMessage & { guild_id?: string };\n const guildId = msg.guild_id || \"@me\";\n const threadId = this.encodeThreadId({\n guildId,\n channelId: msg.channel_id,\n });\n return this.parseDiscordMessage(msg, threadId);\n }\n\n /**\n * Parse a Discord API message into normalized format.\n */\n private parseDiscordMessage(\n msg: APIMessage,\n threadId: string\n ): Message<unknown> {\n const author = msg.author;\n const isBot = author.bot ?? false;\n const isMe = author.id === this.botUserId;\n\n return new Message({\n id: msg.id,\n threadId,\n text: this.formatConverter.extractPlainText(msg.content),\n formatted: this.formatConverter.toAst(msg.content),\n raw: msg,\n author: {\n userId: author.id,\n userName: author.username,\n fullName: author.global_name || author.username,\n isBot,\n isMe,\n },\n metadata: {\n dateSent: new Date(msg.timestamp),\n edited: msg.edited_timestamp !== null,\n editedAt: msg.edited_timestamp\n ? new Date(msg.edited_timestamp)\n : undefined,\n },\n attachments: (msg.attachments || []).map((att) => ({\n type: this.getAttachmentType(att.content_type),\n url: att.url,\n name: att.filename,\n mimeType: att.content_type,\n size: att.size,\n width: att.width ?? undefined,\n height: att.height ?? undefined,\n })),\n });\n }\n\n /**\n * Determine attachment type from MIME type.\n */\n private getAttachmentType(\n mimeType?: string | null\n ): \"image\" | \"video\" | \"audio\" | \"file\" {\n if (!mimeType) {\n return \"file\";\n }\n if (mimeType.startsWith(\"image/\")) {\n return \"image\";\n }\n if (mimeType.startsWith(\"video/\")) {\n return \"video\";\n }\n if (mimeType.startsWith(\"audio/\")) {\n return \"audio\";\n }\n return \"file\";\n }\n\n /**\n * Render formatted content to Discord markdown.\n */\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n\n /**\n * Make a request to the Discord API.\n */\n private async discordFetch(\n path: string,\n method: string,\n body?: unknown\n ): Promise<Response> {\n const url = `${DISCORD_API_BASE}${path}`;\n const headers: Record<string, string> = {\n Authorization: `Bot ${this.botToken}`,\n };\n\n if (body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.logger.error(\"Discord API error\", {\n path,\n method,\n status: response.status,\n error: errorText,\n });\n throw new NetworkError(\n \"discord\",\n `Discord API error: ${response.status} ${errorText}`\n );\n }\n\n return response;\n }\n\n /**\n * Start Gateway WebSocket listener for receiving messages/mentions.\n * Uses waitUntil to keep the connection alive for the specified duration.\n *\n * This is a workaround for serverless environments - the Gateway connection\n * will stay alive for the duration, listening for messages.\n *\n * @param options - Webhook options with waitUntil function\n * @param durationMs - How long to keep listening (default: 180000ms = 3 minutes)\n * @param abortSignal - Optional AbortSignal to stop the listener early (e.g., when a new listener starts)\n * @param webhookUrl - URL to forward Gateway events to (required for webhook forwarding mode)\n * @returns Response indicating the listener was started\n */\n async startGatewayListener(\n options: WebhookOptions,\n durationMs = 180000,\n abortSignal?: AbortSignal,\n webhookUrl?: string\n ): Promise<Response> {\n if (!this.chat) {\n return new Response(\"Chat instance not initialized\", { status: 500 });\n }\n\n if (!options.waitUntil) {\n return new Response(\"waitUntil not provided\", { status: 500 });\n }\n\n this.logger.info(\"Starting Discord Gateway listener\", {\n durationMs,\n webhookUrl: webhookUrl ? \"configured\" : \"not configured\",\n });\n\n // Create a promise that resolves after the duration\n const listenerPromise = this.runGatewayListener(\n durationMs,\n abortSignal,\n webhookUrl\n );\n\n // Use waitUntil to keep the function alive\n options.waitUntil(listenerPromise);\n\n return new Response(\n JSON.stringify({\n status: \"listening\",\n durationMs,\n message: `Gateway listener started, will run for ${durationMs / 1000} seconds`,\n }),\n {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n }\n );\n }\n\n /**\n * Run the Gateway listener for a specified duration.\n */\n private async runGatewayListener(\n durationMs: number,\n abortSignal?: AbortSignal,\n webhookUrl?: string\n ): Promise<void> {\n const client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n GatewayIntentBits.GuildMessageReactions,\n GatewayIntentBits.DirectMessageReactions,\n ],\n partials: [Partials.Channel],\n });\n\n let isShuttingDown = false;\n\n // When webhookUrl is provided, use raw forwarding for ALL events\n // This keeps the Gateway simple - all processing happens in the webhook\n if (webhookUrl) {\n client.on(\"raw\", async (packet: { t: string | null; d: unknown }) => {\n if (isShuttingDown) {\n return;\n }\n if (!packet.t) {\n return; // Skip heartbeats and other non-dispatch events\n }\n\n this.logger.info(\"Discord Gateway forwarding event\", {\n type: packet.t,\n });\n\n // Forward to webhook\n await this.forwardGatewayEvent(webhookUrl, {\n type: `GATEWAY_${packet.t}` as DiscordGatewayEventType,\n timestamp: Date.now(),\n data: packet.d,\n });\n });\n } else {\n // Legacy mode: handle events directly without webhook forwarding\n this.setupLegacyGatewayHandlers(client, () => isShuttingDown);\n }\n\n client.on(Events.ClientReady, () => {\n this.logger.info(\"Discord Gateway connected\", {\n username: client.user?.username,\n id: client.user?.id,\n });\n });\n\n client.on(Events.Error, (error) => {\n this.logger.error(\"Discord Gateway error\", { error: String(error) });\n });\n\n try {\n // Login to Discord\n await client.login(this.botToken);\n\n // Wait for either: duration timeout OR abort signal\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, durationMs);\n\n // Listen for abort signal (e.g., when a new listener starts)\n if (abortSignal) {\n if (abortSignal.aborted) {\n clearTimeout(timeout);\n resolve();\n return;\n }\n abortSignal.addEventListener(\n \"abort\",\n () => {\n this.logger.info(\n \"Discord Gateway listener received abort signal (new listener started)\"\n );\n clearTimeout(timeout);\n resolve();\n },\n { once: true }\n );\n }\n });\n\n this.logger.info(\n \"Discord Gateway listener duration elapsed, disconnecting\"\n );\n } catch (error) {\n this.logger.error(\"Discord Gateway listener error\", {\n error: String(error),\n });\n } finally {\n isShuttingDown = true;\n client.destroy();\n this.logger.info(\"Discord Gateway listener stopped\");\n }\n }\n\n /**\n * Set up legacy Gateway handlers for direct processing (when webhookUrl is not provided).\n */\n private setupLegacyGatewayHandlers(\n client: Client,\n isShuttingDown: () => boolean\n ): void {\n // Message handler\n client.on(Events.MessageCreate, async (message: DiscordJsMessage) => {\n if (isShuttingDown()) {\n this.logger.debug(\"Ignoring message - Gateway is shutting down\");\n return;\n }\n\n // Ignore messages from bots (including ourselves)\n if (message.author.bot) {\n this.logger.debug(\"Ignoring message from bot\", {\n authorId: message.author.id,\n authorName: message.author.username,\n isMe: message.author.id === client.user?.id,\n });\n return;\n }\n\n // Check if we're mentioned (by user ID or configured role IDs)\n const isUserMentioned = message.mentions.has(client.user?.id ?? \"\");\n const isRoleMentioned =\n this.mentionRoleIds.length > 0 &&\n message.mentions.roles.some((role) =>\n this.mentionRoleIds.includes(role.id)\n );\n const isMentioned = isUserMentioned || isRoleMentioned;\n\n this.logger.info(\"Discord Gateway message received\", {\n channelId: message.channelId,\n guildId: message.guildId,\n authorId: message.author.id,\n isMentioned,\n isUserMentioned,\n isRoleMentioned,\n content: message.content.slice(0, 100),\n });\n\n // Process the message directly\n await this.handleGatewayMessage(message, isMentioned);\n });\n\n // Reaction add handler\n client.on(Events.MessageReactionAdd, async (reaction, user) => {\n if (isShuttingDown()) {\n this.logger.debug(\"Ignoring reaction - Gateway is shutting down\");\n return;\n }\n\n // Ignore reactions from bots (including ourselves)\n if (user.bot) {\n this.logger.debug(\"Ignoring reaction from bot\", {\n userId: user.id,\n isMe: user.id === client.user?.id,\n });\n return;\n }\n\n this.logger.info(\"Discord Gateway reaction added\", {\n emoji: reaction.emoji.name,\n messageId: reaction.message.id,\n channelId: reaction.message.channelId,\n userId: user.id,\n });\n\n // Process the reaction (skip partial users without username)\n if (user.username) {\n await this.handleGatewayReaction(\n reaction,\n user as { id: string; username: string; bot: boolean },\n true\n );\n }\n });\n\n // Reaction remove handler\n client.on(Events.MessageReactionRemove, async (reaction, user) => {\n if (isShuttingDown()) {\n this.logger.debug(\n \"Ignoring reaction removal - Gateway is shutting down\"\n );\n return;\n }\n\n // Ignore reactions from bots (including ourselves)\n if (user.bot) {\n this.logger.debug(\"Ignoring reaction removal from bot\", {\n userId: user.id,\n isMe: user.id === client.user?.id,\n });\n return;\n }\n\n this.logger.info(\"Discord Gateway reaction removed\", {\n emoji: reaction.emoji.name,\n messageId: reaction.message.id,\n channelId: reaction.message.channelId,\n userId: user.id,\n });\n\n // Process the reaction (skip partial users without username)\n if (user.username) {\n await this.handleGatewayReaction(\n reaction,\n user as { id: string; username: string; bot: boolean },\n false\n );\n }\n });\n }\n\n /**\n * Forward a Gateway event to the webhook endpoint.\n */\n private async forwardGatewayEvent(\n webhookUrl: string,\n event: DiscordForwardedEvent\n ): Promise<void> {\n try {\n this.logger.debug(\"Forwarding Gateway event to webhook\", {\n type: event.type,\n webhookUrl,\n });\n\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-discord-gateway-token\": this.botToken,\n },\n body: JSON.stringify(event),\n });\n\n if (response.ok) {\n this.logger.debug(\"Gateway event forwarded successfully\", {\n type: event.type,\n });\n } else {\n const errorText = await response.text();\n this.logger.error(\"Failed to forward Gateway event\", {\n type: event.type,\n status: response.status,\n error: errorText,\n });\n }\n } catch (error) {\n this.logger.error(\"Error forwarding Gateway event\", {\n type: event.type,\n error: String(error),\n });\n }\n }\n\n /**\n * Handle a message received via the Gateway WebSocket.\n */\n private async handleGatewayMessage(\n message: DiscordJsMessage,\n isMentioned: boolean\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = message.guildId || \"@me\";\n const channelId = message.channelId;\n\n // Check if this message is in a Discord Thread channel\n const isInThread = message.channel.isThread();\n let discordThreadId: string | undefined;\n let parentChannelId = channelId;\n\n if (\n isInThread &&\n \"parentId\" in message.channel &&\n message.channel.parentId\n ) {\n // Message is in a thread - use the thread channel ID\n discordThreadId = channelId;\n parentChannelId = message.channel.parentId;\n }\n\n // If not in a thread and bot is mentioned, create a thread immediately\n // This ensures the Thread object has the correct ID from the start\n if (!discordThreadId && isMentioned) {\n try {\n const newThread = await this.createDiscordThread(channelId, message.id);\n discordThreadId = newThread.id;\n this.logger.debug(\"Created Discord thread for incoming mention\", {\n channelId,\n messageId: message.id,\n threadId: newThread.id,\n });\n } catch (error) {\n this.logger.error(\"Failed to create Discord thread for mention\", {\n error: String(error),\n messageId: message.id,\n });\n // Continue without thread - will use channel\n }\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Convert discord.js message to our Message format\n const chatMessage = new Message({\n id: message.id,\n threadId,\n text: message.content,\n formatted: this.formatConverter.toAst(message.content),\n author: {\n userId: message.author.id,\n userName: message.author.username,\n fullName: message.author.displayName || message.author.username,\n isBot: message.author.bot,\n isMe: false, // Gateway messages are never from ourselves (we filter those)\n },\n metadata: {\n dateSent: message.createdAt,\n edited: message.editedAt !== null,\n editedAt: message.editedAt ?? undefined,\n },\n attachments: message.attachments.map((a) => ({\n type: this.getAttachmentType(a.contentType),\n url: a.url,\n name: a.name,\n mimeType: a.contentType ?? undefined,\n size: a.size,\n })),\n raw: {\n id: message.id,\n channel_id: channelId,\n guild_id: guildId,\n content: message.content,\n author: {\n id: message.author.id,\n username: message.author.username,\n },\n timestamp: message.createdAt.toISOString(),\n },\n // Add isMention flag for the chat handlers\n isMention: isMentioned,\n });\n\n try {\n await this.chat.handleIncomingMessage(this, threadId, chatMessage);\n } catch (error) {\n this.logger.error(\"Error handling Gateway message\", {\n error: String(error),\n messageId: message.id,\n });\n }\n }\n\n /**\n * Handle a reaction received via the Gateway WebSocket.\n */\n private async handleGatewayReaction(\n reaction: {\n emoji: { name: string | null; id: string | null };\n message: {\n id: string;\n channelId: string;\n guildId: string | null;\n channel?: { isThread?: () => boolean; parentId?: string | null };\n };\n },\n user: { id: string; username: string; bot: boolean },\n added: boolean\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = reaction.message.guildId || \"@me\";\n const channelId = reaction.message.channelId;\n\n // Check if reaction is in a thread channel\n const isInThread = reaction.message.channel?.isThread?.();\n let parentChannelId = channelId;\n let discordThreadId: string | undefined;\n\n if (isInThread && reaction.message.channel?.parentId) {\n discordThreadId = channelId;\n parentChannelId = reaction.message.channel.parentId;\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Normalize emoji\n const emojiName = reaction.emoji.name || \"unknown\";\n const normalizedEmoji = this.normalizeDiscordEmoji(emojiName);\n\n // Build reaction event\n const reactionEvent = {\n adapter: this as Adapter,\n threadId,\n messageId: reaction.message.id,\n emoji: normalizedEmoji,\n rawEmoji: reaction.emoji.id\n ? `<:${emojiName}:${reaction.emoji.id}>`\n : emojiName,\n added,\n user: {\n userId: user.id,\n userName: user.username,\n fullName: user.username,\n isBot: user.bot === true, // Match pattern from handleForwardedReaction\n isMe: user.id === this.applicationId,\n },\n raw: {\n emoji: reaction.emoji,\n message_id: reaction.message.id,\n channel_id: reaction.message.channelId,\n guild_id: reaction.message.guildId,\n user_id: user.id,\n },\n };\n\n this.chat.processReaction(reactionEvent);\n }\n\n /**\n * Derive channel ID from a Discord thread ID.\n * Discord: discord:{guildId}:{channelId}:{threadId} -> discord:{guildId}:{channelId}\n * If already a channel ID (3 parts), returns as-is.\n */\n channelIdFromThreadId(threadId: string): string {\n const parts = threadId.split(\":\");\n // discord:{guildId}:{channelId} or discord:{guildId}:{channelId}:{threadId}\n return parts.slice(0, 3).join(\":\");\n }\n\n /**\n * Fetch channel-level messages (not thread replies).\n * Uses the parent channel ID to fetch top-level messages.\n */\n async fetchChannelMessages(\n channelId: string,\n options: FetchOptions = {}\n ): Promise<FetchResult<unknown>> {\n const parts = channelId.split(\":\");\n const discordChannelId = parts[2];\n if (!discordChannelId) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n const limit = options.limit || 50;\n const direction = options.direction ?? \"backward\";\n\n const params = new URLSearchParams();\n params.set(\"limit\", String(limit));\n\n if (options.cursor) {\n if (direction === \"backward\") {\n params.set(\"before\", options.cursor);\n } else {\n params.set(\"after\", options.cursor);\n }\n }\n\n this.logger.debug(\"Discord API: GET channel messages\", {\n channelId: discordChannelId,\n limit,\n direction,\n cursor: options.cursor,\n });\n\n const response = await this.discordFetch(\n `/channels/${discordChannelId}/messages?${params.toString()}`,\n \"GET\"\n );\n\n const rawMessages = (await response.json()) as APIMessage[];\n\n this.logger.debug(\"Discord API: GET channel messages response\", {\n messageCount: rawMessages.length,\n });\n\n // Discord returns newest first, reverse for chronological order\n const sortedMessages = [...rawMessages].reverse();\n\n const messages = sortedMessages.map((msg) =>\n this.parseDiscordMessage(msg, channelId)\n );\n\n let nextCursor: string | undefined;\n if (rawMessages.length === limit) {\n if (direction === \"backward\") {\n const oldest = rawMessages.at(-1);\n nextCursor = oldest?.id;\n } else {\n const newest = rawMessages[0];\n nextCursor = newest?.id;\n }\n }\n\n return { messages, nextCursor };\n }\n\n /**\n * List threads in a Discord channel.\n * Fetches active threads from the guild and archived threads from the channel.\n */\n async listThreads(\n channelId: string,\n options: ListThreadsOptions = {}\n ): Promise<ListThreadsResult<unknown>> {\n const parts = channelId.split(\":\");\n const guildId = parts[1];\n const discordChannelId = parts[2];\n if (!(guildId && discordChannelId)) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n this.logger.debug(\"Discord API: GET threads\", {\n guildId,\n channelId: discordChannelId,\n });\n\n // Fetch active threads from the guild\n const activeResponse = await this.discordFetch(\n `/guilds/${guildId}/threads/active`,\n \"GET\"\n );\n const activeData = (await activeResponse.json()) as {\n threads: Array<{\n id: string;\n name: string;\n parent_id: string;\n message_count?: number;\n total_message_sent?: number;\n thread_metadata?: { archive_timestamp?: string };\n }>;\n };\n\n // Filter threads that belong to our channel\n const channelThreads = (activeData.threads || []).filter(\n (t) => t.parent_id === discordChannelId\n );\n\n // Also fetch archived public threads\n let archivedThreads: typeof channelThreads = [];\n try {\n const archivedResponse = await this.discordFetch(\n `/channels/${discordChannelId}/threads/archived/public?limit=${options.limit || 50}`,\n \"GET\"\n );\n const archivedData = (await archivedResponse.json()) as {\n threads: typeof channelThreads;\n };\n archivedThreads = archivedData.threads || [];\n } catch {\n // Archived threads may not be available (permissions)\n this.logger.debug(\n \"Could not fetch archived threads (may lack permissions)\"\n );\n }\n\n // Merge and deduplicate\n const allThreads = [...channelThreads, ...archivedThreads];\n const seen = new Set<string>();\n const uniqueThreads = allThreads.filter((t) => {\n if (seen.has(t.id)) {\n return false;\n }\n seen.add(t.id);\n return true;\n });\n\n // Apply limit\n const limit = options.limit || 50;\n const limitedThreads = uniqueThreads.slice(0, limit);\n\n // Convert to ThreadSummary - fetch the first message of each thread\n const threads: ThreadSummary[] = [];\n for (const thread of limitedThreads) {\n const threadId = this.encodeThreadId({\n guildId,\n channelId: discordChannelId,\n threadId: thread.id,\n });\n\n // Fetch the first message of the thread (which is the root/starter message)\n try {\n const msgsResponse = await this.discordFetch(\n `/channels/${thread.id}/messages?limit=1&after=0`,\n \"GET\"\n );\n const msgs = (await msgsResponse.json()) as APIMessage[];\n const rootMsg = msgs[0];\n\n if (rootMsg) {\n threads.push({\n id: threadId,\n rootMessage: this.parseDiscordMessage(rootMsg, threadId),\n replyCount: thread.total_message_sent ?? thread.message_count,\n lastReplyAt: thread.thread_metadata?.archive_timestamp\n ? new Date(thread.thread_metadata.archive_timestamp)\n : undefined,\n });\n }\n } catch {\n // If we can't fetch the root message, create a placeholder\n threads.push({\n id: threadId,\n rootMessage: new Message({\n id: thread.id,\n threadId,\n text: thread.name,\n formatted: this.formatConverter.toAst(thread.name),\n raw: thread,\n author: {\n userId: \"unknown\",\n userName: \"unknown\",\n fullName: \"unknown\",\n isBot: false,\n isMe: false,\n },\n metadata: { dateSent: new Date(), edited: false },\n attachments: [],\n }),\n replyCount: thread.total_message_sent ?? thread.message_count,\n });\n }\n }\n\n this.logger.debug(\"Discord API: listThreads result\", {\n threadCount: threads.length,\n });\n\n return {\n threads,\n nextCursor: uniqueThreads.length > limit ? String(limit) : undefined,\n };\n }\n\n /**\n * Fetch Discord channel info/metadata.\n */\n async fetchChannelInfo(channelId: string): Promise<ChannelInfo> {\n const parts = channelId.split(\":\");\n const discordChannelId = parts[2];\n if (!discordChannelId) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n this.logger.debug(\"Discord API: GET channel info\", {\n channelId: discordChannelId,\n });\n\n const response = await this.discordFetch(\n `/channels/${discordChannelId}`,\n \"GET\"\n );\n const channel = (await response.json()) as {\n id: string;\n name?: string;\n type: ChannelType;\n member_count?: number;\n };\n\n return {\n id: channelId,\n name: channel.name,\n isDM:\n channel.type === ChannelType.DM || channel.type === ChannelType.GroupDM,\n memberCount: channel.member_count,\n metadata: {\n channelType: channel.type,\n raw: channel,\n },\n };\n }\n\n /**\n * Post a message to channel top-level (not in a thread).\n * Posts directly to the parent channel without a thread reference.\n */\n async postChannelMessage(\n channelId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n const parts = channelId.split(\":\");\n const discordChannelId = parts[2];\n if (!discordChannelId) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n // Build message payload\n const payload: DiscordMessagePayload = {};\n const embeds: APIEmbed[] = [];\n const components: DiscordActionRow[] = [];\n\n const card = extractCard(message);\n if (card) {\n const cardPayload = cardToDiscordPayload(card);\n embeds.push(...cardPayload.embeds);\n components.push(...cardPayload.components);\n payload.content = this.truncateContent(cardToFallbackText(card));\n } else {\n payload.content = this.truncateContent(\n convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"discord\"\n )\n );\n }\n\n if (embeds.length > 0) {\n payload.embeds = embeds;\n }\n if (components.length > 0) {\n payload.components = components;\n }\n\n const files = extractFiles(message);\n const slashResponse = this.tryPostSlashResponse(channelId, payload, files);\n if (slashResponse) {\n return slashResponse;\n }\n if (files.length > 0) {\n return this.postMessageWithFiles(\n discordChannelId,\n channelId,\n payload,\n files\n );\n }\n\n this.logger.debug(\"Discord API: POST channel message\", {\n channelId: discordChannelId,\n contentLength: payload.content?.length || 0,\n });\n\n const response = await this.discordFetch(\n `/channels/${discordChannelId}/messages`,\n \"POST\",\n payload\n );\n\n const result = (await response.json()) as APIMessage;\n\n return {\n id: result.id,\n threadId: channelId,\n raw: result,\n };\n }\n\n /**\n * Normalize a Discord emoji to our standard EmojiValue format.\n */\n private normalizeDiscordEmoji(emojiName: string): EmojiValue {\n // Map common Discord unicode emoji to our standard names\n const unicodeToName: Record<string, string> = {\n \"👍\": \"thumbs_up\",\n \"👎\": \"thumbs_down\",\n \"❤️\": \"heart\",\n \"❤\": \"heart\",\n \"🔥\": \"fire\",\n \"🚀\": \"rocket\",\n \"🙌\": \"raised_hands\",\n \"✅\": \"check\",\n \"❌\": \"x\",\n \"👋\": \"wave\",\n \"🤔\": \"thinking\",\n \"😊\": \"smile\",\n \"😂\": \"laugh\",\n \"🎉\": \"party\",\n \"⭐\": \"star\",\n \"✨\": \"sparkles\",\n \"👀\": \"eyes\",\n \"💯\": \"100\",\n };\n\n // Check if it's a unicode emoji we recognize\n const normalizedName = unicodeToName[emojiName] || emojiName;\n return getEmoji(normalizedName);\n }\n}\n\n/**\n * Create a Discord adapter instance.\n */\nexport function createDiscordAdapter(\n config?: Partial<DiscordAdapterConfig & { logger: Logger; userName?: string }>\n): DiscordAdapter {\n const botToken = config?.botToken ?? process.env.DISCORD_BOT_TOKEN;\n if (!botToken) {\n throw new ValidationError(\n \"discord\",\n \"botToken is required. Set DISCORD_BOT_TOKEN or provide it in config.\"\n );\n }\n const publicKey = config?.publicKey ?? process.env.DISCORD_PUBLIC_KEY;\n if (!publicKey) {\n throw new ValidationError(\n \"discord\",\n \"publicKey is required. Set DISCORD_PUBLIC_KEY or provide it in config.\"\n );\n }\n const applicationId =\n config?.applicationId ?? process.env.DISCORD_APPLICATION_ID;\n if (!applicationId) {\n throw new ValidationError(\n \"discord\",\n \"applicationId is required. Set DISCORD_APPLICATION_ID or provide it in config.\"\n );\n }\n const mentionRoleIds =\n config?.mentionRoleIds ??\n (process.env.DISCORD_MENTION_ROLE_IDS\n ? process.env.DISCORD_MENTION_ROLE_IDS.split(\",\").map((id) => id.trim())\n : undefined);\n const resolved: DiscordAdapterConfig & {\n logger: Logger;\n userName?: string;\n } = {\n botToken,\n publicKey,\n applicationId,\n mentionRoleIds,\n logger: config?.logger ?? new ConsoleLogger(\"info\").child(\"discord\"),\n userName: config?.userName,\n };\n return new DiscordAdapter(resolved);\n}\n\n// Re-export card converter for advanced use\nexport { cardToDiscordPayload, cardToFallbackText } from \"./cards\";\n\n// Re-export format converter for advanced use\nexport {\n DiscordFormatConverter,\n DiscordFormatConverter as DiscordMarkdownConverter,\n} from \"./markdown\";\n// Re-export types\nexport type { DiscordAdapterConfig, DiscordThreadId } from \"./types\";\n","/**\n * Discord Embed and Component converter for cross-platform cards.\n *\n * Converts CardElement to Discord Embeds and Action Row Components.\n * @see https://discord.com/developers/docs/resources/message#embed-object\n * @see https://discord.com/developers/docs/interactions/message-components\n */\n\nimport { renderGfmTable } from \"@chat-adapter/shared\";\nimport type {\n ActionsElement,\n ButtonElement,\n CardChild,\n CardElement,\n FieldsElement,\n LinkButtonElement,\n SectionElement,\n TextElement,\n} from \"chat\";\nimport {\n cardChildToFallbackText,\n convertEmojiPlaceholders,\n tableElementToAscii,\n} from \"chat\";\nimport type { APIEmbed, APIEmbedField } from \"discord-api-types/v10\";\nimport { ButtonStyle } from \"discord-api-types/v10\";\nimport type { DiscordActionRow, DiscordButton } from \"./types\";\n\n/**\n * Convert emoji placeholders to Discord format.\n */\nfunction convertEmoji(text: string): string {\n return convertEmojiPlaceholders(text, \"discord\");\n}\n\n/**\n * Convert a CardElement to Discord message payload (embeds + components).\n */\nexport function cardToDiscordPayload(card: CardElement): {\n embeds: APIEmbed[];\n components: DiscordActionRow[];\n} {\n const embed: APIEmbed = {};\n const fields: APIEmbedField[] = [];\n const components: DiscordActionRow[] = [];\n\n // Set title and description (with emoji conversion)\n if (card.title) {\n embed.title = convertEmoji(card.title);\n }\n\n if (card.subtitle) {\n embed.description = convertEmoji(card.subtitle);\n }\n\n // Set header image\n if (card.imageUrl) {\n embed.image = {\n url: card.imageUrl,\n };\n }\n\n // Set color (default to Discord blurple)\n embed.color = 0x5865f2;\n\n // Process children\n const textParts: string[] = [];\n\n for (const child of card.children) {\n processChild(child, textParts, fields, components);\n }\n\n // If we have text parts and no description, set them as description\n if (textParts.length > 0) {\n if (embed.description) {\n embed.description += `\\n\\n${textParts.join(\"\\n\\n\")}`;\n } else {\n embed.description = textParts.join(\"\\n\\n\");\n }\n }\n\n // Add fields if we have any\n if (fields.length > 0) {\n embed.fields = fields;\n }\n\n return {\n embeds: [embed],\n components,\n };\n}\n\n/**\n * Process a card child element.\n */\nfunction processChild(\n child: CardChild,\n textParts: string[],\n fields: APIEmbedField[],\n components: DiscordActionRow[]\n): void {\n switch (child.type) {\n case \"text\":\n textParts.push(convertTextElement(child));\n break;\n case \"image\":\n // Discord embeds can only have one image, handled at card level\n // Additional images could be added as separate embeds\n break;\n case \"divider\":\n // No direct equivalent, add a horizontal line marker\n textParts.push(\"───────────\");\n break;\n case \"actions\":\n components.push(...convertActionsToRows(child));\n break;\n case \"section\":\n processSectionElement(child, textParts, fields, components);\n break;\n case \"fields\":\n convertFieldsElement(child, fields);\n break;\n case \"link\":\n textParts.push(`[${convertEmoji(child.label)}](${child.url})`);\n break;\n case \"table\": {\n // Render as GFM markdown table in embed description\n textParts.push(renderGfmTable(child).join(\"\\n\"));\n break;\n }\n default: {\n const text = cardChildToFallbackText(child);\n if (text) {\n textParts.push(text);\n }\n break;\n }\n }\n}\n\n/**\n * Convert a text element to Discord markdown.\n */\nfunction convertTextElement(element: TextElement): string {\n let text = convertEmoji(element.content);\n\n // Apply style\n if (element.style === \"bold\") {\n text = `**${text}**`;\n } else if (element.style === \"muted\") {\n // Discord doesn't have muted, use italic as approximation\n text = `*${text}*`;\n }\n\n return text;\n}\n\n/**\n * Convert an actions element to Discord action rows.\n * Discord limits each action row to 5 components, so we chunk buttons.\n */\nfunction convertActionsToRows(element: ActionsElement): DiscordActionRow[] {\n const buttons: DiscordButton[] = element.children\n .filter((child) => child.type === \"button\" || child.type === \"link-button\")\n .map((button) => {\n if (button.type === \"link-button\") {\n return convertLinkButtonElement(button);\n }\n return convertButtonElement(button);\n });\n\n // Discord allows max 5 buttons per action row\n const rows: DiscordActionRow[] = [];\n for (let i = 0; i < buttons.length; i += 5) {\n rows.push({\n type: 1, // Action Row\n components: buttons.slice(i, i + 5),\n });\n }\n return rows;\n}\n\n/**\n * Convert a button element to a Discord button.\n */\nfunction convertButtonElement(button: ButtonElement): DiscordButton {\n const discordButton: DiscordButton = {\n type: 2, // Button\n style: getButtonStyle(button.style),\n label: button.label,\n custom_id: button.id,\n };\n\n if (button.disabled) {\n discordButton.disabled = true;\n }\n\n return discordButton;\n}\n\n/**\n * Convert a link button element to a Discord link button.\n */\nfunction convertLinkButtonElement(button: LinkButtonElement): DiscordButton {\n return {\n type: 2, // Button\n style: ButtonStyle.Link,\n label: button.label,\n url: button.url,\n };\n}\n\n/**\n * Map button style to Discord button style.\n */\nfunction getButtonStyle(style?: ButtonElement[\"style\"]): ButtonStyle {\n switch (style) {\n case \"primary\":\n return ButtonStyle.Primary;\n case \"danger\":\n return ButtonStyle.Danger;\n default:\n return ButtonStyle.Secondary;\n }\n}\n\n/**\n * Process a section element.\n */\nfunction processSectionElement(\n element: SectionElement,\n textParts: string[],\n fields: APIEmbedField[],\n components: DiscordActionRow[]\n): void {\n for (const child of element.children) {\n processChild(child, textParts, fields, components);\n }\n}\n\n/**\n * Convert fields element to Discord embed fields.\n */\nfunction convertFieldsElement(\n element: FieldsElement,\n fields: APIEmbedField[]\n): void {\n for (const field of element.children) {\n fields.push({\n name: convertEmoji(field.label),\n value: convertEmoji(field.value),\n inline: true, // Discord fields can be inline\n });\n }\n}\n\n/**\n * Generate fallback text from a card element.\n * Used when embeds aren't supported or for notifications.\n */\nexport function cardToFallbackText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(`**${convertEmoji(card.title)}**`);\n }\n\n if (card.subtitle) {\n parts.push(convertEmoji(card.subtitle));\n }\n\n for (const child of card.children) {\n const text = childToFallbackText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n\n/**\n * Convert a card child element to fallback text.\n */\nfunction childToFallbackText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return convertEmoji(child.content);\n case \"fields\":\n return child.children\n .map((f) => `**${convertEmoji(f.label)}**: ${convertEmoji(f.value)}`)\n .join(\"\\n\");\n case \"actions\":\n // Actions are interactive-only — exclude from fallback text.\n // See: https://docs.slack.dev/reference/methods/chat.postMessage\n return null;\n case \"section\":\n return child.children\n .map((c) => childToFallbackText(c))\n .filter(Boolean)\n .join(\"\\n\");\n case \"table\":\n return `\\`\\`\\`\\n${tableElementToAscii(child.headers, child.rows)}\\n\\`\\`\\``;\n case \"divider\":\n return \"---\";\n default:\n return cardChildToFallbackText(child);\n }\n}\n","/**\n * Discord-specific format conversion using AST-based parsing.\n *\n * Discord uses standard markdown with some extensions:\n * - Bold: **text** (standard)\n * - Italic: *text* or _text_ (standard)\n * - Strikethrough: ~~text~~ (standard GFM)\n * - Links: [text](url) (standard)\n * - User mentions: <@userId>\n * - Channel mentions: <#channelId>\n * - Role mentions: <@&roleId>\n * - Custom emoji: <:name:id> or <a:name:id> (animated)\n * - Spoiler: ||text||\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Content,\n getNodeChildren,\n isBlockquoteNode,\n isCodeNode,\n isDeleteNode,\n isEmphasisNode,\n isInlineCodeNode,\n isLinkNode,\n isListNode,\n isParagraphNode,\n isStrongNode,\n isTableNode,\n isTextNode,\n parseMarkdown,\n type Root,\n tableToAscii,\n} from \"chat\";\n\nexport class DiscordFormatConverter extends BaseFormatConverter {\n /**\n * Convert @mentions to Discord format in plain text.\n * @name → <@name>\n */\n private convertMentionsToDiscord(text: string): string {\n return text.replace(/@(\\w+)/g, \"<@$1>\");\n }\n\n /**\n * Override renderPostable to convert @mentions in plain strings.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return this.convertMentionsToDiscord(message);\n }\n if (\"raw\" in message) {\n return this.convertMentionsToDiscord(message.raw);\n }\n if (\"markdown\" in message) {\n return this.fromAst(parseMarkdown(message.markdown));\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return \"\";\n }\n\n /**\n * Render an AST to Discord markdown format.\n */\n fromAst(ast: Root): string {\n return this.fromAstWithNodeConverter(ast, (node) =>\n this.nodeToDiscordMarkdown(node)\n );\n }\n\n /**\n * Parse Discord markdown into an AST.\n */\n toAst(discordMarkdown: string): Root {\n // Convert Discord-specific formats to standard markdown, then parse\n let markdown = discordMarkdown;\n\n // User mentions: <@userId> or <@!userId> -> @userId\n markdown = markdown.replace(/<@!?(\\w+)>/g, \"@$1\");\n\n // Channel mentions: <#channelId> -> #channelId\n markdown = markdown.replace(/<#(\\w+)>/g, \"#$1\");\n\n // Role mentions: <@&roleId> -> @&roleId\n markdown = markdown.replace(/<@&(\\w+)>/g, \"@&$1\");\n\n // Custom emoji: <:name:id> or <a:name:id> -> :name:\n markdown = markdown.replace(/<a?:(\\w+):\\d+>/g, \":$1:\");\n\n // Spoiler tags: ||text|| -> [spoiler: text]\n // (no direct markdown equivalent, convert to placeholder)\n markdown = markdown.replace(/\\|\\|([^|]+)\\|\\|/g, \"[spoiler: $1]\");\n\n return parseMarkdown(markdown);\n }\n\n private nodeToDiscordMarkdown(node: Content): string {\n // Use type guards for type-safe node handling\n if (isParagraphNode(node)) {\n return getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n }\n\n if (isTextNode(node)) {\n // Convert @mentions to Discord format <@mention>\n return node.value.replace(/@(\\w+)/g, \"<@$1>\");\n }\n\n if (isStrongNode(node)) {\n // Standard markdown **text**\n const content = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n return `**${content}**`;\n }\n\n if (isEmphasisNode(node)) {\n // Standard markdown *text*\n const content = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n return `*${content}*`;\n }\n\n if (isDeleteNode(node)) {\n // Standard GFM ~~text~~\n const content = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n return `~~${content}~~`;\n }\n\n if (isInlineCodeNode(node)) {\n return `\\`${node.value}\\``;\n }\n\n if (isCodeNode(node)) {\n return `\\`\\`\\`${node.lang || \"\"}\\n${node.value}\\n\\`\\`\\``;\n }\n\n if (isLinkNode(node)) {\n const linkText = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n // Standard markdown [text](url)\n return `[${linkText}](${node.url})`;\n }\n\n if (isBlockquoteNode(node)) {\n return getNodeChildren(node)\n .map((child) => `> ${this.nodeToDiscordMarkdown(child)}`)\n .join(\"\\n\");\n }\n\n if (isListNode(node)) {\n return this.renderList(node, 0, (child) =>\n this.nodeToDiscordMarkdown(child)\n );\n }\n\n if (node.type === \"break\") {\n return \"\\n\";\n }\n\n if (node.type === \"thematicBreak\") {\n return \"---\";\n }\n\n if (isTableNode(node)) {\n return `\\`\\`\\`\\n${tableToAscii(node)}\\n\\`\\`\\``;\n }\n\n return this.defaultNodeToText(node, (child) =>\n this.nodeToDiscordMarkdown(child)\n );\n }\n}\n","/**\n * Discord adapter types.\n */\n\nimport type {\n APIEmbed,\n APIMessage,\n ButtonStyle,\n ChannelType,\n InteractionType,\n} from \"discord-api-types/v10\";\n\n/**\n * Discord adapter configuration.\n */\nexport interface DiscordAdapterConfig {\n /** Discord application ID */\n applicationId: string;\n /** Discord bot token */\n botToken: string;\n /** Role IDs that should trigger mention handlers (in addition to direct user mentions) */\n mentionRoleIds?: string[];\n /** Discord application public key for webhook signature verification */\n publicKey: string;\n}\n\n/**\n * Discord thread ID components.\n * Used for encoding/decoding thread IDs.\n */\nexport interface DiscordThreadId {\n /** Channel ID */\n channelId: string;\n /** Guild ID, or \"@me\" for DMs */\n guildId: string;\n /** Thread ID (if message is in a thread) */\n threadId?: string;\n}\n\n/**\n * Per-request slash command context used while resolving deferred responses.\n */\nexport interface DiscordSlashCommandContext {\n channelId: string;\n initialResponseSent: boolean;\n interactionToken: string;\n}\n\n/**\n * Async request context for Discord webhook handling.\n */\nexport interface DiscordRequestContext {\n slashCommand?: DiscordSlashCommandContext;\n}\n\n/**\n * Incoming Discord interaction from webhook.\n */\nexport interface DiscordInteraction {\n application_id: string;\n channel?: {\n id: string;\n type: ChannelType;\n name?: string;\n /** Parent channel ID (present when channel is a thread) */\n parent_id?: string;\n };\n channel_id?: string;\n data?: DiscordInteractionData;\n guild_id?: string;\n id: string;\n member?: {\n user: DiscordUser;\n nick?: string;\n roles: string[];\n joined_at: string;\n };\n message?: APIMessage;\n token: string;\n type: InteractionType;\n user?: DiscordUser;\n version: number;\n}\n\n/**\n * Discord user object.\n */\nexport interface DiscordUser {\n avatar?: string;\n bot?: boolean;\n discriminator: string;\n global_name?: string;\n id: string;\n username: string;\n}\n\n/**\n * Discord interaction data (for components/commands).\n */\nexport interface DiscordInteractionData {\n component_type?: number;\n custom_id?: string;\n name?: string;\n options?: DiscordCommandOption[];\n type?: number;\n values?: string[];\n}\n\n/**\n * Discord command option.\n */\nexport interface DiscordCommandOption {\n name: string;\n options?: DiscordCommandOption[];\n type: number;\n value?: string | number | boolean;\n}\n\n/**\n * Discord emoji.\n */\nexport interface DiscordEmoji {\n animated?: boolean;\n id?: string;\n name: string;\n}\n\n/**\n * Discord button component.\n */\nexport interface DiscordButton {\n custom_id?: string;\n disabled?: boolean;\n emoji?: DiscordEmoji;\n label?: string;\n style: ButtonStyle;\n type: 2; // Component type for button\n url?: string;\n}\n\n/**\n * Discord action row component.\n */\nexport interface DiscordActionRow {\n components: DiscordButton[];\n type: 1; // Component type for action row\n}\n\n/**\n * Discord message create payload.\n */\nexport interface DiscordMessagePayload {\n allowed_mentions?: {\n parse?: (\"roles\" | \"users\" | \"everyone\")[];\n roles?: string[];\n users?: string[];\n replied_user?: boolean;\n };\n attachments?: {\n id: string;\n filename: string;\n description?: string;\n }[];\n components?: DiscordActionRow[];\n content?: string;\n embeds?: APIEmbed[];\n message_reference?: {\n message_id: string;\n fail_if_not_exists?: boolean;\n };\n}\n\n/**\n * Discord interaction response types.\n * Note: Only the types currently used are defined here.\n * Additional types: ChannelMessageWithSource (4), UpdateMessage (7)\n */\nexport const InteractionResponseType = {\n /** ACK and edit later (deferred) */\n DeferredChannelMessageWithSource: 5,\n /** ACK component interaction, update message later */\n DeferredUpdateMessage: 6,\n} as const;\n\nexport type InteractionResponseType =\n (typeof InteractionResponseType)[keyof typeof InteractionResponseType];\n\n/**\n * Discord interaction response.\n */\nexport interface DiscordInteractionResponse {\n data?: DiscordMessagePayload;\n type: InteractionResponseType;\n}\n\n// ============================================================================\n// Gateway Forwarded Events\n// These types represent Gateway WebSocket events forwarded to the webhook endpoint\n// ============================================================================\n\n/**\n * Known Gateway event types that have specific handlers.\n * Other event types are still forwarded but processed generically.\n */\nexport type DiscordGatewayEventType =\n | \"GATEWAY_MESSAGE_CREATE\"\n | \"GATEWAY_MESSAGE_REACTION_ADD\"\n | \"GATEWAY_MESSAGE_REACTION_REMOVE\"\n | `GATEWAY_${string}`; // Allow any Gateway event type\n\n/**\n * A Gateway event forwarded to the webhook endpoint.\n * All Gateway events are forwarded, even ones without specific handlers.\n */\nexport interface DiscordForwardedEvent {\n /** Event-specific data - structure varies by event type */\n data: DiscordGatewayMessageData | DiscordGatewayReactionData | unknown;\n /** Unix timestamp when the event was received */\n timestamp: number;\n /** Event type identifier (prefixed with GATEWAY_) */\n type: DiscordGatewayEventType;\n}\n\n/**\n * Message data from a MESSAGE_CREATE Gateway event.\n */\nexport interface DiscordGatewayMessageData {\n /** File attachments */\n attachments: Array<{\n id: string;\n url: string;\n filename: string;\n content_type?: string;\n size: number;\n }>;\n /** Message author */\n author: {\n id: string;\n username: string;\n global_name?: string;\n bot: boolean;\n };\n /** Channel where the message was sent */\n channel_id: string;\n /** Channel type (11 = public thread, 12 = private thread) */\n channel_type?: number;\n /** Message content */\n content: string;\n /** Guild ID, or null for DMs */\n guild_id: string | null;\n /** Message ID */\n id: string;\n /** Whether the bot was mentioned */\n is_mention?: boolean;\n /** Role IDs mentioned in the message */\n mention_roles?: string[];\n /** Users mentioned in the message */\n mentions: Array<{ id: string; username: string }>;\n /** Thread info if message is in a thread */\n thread?: {\n id: string;\n parent_id: string;\n };\n /** ISO timestamp */\n timestamp: string;\n}\n\n/**\n * Reaction data from REACTION_ADD or REACTION_REMOVE Gateway events.\n */\nexport interface DiscordGatewayReactionData {\n /** Channel containing the message */\n channel_id: string;\n /** Channel type (11 = public thread, 12 = private thread) */\n channel_type?: number;\n /** Emoji used for the reaction */\n emoji: {\n name: string | null;\n id: string | null;\n };\n /** Guild ID, or null for DMs */\n guild_id: string | null;\n /** Member details (for guild reactions) */\n member?: {\n user: {\n id: string;\n username: string;\n global_name?: string;\n bot?: boolean;\n };\n };\n /** ID of the message that was reacted to */\n message_id: string;\n /** User details (for DMs) */\n user?: {\n id: string;\n username: string;\n bot?: boolean;\n };\n /** User who added/removed the reaction */\n user_id: string;\n}\n"],"mappings":"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","names":["convertEmojiPlaceholders","convertEmojiPlaceholders"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts","../src/types.ts"],"sourcesContent":["/**\n * Discord adapter for chat-sdk.\n *\n * Uses Discord's HTTP Interactions API (not Gateway WebSocket) for\n * serverless compatibility. Webhook signature verification uses Ed25519.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n extractCard,\n extractFiles,\n NetworkError,\n toBuffer,\n ValidationError,\n} from \"@chat-adapter/shared\";\nimport type {\n ActionEvent,\n Adapter,\n AdapterPostableMessage,\n ChannelInfo,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n ListThreadsOptions,\n ListThreadsResult,\n Logger,\n RawMessage,\n ThreadInfo,\n ThreadSummary,\n WebhookOptions,\n} from \"chat\";\nimport {\n ConsoleLogger,\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n getEmoji,\n Message,\n} from \"chat\";\nimport {\n Client,\n type Message as DiscordJsMessage,\n Events,\n GatewayIntentBits,\n Partials,\n} from \"discord.js\";\nimport { MessageType } from \"discord-api-types/v9\";\nimport {\n type APIEmbed,\n type APIMessage,\n ChannelType,\n InteractionType,\n} from \"discord-api-types/v10\";\nimport {\n InteractionResponseType as DiscordInteractionResponseType,\n verifyKey,\n} from \"discord-interactions\";\nimport { cardToDiscordPayload, cardToFallbackText } from \"./cards\";\nimport { DiscordFormatConverter } from \"./markdown\";\nimport {\n type DiscordActionRow,\n type DiscordAdapterConfig,\n type DiscordCommandOption,\n type DiscordForwardedEvent,\n type DiscordGatewayEventType,\n type DiscordGatewayMessageData,\n type DiscordGatewayReactionData,\n type DiscordInteraction,\n type DiscordInteractionResponse,\n type DiscordMessagePayload,\n type DiscordRequestContext,\n type DiscordSlashCommandContext,\n type DiscordThreadId,\n InteractionResponseType,\n} from \"./types\";\n\nconst DISCORD_API_BASE = \"https://discord.com/api/v10\";\nconst DISCORD_MAX_CONTENT_LENGTH = 2000;\nconst HEX_64_PATTERN = /^[0-9a-f]{64}$/;\nconst HEX_PATTERN = /^[0-9a-f]+$/;\n\nexport class DiscordAdapter implements Adapter<DiscordThreadId, unknown> {\n readonly name = \"discord\";\n readonly userName: string;\n readonly botUserId?: string;\n\n private readonly botToken: string;\n private readonly publicKey: string;\n private readonly applicationId: string;\n private readonly mentionRoleIds: string[];\n private chat: ChatInstance | null = null;\n private readonly logger: Logger;\n private readonly formatConverter = new DiscordFormatConverter();\n private readonly requestContext =\n new AsyncLocalStorage<DiscordRequestContext>();\n private readonly threadParentCache = new Map<\n string,\n { parentId: string; expiresAt: number }\n >();\n private static readonly THREAD_PARENT_CACHE_TTL = 5 * 60 * 1000;\n\n constructor(config: DiscordAdapterConfig = {}) {\n const botToken = config.botToken ?? process.env.DISCORD_BOT_TOKEN;\n if (!botToken) {\n throw new ValidationError(\n \"discord\",\n \"botToken is required. Set DISCORD_BOT_TOKEN or provide it in config.\"\n );\n }\n const publicKey = config.publicKey ?? process.env.DISCORD_PUBLIC_KEY;\n if (!publicKey) {\n throw new ValidationError(\n \"discord\",\n \"publicKey is required. Set DISCORD_PUBLIC_KEY or provide it in config.\"\n );\n }\n const applicationId =\n config.applicationId ?? process.env.DISCORD_APPLICATION_ID;\n if (!applicationId) {\n throw new ValidationError(\n \"discord\",\n \"applicationId is required. Set DISCORD_APPLICATION_ID or provide it in config.\"\n );\n }\n\n this.botToken = botToken;\n this.publicKey = publicKey.trim().toLowerCase();\n this.applicationId = applicationId;\n this.mentionRoleIds =\n config.mentionRoleIds ??\n (process.env.DISCORD_MENTION_ROLE_IDS\n ? process.env.DISCORD_MENTION_ROLE_IDS.split(\",\").map((id) => id.trim())\n : []);\n this.botUserId = applicationId; // Discord app ID is the bot's user ID\n this.logger = config.logger ?? new ConsoleLogger(\"info\").child(\"discord\");\n this.userName = config.userName ?? \"bot\";\n\n // Validate public key format\n if (!HEX_64_PATTERN.test(this.publicKey)) {\n this.logger.error(\"Invalid Discord public key format\", {\n length: this.publicKey.length,\n isHex: HEX_PATTERN.test(this.publicKey),\n });\n }\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n this.logger.info(\"Discord adapter initialized\");\n }\n\n /**\n * Handle incoming Discord webhook (HTTP Interactions or forwarded Gateway events).\n */\n async handleWebhook(\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n // Get raw body bytes for signature verification\n const bodyBuffer = await request.arrayBuffer();\n const bodyBytes = new Uint8Array(bodyBuffer);\n const body = new TextDecoder().decode(bodyBytes);\n\n // Check if this is a forwarded Gateway event (uses bot token for auth)\n const gatewayToken = request.headers.get(\"x-discord-gateway-token\");\n if (gatewayToken) {\n if (gatewayToken !== this.botToken) {\n this.logger.warn(\"Invalid gateway token\");\n return new Response(\"Invalid gateway token\", { status: 401 });\n }\n this.logger.info(\"Discord forwarded Gateway event received\");\n try {\n const event = JSON.parse(body) as DiscordForwardedEvent;\n return this.handleForwardedGatewayEvent(event, options);\n } catch {\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n }\n\n this.logger.info(\"Discord webhook received\", {\n bodyLength: body.length,\n bodyBytesLength: bodyBytes.length,\n hasSignature: !!request.headers.get(\"x-signature-ed25519\"),\n hasTimestamp: !!request.headers.get(\"x-signature-timestamp\"),\n });\n\n // Verify Ed25519 signature using raw bytes\n const signature = request.headers.get(\"x-signature-ed25519\");\n const timestamp = request.headers.get(\"x-signature-timestamp\");\n\n const signatureValid = await this.verifySignature(\n bodyBytes,\n signature,\n timestamp\n );\n if (!signatureValid) {\n this.logger.warn(\"Discord signature verification failed, returning 401\");\n return new Response(\"Invalid signature\", { status: 401 });\n }\n this.logger.info(\"Discord signature verification passed\");\n\n let interaction: DiscordInteraction;\n try {\n interaction = JSON.parse(body);\n } catch {\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n this.logger.info(\"Discord interaction parsed\", {\n type: interaction.type,\n typeIsPing: interaction.type === InteractionType.Ping,\n expectedPingType: InteractionType.Ping,\n id: interaction.id,\n });\n\n // Handle PING (Discord verification)\n if (interaction.type === InteractionType.Ping) {\n // Use official discord-interactions response type\n const responseBody = JSON.stringify({\n type: DiscordInteractionResponseType.PONG,\n });\n this.logger.info(\"Discord PING received, responding with PONG\", {\n responseBody,\n responseType: DiscordInteractionResponseType.PONG,\n });\n return new Response(responseBody, {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n // Handle MESSAGE_COMPONENT (button clicks)\n if (interaction.type === InteractionType.MessageComponent) {\n this.handleComponentInteraction(interaction, options);\n // ACK the interaction immediately\n return this.respondToInteraction({\n type: InteractionResponseType.DeferredUpdateMessage,\n });\n }\n\n // Handle APPLICATION_COMMAND (slash commands)\n if (interaction.type === InteractionType.ApplicationCommand) {\n this.handleApplicationCommandInteraction(interaction, options);\n return this.respondToInteraction({\n type: InteractionResponseType.DeferredChannelMessageWithSource,\n });\n }\n\n return new Response(\"Unknown interaction type\", { status: 400 });\n }\n\n /**\n * Verify Discord's Ed25519 signature using official discord-interactions library.\n */\n private async verifySignature(\n bodyBytes: Uint8Array,\n signature: string | null,\n timestamp: string | null\n ): Promise<boolean> {\n if (!(signature && timestamp)) {\n this.logger.warn(\n \"Discord signature verification failed: missing headers\",\n {\n hasSignature: !!signature,\n hasTimestamp: !!timestamp,\n }\n );\n return false;\n }\n\n try {\n // Log exactly what we're verifying\n this.logger.info(\"Discord signature verification attempt\", {\n bodyBytesLength: bodyBytes.length,\n signatureLength: signature.length,\n timestampLength: timestamp.length,\n publicKeyLength: this.publicKey.length,\n timestamp,\n signaturePrefix: signature.slice(0, 16),\n publicKey: this.publicKey,\n });\n\n // Use the official discord-interactions library for verification with raw bytes\n const isValid = await verifyKey(\n bodyBytes,\n signature,\n timestamp,\n this.publicKey\n );\n\n if (!isValid) {\n const bodyString = new TextDecoder().decode(bodyBytes);\n this.logger.warn(\n \"Discord signature verification failed: invalid signature\",\n {\n publicKeyLength: this.publicKey.length,\n signatureLength: signature.length,\n publicKeyPrefix: this.publicKey.slice(0, 8),\n publicKeySuffix: this.publicKey.slice(-8),\n timestamp,\n bodyLength: bodyBytes.length,\n bodyPrefix: bodyString.slice(0, 50),\n }\n );\n }\n\n return isValid;\n } catch (error) {\n this.logger.warn(\"Discord signature verification failed: exception\", {\n error,\n });\n return false;\n }\n }\n\n /**\n * Create a JSON response for Discord interactions.\n */\n private respondToInteraction(response: DiscordInteractionResponse): Response {\n return Response.json(response);\n }\n\n /**\n * Handle MESSAGE_COMPONENT interactions (button clicks).\n */\n private handleComponentInteraction(\n interaction: DiscordInteraction,\n options?: WebhookOptions\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring interaction\");\n return;\n }\n\n const customId = interaction.data?.custom_id;\n if (!customId) {\n this.logger.warn(\"No custom_id in component interaction\");\n return;\n }\n\n const user = interaction.member?.user || interaction.user;\n if (!user) {\n this.logger.warn(\"No user in component interaction\");\n return;\n }\n\n const interactionChannelId = interaction.channel_id;\n const guildId = interaction.guild_id || \"@me\";\n const messageId = interaction.message?.id;\n\n if (!(interactionChannelId && messageId)) {\n this.logger.warn(\"Missing channel_id or message_id in interaction\");\n return;\n }\n\n // Detect if the interaction is inside a thread channel\n // Discord channel types: 11 = public thread, 12 = private thread\n const channel = interaction.channel;\n const isThread = channel?.type === 11 || channel?.type === 12;\n const parentChannelId =\n isThread && channel?.parent_id ? channel.parent_id : interactionChannelId;\n\n const threadId = isThread\n ? this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: interactionChannelId,\n })\n : this.encodeThreadId({\n guildId,\n channelId: interactionChannelId,\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\" | \"openModal\"> & {\n adapter: DiscordAdapter;\n } = {\n actionId: customId,\n value: customId, // Discord custom_id often contains the value\n user: {\n userId: user.id,\n userName: user.username,\n fullName: user.global_name || user.username,\n isBot: user.bot ?? false,\n isMe: false,\n },\n messageId,\n threadId,\n adapter: this,\n raw: interaction,\n };\n\n this.logger.debug(\"Processing Discord button action\", {\n actionId: customId,\n messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n }\n\n /**\n * Handle APPLICATION_COMMAND interactions (slash commands).\n */\n private handleApplicationCommandInteraction(\n interaction: DiscordInteraction,\n options?: WebhookOptions\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring interaction\");\n return;\n }\n\n const commandName = interaction.data?.name;\n if (!commandName) {\n this.logger.warn(\"No command name in application command interaction\");\n return;\n }\n\n const user = interaction.member?.user || interaction.user;\n if (!user) {\n this.logger.warn(\"No user in application command interaction\");\n return;\n }\n\n const interactionChannelId = interaction.channel_id;\n if (!interactionChannelId) {\n this.logger.warn(\"Missing channel_id in application command interaction\");\n return;\n }\n\n const guildId = interaction.guild_id || \"@me\";\n const channel = interaction.channel;\n const isThread = channel?.type === 11 || channel?.type === 12;\n const parentChannelId =\n isThread && channel?.parent_id ? channel.parent_id : interactionChannelId;\n\n const channelId = isThread\n ? this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: interactionChannelId,\n })\n : this.encodeThreadId({\n guildId,\n channelId: interactionChannelId,\n });\n\n const { command, text } = this.parseSlashCommand(\n commandName,\n interaction.data?.options\n );\n\n this.logger.debug(\"Processing Discord slash command\", {\n command,\n text,\n userId: user.id,\n channelId,\n });\n\n // Keep interaction metadata in AsyncLocalStorage so event.channel.post(...)\n // can resolve Discord's deferred \"thinking...\" response natively.\n this.requestContext.run(\n {\n slashCommand: {\n channelId,\n interactionToken: interaction.token,\n initialResponseSent: false,\n },\n },\n () => {\n this.chat?.processSlashCommand(\n {\n command,\n text,\n user: {\n userId: user.id,\n userName: user.username,\n fullName: user.global_name || user.username,\n isBot: user.bot ?? false,\n isMe: user.id === this.applicationId,\n },\n adapter: this as Adapter,\n raw: interaction,\n channelId,\n },\n options\n );\n }\n );\n }\n\n /**\n * Parse a Discord slash command into a full command path and flat text.\n *\n * Subcommand and subcommand-group names are appended to the command path\n * so `/project issue create --title=\"Login fails\"` becomes:\n * command = \"/project issue create\"\n * text = \"Login fails\"\n *\n * Leaf option values are flattened into `text`. Consumers needing the full\n * option tree (names, types) can use `event.raw`.\n */\n private parseSlashCommand(\n name: string,\n options?: DiscordCommandOption[]\n ): { command: string; text: string } {\n const commandParts: string[] = [name.startsWith(\"/\") ? name : `/${name}`];\n const valueParts: string[] = [];\n\n const collect = (items: DiscordCommandOption[]): void => {\n for (const option of items) {\n if (option.value !== undefined) {\n valueParts.push(String(option.value));\n continue;\n }\n // Subcommand or subcommand-group — append name to command path\n if (option.options && option.options.length > 0) {\n commandParts.push(option.name);\n collect(option.options);\n }\n }\n };\n\n if (options && options.length > 0) {\n collect(options);\n }\n\n return {\n command: commandParts.join(\" \"),\n text: valueParts.join(\" \").trim(),\n };\n }\n\n /**\n * Handle a forwarded Gateway event received via webhook.\n */\n private async handleForwardedGatewayEvent(\n event: DiscordForwardedEvent,\n options?: WebhookOptions\n ): Promise<Response> {\n this.logger.info(\"Processing forwarded Gateway event\", {\n type: event.type,\n timestamp: event.timestamp,\n });\n\n switch (event.type) {\n case \"GATEWAY_MESSAGE_CREATE\":\n await this.handleForwardedMessage(\n event.data as DiscordGatewayMessageData,\n options\n );\n break;\n case \"GATEWAY_MESSAGE_REACTION_ADD\":\n await this.handleForwardedReaction(\n event.data as DiscordGatewayReactionData,\n true,\n options\n );\n break;\n case \"GATEWAY_MESSAGE_REACTION_REMOVE\":\n await this.handleForwardedReaction(\n event.data as DiscordGatewayReactionData,\n false,\n options\n );\n break;\n default:\n // Other Gateway events are forwarded but not processed - this is expected\n this.logger.debug(\"Forwarded Gateway event (no handler)\", {\n type: event.type,\n });\n }\n\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n\n /**\n * Handle a forwarded MESSAGE_CREATE event.\n */\n private async handleForwardedMessage(\n data: DiscordGatewayMessageData,\n _options?: WebhookOptions\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = data.guild_id || \"@me\";\n const channelId = data.channel_id;\n\n // Use thread info if provided, otherwise use channel\n let discordThreadId: string | undefined;\n let parentChannelId = channelId;\n\n if (data.thread) {\n discordThreadId = data.thread.id;\n parentChannelId = data.thread.parent_id;\n } else if (data.channel_type === 11 || data.channel_type === 12) {\n // Message is in a thread (11 = public, 12 = private) but we don't have parent info\n // Fetch the channel to get parent_id\n try {\n const response = await this.discordFetch(\n `/channels/${channelId}`,\n \"GET\"\n );\n const channel = (await response.json()) as { parent_id?: string };\n if (channel.parent_id) {\n discordThreadId = channelId;\n parentChannelId = channel.parent_id;\n this.logger.debug(\"Fetched thread parent for forwarded message\", {\n threadId: channelId,\n parentId: channel.parent_id,\n });\n }\n } catch (error) {\n this.logger.error(\"Failed to fetch thread parent\", {\n error: String(error),\n channelId,\n });\n }\n }\n\n // Check if bot is mentioned (by user ID or configured role IDs)\n const isUserMentioned =\n data.is_mention || data.mentions.some((m) => m.id === this.applicationId);\n const isRoleMentioned =\n this.mentionRoleIds.length > 0 &&\n data.mention_roles?.some((roleId) =>\n this.mentionRoleIds.includes(roleId)\n );\n const isMentioned = isUserMentioned || isRoleMentioned;\n\n // If mentioned and not in a thread, create one\n if (!discordThreadId && isMentioned) {\n try {\n const newThread = await this.createDiscordThread(channelId, data.id);\n discordThreadId = newThread.id;\n this.logger.debug(\"Created Discord thread for forwarded mention\", {\n channelId,\n messageId: data.id,\n threadId: newThread.id,\n });\n } catch (error) {\n this.logger.error(\"Failed to create Discord thread for mention\", {\n error: String(error),\n messageId: data.id,\n });\n }\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Convert to SDK Message format\n const chatMessage = new Message({\n id: data.id,\n threadId,\n text: data.content,\n formatted: this.formatConverter.toAst(data.content),\n author: {\n userId: data.author.id,\n userName: data.author.username,\n fullName: data.author.global_name || data.author.username,\n isBot: data.author.bot === true, // Discord returns null for non-bots\n isMe: data.author.id === this.applicationId,\n },\n metadata: {\n dateSent: new Date(data.timestamp),\n edited: false,\n },\n attachments: data.attachments.map((a) => ({\n type: this.getAttachmentType(a.content_type),\n url: a.url,\n name: a.filename,\n mimeType: a.content_type,\n size: a.size,\n })),\n raw: data,\n isMention: isMentioned,\n });\n\n try {\n await this.chat.handleIncomingMessage(this, threadId, chatMessage);\n } catch (error) {\n this.logger.error(\"Error handling forwarded message\", {\n error: String(error),\n messageId: data.id,\n });\n }\n }\n\n /**\n * Handle a forwarded REACTION_ADD or REACTION_REMOVE event.\n */\n private async handleForwardedReaction(\n data: DiscordGatewayReactionData,\n added: boolean,\n _options?: WebhookOptions\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = data.guild_id || \"@me\";\n const channelId = data.channel_id;\n\n // Check if reaction is in a thread channel\n let discordThreadId: string | undefined;\n let parentChannelId = channelId;\n\n if (\n data.channel_type === ChannelType.GuildPublicThread ||\n data.channel_type === ChannelType.GuildPrivateThread\n ) {\n const cached = this.threadParentCache.get(channelId);\n if (cached && cached.expiresAt > Date.now()) {\n discordThreadId = channelId;\n parentChannelId = cached.parentId;\n } else {\n try {\n const response = await this.discordFetch(\n `/channels/${channelId}`,\n \"GET\"\n );\n const channel = (await response.json()) as { parent_id?: string };\n if (channel.parent_id) {\n discordThreadId = channelId;\n parentChannelId = channel.parent_id;\n this.threadParentCache.set(channelId, {\n parentId: channel.parent_id,\n expiresAt: Date.now() + DiscordAdapter.THREAD_PARENT_CACHE_TTL,\n });\n }\n } catch (error) {\n this.logger.error(\"Failed to fetch thread parent for reaction\", {\n error: String(error),\n channelId,\n });\n }\n }\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Normalize emoji\n const emojiName = data.emoji.name || \"unknown\";\n const normalizedEmoji = this.normalizeDiscordEmoji(emojiName);\n\n // Get user info from either data.user (DMs) or data.member.user (guilds)\n const userInfo = data.user ?? data.member?.user;\n if (!userInfo) {\n this.logger.warn(\"Reaction event missing user info\", { data });\n return;\n }\n\n const reactionEvent = {\n adapter: this as Adapter,\n threadId,\n messageId: data.message_id,\n emoji: normalizedEmoji,\n rawEmoji: data.emoji.id ? `<:${emojiName}:${data.emoji.id}>` : emojiName,\n added,\n user: {\n userId: userInfo.id,\n userName: userInfo.username,\n fullName: userInfo.username,\n isBot: userInfo.bot === true, // Discord returns null for non-bots\n isMe: userInfo.id === this.applicationId,\n },\n raw: data,\n };\n\n this.chat.processReaction(reactionEvent);\n }\n\n /**\n * Post a message to a Discord channel or thread.\n */\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n let { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const actualThreadId = threadId;\n\n // If in a thread, post to the thread channel\n if (discordThreadId) {\n channelId = discordThreadId;\n }\n\n // Build message payload\n const payload: DiscordMessagePayload = {};\n const embeds: APIEmbed[] = [];\n const components: DiscordActionRow[] = [];\n\n // Check for card\n const card = extractCard(message);\n if (card) {\n const cardPayload = cardToDiscordPayload(card);\n embeds.push(...cardPayload.embeds);\n components.push(...cardPayload.components);\n // Fallback text (truncated to Discord's limit)\n payload.content = this.truncateContent(cardToFallbackText(card));\n } else {\n // Regular text message (truncated to Discord's limit)\n payload.content = this.truncateContent(\n convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"discord\"\n )\n );\n }\n\n if (embeds.length > 0) {\n payload.embeds = embeds;\n }\n if (components.length > 0) {\n payload.components = components;\n }\n\n // Handle file uploads\n const files = extractFiles(message);\n const slashResponse = this.tryPostSlashResponse(\n actualThreadId,\n payload,\n files\n );\n if (slashResponse) {\n return slashResponse;\n }\n if (files.length > 0) {\n return this.postMessageWithFiles(\n channelId,\n actualThreadId,\n payload,\n files\n );\n }\n\n this.logger.debug(\"Discord API: POST message\", {\n channelId,\n contentLength: payload.content?.length || 0,\n embedCount: embeds.length,\n componentCount: components.length,\n });\n\n const response = await this.discordFetch(\n `/channels/${channelId}/messages`,\n \"POST\",\n payload\n );\n\n const result = (await response.json()) as APIMessage;\n\n this.logger.debug(\"Discord API: POST message response\", {\n messageId: result.id,\n });\n\n return {\n id: result.id,\n threadId: actualThreadId,\n raw: result,\n };\n }\n\n private tryPostSlashResponse(\n threadId: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<RawMessage<unknown>> | undefined {\n const slashContext = this.requestContext.getStore()?.slashCommand;\n if (!slashContext || slashContext.channelId !== threadId) {\n return undefined;\n }\n return this.postSlashCommandResponse(\n slashContext,\n threadId,\n payload,\n files\n );\n }\n\n private async postSlashCommandResponse(\n slashContext: DiscordSlashCommandContext,\n threadId: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<RawMessage<unknown>> {\n const isInitialResponse = !slashContext.initialResponseSent;\n // Set flag before awaiting to prevent concurrent post() calls from both\n // trying to PATCH @original instead of the second being a followup POST.\n slashContext.initialResponseSent = true;\n\n const path = isInitialResponse\n ? `/webhooks/${this.applicationId}/${slashContext.interactionToken}/messages/@original`\n : `/webhooks/${this.applicationId}/${slashContext.interactionToken}?wait=true`;\n const method = isInitialResponse ? \"PATCH\" : \"POST\";\n\n this.logger.debug(\n \"Discord interaction webhook: responding to slash command\",\n {\n threadId,\n isInitialResponse,\n hasFiles: files.length > 0,\n }\n );\n\n const response =\n files.length > 0\n ? await this.discordInteractionFetchWithFiles(\n path,\n method,\n payload,\n files\n )\n : await this.discordInteractionFetch(path, method, payload);\n\n const result = (await response.json()) as APIMessage;\n\n return {\n id: result.id,\n threadId,\n raw: result,\n };\n }\n\n /**\n * Create a Discord thread from a message.\n */\n private async createDiscordThread(\n channelId: string,\n messageId: string\n ): Promise<{ id: string; name: string }> {\n const threadName = `Thread ${new Date().toLocaleString()}`;\n\n this.logger.debug(\"Discord API: POST thread\", {\n channelId,\n messageId,\n threadName,\n });\n\n const response = await this.discordFetch(\n `/channels/${channelId}/messages/${messageId}/threads`,\n \"POST\",\n {\n name: threadName,\n auto_archive_duration: 1440, // 24 hours\n }\n );\n\n const result = (await response.json()) as { id: string; name: string };\n\n this.logger.debug(\"Discord API: POST thread response\", {\n threadId: result.id,\n threadName: result.name,\n });\n\n return result;\n }\n\n /**\n * Truncate content to Discord's maximum length.\n */\n private truncateContent(content: string): string {\n if (content.length <= DISCORD_MAX_CONTENT_LENGTH) {\n return content;\n }\n // Truncate and add ellipsis\n return `${content.slice(0, DISCORD_MAX_CONTENT_LENGTH - 3)}...`;\n }\n\n /**\n * Post a message with file attachments.\n */\n private async postMessageWithFiles(\n channelId: string,\n threadId: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<RawMessage<unknown>> {\n const formData = new FormData();\n\n // Add JSON payload\n formData.append(\"payload_json\", JSON.stringify(payload));\n\n // Add files\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const buffer = await toBuffer(file.data, {\n platform: \"discord\" as \"slack\",\n });\n if (!buffer) {\n continue;\n }\n const blob = new Blob([new Uint8Array(buffer)], {\n type: file.mimeType || \"application/octet-stream\",\n });\n formData.append(`files[${i}]`, blob, file.filename);\n }\n\n const response = await fetch(\n `${DISCORD_API_BASE}/channels/${channelId}/messages`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bot ${this.botToken}`,\n },\n body: formData,\n }\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new NetworkError(\n \"discord\",\n `Failed to post message: ${response.status} ${error}`\n );\n }\n\n const result = (await response.json()) as APIMessage;\n\n return {\n id: result.id,\n threadId,\n raw: result,\n };\n }\n\n private async discordInteractionFetch(\n path: string,\n method: string,\n body?: unknown\n ): Promise<Response> {\n const response = await fetch(`${DISCORD_API_BASE}${path}`, {\n method,\n headers: body ? { \"Content-Type\": \"application/json\" } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.logger.error(\"Discord interaction API error\", {\n path,\n method,\n status: response.status,\n error: errorText,\n });\n throw new NetworkError(\n \"discord\",\n `Discord interaction API error: ${response.status} ${errorText}`\n );\n }\n\n return response;\n }\n\n private async discordInteractionFetchWithFiles(\n path: string,\n method: string,\n payload: DiscordMessagePayload,\n files: Array<{\n filename: string;\n data: Buffer | Blob | ArrayBuffer;\n mimeType?: string;\n }>\n ): Promise<Response> {\n const formData = new FormData();\n formData.append(\"payload_json\", JSON.stringify(payload));\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const buffer = await toBuffer(file.data, {\n platform: \"discord\",\n });\n if (!buffer) {\n continue;\n }\n const blob = new Blob([new Uint8Array(buffer)], {\n type: file.mimeType || \"application/octet-stream\",\n });\n formData.append(`files[${i}]`, blob, file.filename);\n }\n\n const response = await fetch(`${DISCORD_API_BASE}${path}`, {\n method,\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.logger.error(\"Discord interaction API error\", {\n path,\n method,\n status: response.status,\n error: errorText,\n });\n throw new NetworkError(\n \"discord\",\n `Discord interaction API error: ${response.status} ${errorText}`\n );\n }\n\n return response;\n }\n\n /**\n * Edit an existing Discord message.\n */\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n // Use thread channel ID if in a thread, otherwise use channel ID\n const targetChannelId = discordThreadId || channelId;\n\n // Build message payload\n const payload: DiscordMessagePayload = {};\n const embeds: APIEmbed[] = [];\n const components: DiscordActionRow[] = [];\n\n // Check for card\n const card = extractCard(message);\n if (card) {\n const cardPayload = cardToDiscordPayload(card);\n embeds.push(...cardPayload.embeds);\n components.push(...cardPayload.components);\n // Fallback text (truncated to Discord's limit)\n payload.content = this.truncateContent(cardToFallbackText(card));\n } else {\n // Regular text message (truncated to Discord's limit)\n payload.content = this.truncateContent(\n convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"discord\"\n )\n );\n }\n\n if (embeds.length > 0) {\n payload.embeds = embeds;\n }\n if (components.length > 0) {\n payload.components = components;\n }\n\n this.logger.debug(\"Discord API: PATCH message\", {\n channelId: targetChannelId,\n messageId,\n contentLength: payload.content?.length || 0,\n });\n\n const response = await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}`,\n \"PATCH\",\n payload\n );\n\n const result = (await response.json()) as APIMessage;\n\n this.logger.debug(\"Discord API: PATCH message response\", {\n messageId: result.id,\n });\n\n return {\n id: result.id,\n threadId,\n raw: result,\n };\n }\n\n /**\n * Delete a Discord message.\n */\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const targetChannelId = discordThreadId || channelId;\n\n this.logger.debug(\"Discord API: DELETE message\", {\n channelId: targetChannelId,\n messageId,\n });\n\n await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}`,\n \"DELETE\"\n );\n\n this.logger.debug(\"Discord API: DELETE message response\", { ok: true });\n }\n\n /**\n * Add a reaction to a Discord message.\n */\n async addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const targetChannelId = discordThreadId || channelId;\n const emojiEncoded = this.encodeEmoji(emoji);\n\n this.logger.debug(\"Discord API: PUT reaction\", {\n channelId: targetChannelId,\n messageId,\n emoji: emojiEncoded,\n });\n\n await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}/reactions/${emojiEncoded}/@me`,\n \"PUT\"\n );\n\n this.logger.debug(\"Discord API: PUT reaction response\", { ok: true });\n }\n\n /**\n * Remove a reaction from a Discord message.\n */\n async removeReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n const targetChannelId = discordThreadId || channelId;\n const emojiEncoded = this.encodeEmoji(emoji);\n\n this.logger.debug(\"Discord API: DELETE reaction\", {\n channelId: targetChannelId,\n messageId,\n emoji: emojiEncoded,\n });\n\n await this.discordFetch(\n `/channels/${targetChannelId}/messages/${messageId}/reactions/${emojiEncoded}/@me`,\n \"DELETE\"\n );\n\n this.logger.debug(\"Discord API: DELETE reaction response\", { ok: true });\n }\n\n /**\n * Encode an emoji for use in Discord API URLs.\n */\n private encodeEmoji(emoji: EmojiValue | string): string {\n const emojiStr = defaultEmojiResolver.toDiscord\n ? defaultEmojiResolver.toDiscord(emoji)\n : String(emoji);\n // URL-encode the emoji for the API path\n return encodeURIComponent(emojiStr);\n }\n\n /**\n * Start typing indicator in a Discord channel or thread.\n */\n async startTyping(threadId: string, _status?: string): Promise<void> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n // Use thread channel ID if in a thread, otherwise use channel ID\n const targetChannelId = discordThreadId || channelId;\n\n this.logger.debug(\"Discord API: POST typing\", {\n channelId: targetChannelId,\n });\n\n await this.discordFetch(`/channels/${targetChannelId}/typing`, \"POST\");\n }\n\n /**\n * Fetch messages from a Discord channel or thread.\n * If threadId includes a Discord thread ID, fetches from that thread channel.\n */\n async fetchMessages(\n threadId: string,\n options: FetchOptions = {}\n ): Promise<FetchResult<unknown>> {\n const { channelId, threadId: discordThreadId } =\n this.decodeThreadId(threadId);\n // Use thread channel ID if in a thread, otherwise use channel ID\n const targetChannelId = discordThreadId || channelId;\n\n const limit = options.limit || 50;\n const direction = options.direction ?? \"backward\";\n\n const params = new URLSearchParams();\n params.set(\"limit\", String(limit));\n\n // Handle pagination cursor\n if (options.cursor) {\n if (direction === \"backward\") {\n params.set(\"before\", options.cursor);\n } else {\n params.set(\"after\", options.cursor);\n }\n }\n\n this.logger.debug(\"Discord API: GET messages\", {\n channelId: targetChannelId,\n limit,\n direction,\n cursor: options.cursor,\n });\n\n const response = await this.discordFetch(\n `/channels/${targetChannelId}/messages?${params.toString()}`,\n \"GET\"\n );\n\n const rawMessages = (await response.json()) as APIMessage[];\n\n this.logger.debug(\"Discord API: GET messages response\", {\n messageCount: rawMessages.length,\n });\n\n // Discord returns messages in reverse chronological order (newest first)\n // For consistency, reverse to chronological order (oldest first)\n const sortedMessages = [...rawMessages].reverse();\n\n const messages = sortedMessages.map((msg) =>\n this.parseDiscordMessage(msg, threadId)\n );\n\n // Determine next cursor\n let nextCursor: string | undefined;\n if (rawMessages.length === limit) {\n if (direction === \"backward\") {\n // For backward, cursor is the oldest message ID in the batch\n const oldest = rawMessages.at(-1);\n nextCursor = oldest?.id;\n } else {\n // For forward, cursor is the newest message ID in the batch\n const newest = rawMessages[0];\n nextCursor = newest?.id;\n }\n }\n\n return {\n messages,\n nextCursor,\n };\n }\n\n /**\n * Fetch thread/channel information.\n */\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { channelId, guildId } = this.decodeThreadId(threadId);\n\n this.logger.debug(\"Discord API: GET channel\", { channelId });\n\n const response = await this.discordFetch(`/channels/${channelId}`, \"GET\");\n const channel = (await response.json()) as {\n id: string;\n name?: string;\n type: ChannelType;\n };\n\n return {\n id: threadId,\n channelId,\n channelName: channel.name,\n isDM:\n channel.type === ChannelType.DM || channel.type === ChannelType.GroupDM,\n metadata: {\n guildId,\n channelType: channel.type,\n raw: channel,\n },\n };\n }\n\n /**\n * Open a DM with a user.\n */\n async openDM(userId: string): Promise<string> {\n this.logger.debug(\"Discord API: POST DM channel\", { userId });\n\n const response = await this.discordFetch(\"/users/@me/channels\", \"POST\", {\n recipient_id: userId,\n });\n\n const dmChannel = (await response.json()) as {\n id: string;\n type: ChannelType;\n };\n\n this.logger.debug(\"Discord API: POST DM channel response\", {\n channelId: dmChannel.id,\n });\n\n return this.encodeThreadId({\n guildId: \"@me\",\n channelId: dmChannel.id,\n });\n }\n\n /**\n * Check if a thread is a DM.\n */\n isDM(threadId: string): boolean {\n const { guildId } = this.decodeThreadId(threadId);\n return guildId === \"@me\";\n }\n\n /**\n * Encode platform data into a thread ID string.\n */\n encodeThreadId(platformData: DiscordThreadId): string {\n const threadPart = platformData.threadId ? `:${platformData.threadId}` : \"\";\n return `discord:${platformData.guildId}:${platformData.channelId}${threadPart}`;\n }\n\n /**\n * Decode thread ID string back to platform data.\n */\n decodeThreadId(threadId: string): DiscordThreadId {\n const parts = threadId.split(\":\");\n if (parts.length < 3 || parts[0] !== \"discord\") {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord thread ID: ${threadId}`\n );\n }\n\n return {\n guildId: parts[1] as string,\n channelId: parts[2] as string,\n threadId: parts[3],\n };\n }\n\n /**\n * Parse a Discord message into normalized format.\n */\n parseMessage(raw: unknown): Message<unknown> {\n const msg = raw as APIMessage & { guild_id?: string };\n const guildId = msg.guild_id || \"@me\";\n const threadId = this.encodeThreadId({\n guildId,\n channelId: msg.channel_id,\n });\n return this.parseDiscordMessage(msg, threadId);\n }\n\n /**\n * Parse a Discord API message into normalized format.\n */\n private parseDiscordMessage(\n raw: APIMessage,\n threadId: string\n ): Message<unknown> {\n // use original message instead of empty thread starter message if available\n const msg =\n raw.type === MessageType.ThreadStarterMessage && raw.referenced_message\n ? raw.referenced_message\n : raw;\n\n const author = msg.author;\n const isBot = author.bot ?? false;\n const isMe = author.id === this.botUserId;\n\n return new Message({\n id: msg.id,\n threadId,\n text: this.formatConverter.extractPlainText(msg.content),\n formatted: this.formatConverter.toAst(msg.content),\n raw,\n author: {\n userId: author.id,\n userName: author.username,\n fullName: author.global_name || author.username,\n isBot,\n isMe,\n },\n metadata: {\n dateSent: new Date(msg.timestamp),\n edited: msg.edited_timestamp !== null,\n editedAt: msg.edited_timestamp\n ? new Date(msg.edited_timestamp)\n : undefined,\n },\n attachments: (msg.attachments || []).map((att) => ({\n type: this.getAttachmentType(att.content_type),\n url: att.url,\n name: att.filename,\n mimeType: att.content_type,\n size: att.size,\n width: att.width ?? undefined,\n height: att.height ?? undefined,\n })),\n });\n }\n\n /**\n * Determine attachment type from MIME type.\n */\n private getAttachmentType(\n mimeType?: string | null\n ): \"image\" | \"video\" | \"audio\" | \"file\" {\n if (!mimeType) {\n return \"file\";\n }\n if (mimeType.startsWith(\"image/\")) {\n return \"image\";\n }\n if (mimeType.startsWith(\"video/\")) {\n return \"video\";\n }\n if (mimeType.startsWith(\"audio/\")) {\n return \"audio\";\n }\n return \"file\";\n }\n\n /**\n * Render formatted content to Discord markdown.\n */\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n\n /**\n * Make a request to the Discord API.\n */\n private async discordFetch(\n path: string,\n method: string,\n body?: unknown\n ): Promise<Response> {\n const url = `${DISCORD_API_BASE}${path}`;\n const headers: Record<string, string> = {\n Authorization: `Bot ${this.botToken}`,\n };\n\n if (body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n this.logger.error(\"Discord API error\", {\n path,\n method,\n status: response.status,\n error: errorText,\n });\n throw new NetworkError(\n \"discord\",\n `Discord API error: ${response.status} ${errorText}`\n );\n }\n\n return response;\n }\n\n /**\n * Start Gateway WebSocket listener for receiving messages/mentions.\n * Uses waitUntil to keep the connection alive for the specified duration.\n *\n * This is a workaround for serverless environments - the Gateway connection\n * will stay alive for the duration, listening for messages.\n *\n * @param options - Webhook options with waitUntil function\n * @param durationMs - How long to keep listening (default: 180000ms = 3 minutes)\n * @param abortSignal - Optional AbortSignal to stop the listener early (e.g., when a new listener starts)\n * @param webhookUrl - URL to forward Gateway events to (required for webhook forwarding mode)\n * @returns Response indicating the listener was started\n */\n async startGatewayListener(\n options: WebhookOptions,\n durationMs = 180000,\n abortSignal?: AbortSignal,\n webhookUrl?: string\n ): Promise<Response> {\n if (!this.chat) {\n return new Response(\"Chat instance not initialized\", { status: 500 });\n }\n\n if (!options.waitUntil) {\n return new Response(\"waitUntil not provided\", { status: 500 });\n }\n\n this.logger.info(\"Starting Discord Gateway listener\", {\n durationMs,\n webhookUrl: webhookUrl ? \"configured\" : \"not configured\",\n });\n\n // Create a promise that resolves after the duration\n const listenerPromise = this.runGatewayListener(\n durationMs,\n abortSignal,\n webhookUrl\n );\n\n // Use waitUntil to keep the function alive\n options.waitUntil(listenerPromise);\n\n return new Response(\n JSON.stringify({\n status: \"listening\",\n durationMs,\n message: `Gateway listener started, will run for ${durationMs / 1000} seconds`,\n }),\n {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n }\n );\n }\n\n /**\n * Run the Gateway listener for a specified duration.\n */\n private async runGatewayListener(\n durationMs: number,\n abortSignal?: AbortSignal,\n webhookUrl?: string\n ): Promise<void> {\n const client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n GatewayIntentBits.GuildMessageReactions,\n GatewayIntentBits.DirectMessageReactions,\n ],\n partials: [Partials.Channel],\n });\n\n let isShuttingDown = false;\n\n // When webhookUrl is provided, use raw forwarding for ALL events\n // This keeps the Gateway simple - all processing happens in the webhook\n if (webhookUrl) {\n client.on(\"raw\", async (packet: { t: string | null; d: unknown }) => {\n if (isShuttingDown) {\n return;\n }\n if (!packet.t) {\n return; // Skip heartbeats and other non-dispatch events\n }\n\n this.logger.info(\"Discord Gateway forwarding event\", {\n type: packet.t,\n });\n\n // Forward to webhook\n await this.forwardGatewayEvent(webhookUrl, {\n type: `GATEWAY_${packet.t}` as DiscordGatewayEventType,\n timestamp: Date.now(),\n data: packet.d,\n });\n });\n } else {\n // Legacy mode: handle events directly without webhook forwarding\n this.setupLegacyGatewayHandlers(client, () => isShuttingDown);\n }\n\n client.on(Events.ClientReady, () => {\n this.logger.info(\"Discord Gateway connected\", {\n username: client.user?.username,\n id: client.user?.id,\n });\n });\n\n client.on(Events.Error, (error) => {\n this.logger.error(\"Discord Gateway error\", { error: String(error) });\n });\n\n try {\n // Login to Discord\n await client.login(this.botToken);\n\n // Wait for either: duration timeout OR abort signal\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(resolve, durationMs);\n\n // Listen for abort signal (e.g., when a new listener starts)\n if (abortSignal) {\n if (abortSignal.aborted) {\n clearTimeout(timeout);\n resolve();\n return;\n }\n abortSignal.addEventListener(\n \"abort\",\n () => {\n this.logger.info(\n \"Discord Gateway listener received abort signal (new listener started)\"\n );\n clearTimeout(timeout);\n resolve();\n },\n { once: true }\n );\n }\n });\n\n this.logger.info(\n \"Discord Gateway listener duration elapsed, disconnecting\"\n );\n } catch (error) {\n this.logger.error(\"Discord Gateway listener error\", {\n error: String(error),\n });\n } finally {\n isShuttingDown = true;\n client.destroy();\n this.logger.info(\"Discord Gateway listener stopped\");\n }\n }\n\n /**\n * Set up legacy Gateway handlers for direct processing (when webhookUrl is not provided).\n */\n private setupLegacyGatewayHandlers(\n client: Client,\n isShuttingDown: () => boolean\n ): void {\n // Message handler\n client.on(Events.MessageCreate, async (message: DiscordJsMessage) => {\n if (isShuttingDown()) {\n this.logger.debug(\"Ignoring message - Gateway is shutting down\");\n return;\n }\n\n // Ignore messages from bots (including ourselves)\n if (message.author.bot) {\n this.logger.debug(\"Ignoring message from bot\", {\n authorId: message.author.id,\n authorName: message.author.username,\n isMe: message.author.id === client.user?.id,\n });\n return;\n }\n\n // Check if we're mentioned (by user ID or configured role IDs)\n const isUserMentioned = message.mentions.has(client.user?.id ?? \"\");\n const isRoleMentioned =\n this.mentionRoleIds.length > 0 &&\n message.mentions.roles.some((role) =>\n this.mentionRoleIds.includes(role.id)\n );\n const isMentioned = isUserMentioned || isRoleMentioned;\n\n this.logger.info(\"Discord Gateway message received\", {\n channelId: message.channelId,\n guildId: message.guildId,\n authorId: message.author.id,\n isMentioned,\n isUserMentioned,\n isRoleMentioned,\n content: message.content.slice(0, 100),\n });\n\n // Process the message directly\n await this.handleGatewayMessage(message, isMentioned);\n });\n\n // Reaction add handler\n client.on(Events.MessageReactionAdd, async (reaction, user) => {\n if (isShuttingDown()) {\n this.logger.debug(\"Ignoring reaction - Gateway is shutting down\");\n return;\n }\n\n // Ignore reactions from bots (including ourselves)\n if (user.bot) {\n this.logger.debug(\"Ignoring reaction from bot\", {\n userId: user.id,\n isMe: user.id === client.user?.id,\n });\n return;\n }\n\n this.logger.info(\"Discord Gateway reaction added\", {\n emoji: reaction.emoji.name,\n messageId: reaction.message.id,\n channelId: reaction.message.channelId,\n userId: user.id,\n });\n\n // Process the reaction (skip partial users without username)\n if (user.username) {\n await this.handleGatewayReaction(\n reaction,\n user as { id: string; username: string; bot: boolean },\n true\n );\n }\n });\n\n // Reaction remove handler\n client.on(Events.MessageReactionRemove, async (reaction, user) => {\n if (isShuttingDown()) {\n this.logger.debug(\n \"Ignoring reaction removal - Gateway is shutting down\"\n );\n return;\n }\n\n // Ignore reactions from bots (including ourselves)\n if (user.bot) {\n this.logger.debug(\"Ignoring reaction removal from bot\", {\n userId: user.id,\n isMe: user.id === client.user?.id,\n });\n return;\n }\n\n this.logger.info(\"Discord Gateway reaction removed\", {\n emoji: reaction.emoji.name,\n messageId: reaction.message.id,\n channelId: reaction.message.channelId,\n userId: user.id,\n });\n\n // Process the reaction (skip partial users without username)\n if (user.username) {\n await this.handleGatewayReaction(\n reaction,\n user as { id: string; username: string; bot: boolean },\n false\n );\n }\n });\n }\n\n /**\n * Forward a Gateway event to the webhook endpoint.\n */\n private async forwardGatewayEvent(\n webhookUrl: string,\n event: DiscordForwardedEvent\n ): Promise<void> {\n try {\n this.logger.debug(\"Forwarding Gateway event to webhook\", {\n type: event.type,\n webhookUrl,\n });\n\n const response = await fetch(webhookUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-discord-gateway-token\": this.botToken,\n },\n body: JSON.stringify(event),\n });\n\n if (response.ok) {\n this.logger.debug(\"Gateway event forwarded successfully\", {\n type: event.type,\n });\n } else {\n const errorText = await response.text();\n this.logger.error(\"Failed to forward Gateway event\", {\n type: event.type,\n status: response.status,\n error: errorText,\n });\n }\n } catch (error) {\n this.logger.error(\"Error forwarding Gateway event\", {\n type: event.type,\n error: String(error),\n });\n }\n }\n\n /**\n * Handle a message received via the Gateway WebSocket.\n */\n private async handleGatewayMessage(\n message: DiscordJsMessage,\n isMentioned: boolean\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = message.guildId || \"@me\";\n const channelId = message.channelId;\n\n // Check if this message is in a Discord Thread channel\n const isInThread = message.channel.isThread();\n let discordThreadId: string | undefined;\n let parentChannelId = channelId;\n\n if (\n isInThread &&\n \"parentId\" in message.channel &&\n message.channel.parentId\n ) {\n // Message is in a thread - use the thread channel ID\n discordThreadId = channelId;\n parentChannelId = message.channel.parentId;\n }\n\n // If not in a thread and bot is mentioned, create a thread immediately\n // This ensures the Thread object has the correct ID from the start\n if (!discordThreadId && isMentioned) {\n try {\n const newThread = await this.createDiscordThread(channelId, message.id);\n discordThreadId = newThread.id;\n this.logger.debug(\"Created Discord thread for incoming mention\", {\n channelId,\n messageId: message.id,\n threadId: newThread.id,\n });\n } catch (error) {\n this.logger.error(\"Failed to create Discord thread for mention\", {\n error: String(error),\n messageId: message.id,\n });\n // Continue without thread - will use channel\n }\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Convert discord.js message to our Message format\n const chatMessage = new Message({\n id: message.id,\n threadId,\n text: message.content,\n formatted: this.formatConverter.toAst(message.content),\n author: {\n userId: message.author.id,\n userName: message.author.username,\n fullName: message.author.displayName || message.author.username,\n isBot: message.author.bot,\n isMe: false, // Gateway messages are never from ourselves (we filter those)\n },\n metadata: {\n dateSent: message.createdAt,\n edited: message.editedAt !== null,\n editedAt: message.editedAt ?? undefined,\n },\n attachments: message.attachments.map((a) => ({\n type: this.getAttachmentType(a.contentType),\n url: a.url,\n name: a.name,\n mimeType: a.contentType ?? undefined,\n size: a.size,\n })),\n raw: {\n id: message.id,\n channel_id: channelId,\n guild_id: guildId,\n content: message.content,\n author: {\n id: message.author.id,\n username: message.author.username,\n },\n timestamp: message.createdAt.toISOString(),\n },\n // Add isMention flag for the chat handlers\n isMention: isMentioned,\n });\n\n try {\n await this.chat.handleIncomingMessage(this, threadId, chatMessage);\n } catch (error) {\n this.logger.error(\"Error handling Gateway message\", {\n error: String(error),\n messageId: message.id,\n });\n }\n }\n\n /**\n * Handle a reaction received via the Gateway WebSocket.\n */\n private async handleGatewayReaction(\n reaction: {\n emoji: { name: string | null; id: string | null };\n message: {\n id: string;\n channelId: string;\n guildId: string | null;\n channel?: { isThread?: () => boolean; parentId?: string | null };\n };\n },\n user: { id: string; username: string; bot: boolean },\n added: boolean\n ): Promise<void> {\n if (!this.chat) {\n return;\n }\n\n const guildId = reaction.message.guildId || \"@me\";\n const channelId = reaction.message.channelId;\n\n // Check if reaction is in a thread channel\n const isInThread = reaction.message.channel?.isThread?.();\n let parentChannelId = channelId;\n let discordThreadId: string | undefined;\n\n if (isInThread && reaction.message.channel?.parentId) {\n discordThreadId = channelId;\n parentChannelId = reaction.message.channel.parentId;\n }\n\n const threadId = this.encodeThreadId({\n guildId,\n channelId: parentChannelId,\n threadId: discordThreadId,\n });\n\n // Normalize emoji\n const emojiName = reaction.emoji.name || \"unknown\";\n const normalizedEmoji = this.normalizeDiscordEmoji(emojiName);\n\n // Build reaction event\n const reactionEvent = {\n adapter: this as Adapter,\n threadId,\n messageId: reaction.message.id,\n emoji: normalizedEmoji,\n rawEmoji: reaction.emoji.id\n ? `<:${emojiName}:${reaction.emoji.id}>`\n : emojiName,\n added,\n user: {\n userId: user.id,\n userName: user.username,\n fullName: user.username,\n isBot: user.bot === true, // Match pattern from handleForwardedReaction\n isMe: user.id === this.applicationId,\n },\n raw: {\n emoji: reaction.emoji,\n message_id: reaction.message.id,\n channel_id: reaction.message.channelId,\n guild_id: reaction.message.guildId,\n user_id: user.id,\n },\n };\n\n this.chat.processReaction(reactionEvent);\n }\n\n /**\n * Derive channel ID from a Discord thread ID.\n * Discord: discord:{guildId}:{channelId}:{threadId} -> discord:{guildId}:{channelId}\n * If already a channel ID (3 parts), returns as-is.\n */\n channelIdFromThreadId(threadId: string): string {\n const parts = threadId.split(\":\");\n // discord:{guildId}:{channelId} or discord:{guildId}:{channelId}:{threadId}\n return parts.slice(0, 3).join(\":\");\n }\n\n /**\n * Fetch channel-level messages (not thread replies).\n * Uses the parent channel ID to fetch top-level messages.\n */\n async fetchChannelMessages(\n channelId: string,\n options: FetchOptions = {}\n ): Promise<FetchResult<unknown>> {\n const parts = channelId.split(\":\");\n const discordChannelId = parts[2];\n if (!discordChannelId) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n const limit = options.limit || 50;\n const direction = options.direction ?? \"backward\";\n\n const params = new URLSearchParams();\n params.set(\"limit\", String(limit));\n\n if (options.cursor) {\n if (direction === \"backward\") {\n params.set(\"before\", options.cursor);\n } else {\n params.set(\"after\", options.cursor);\n }\n }\n\n this.logger.debug(\"Discord API: GET channel messages\", {\n channelId: discordChannelId,\n limit,\n direction,\n cursor: options.cursor,\n });\n\n const response = await this.discordFetch(\n `/channels/${discordChannelId}/messages?${params.toString()}`,\n \"GET\"\n );\n\n const rawMessages = (await response.json()) as APIMessage[];\n\n this.logger.debug(\"Discord API: GET channel messages response\", {\n messageCount: rawMessages.length,\n });\n\n // Discord returns newest first, reverse for chronological order\n const sortedMessages = [...rawMessages].reverse();\n\n const messages = sortedMessages.map((msg) =>\n this.parseDiscordMessage(msg, channelId)\n );\n\n let nextCursor: string | undefined;\n if (rawMessages.length === limit) {\n if (direction === \"backward\") {\n const oldest = rawMessages.at(-1);\n nextCursor = oldest?.id;\n } else {\n const newest = rawMessages[0];\n nextCursor = newest?.id;\n }\n }\n\n return { messages, nextCursor };\n }\n\n /**\n * List threads in a Discord channel.\n * Fetches active threads from the guild and archived threads from the channel.\n */\n async listThreads(\n channelId: string,\n options: ListThreadsOptions = {}\n ): Promise<ListThreadsResult<unknown>> {\n const parts = channelId.split(\":\");\n const guildId = parts[1];\n const discordChannelId = parts[2];\n if (!(guildId && discordChannelId)) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n this.logger.debug(\"Discord API: GET threads\", {\n guildId,\n channelId: discordChannelId,\n });\n\n // Fetch active threads from the guild\n const activeResponse = await this.discordFetch(\n `/guilds/${guildId}/threads/active`,\n \"GET\"\n );\n const activeData = (await activeResponse.json()) as {\n threads: Array<{\n id: string;\n name: string;\n parent_id: string;\n message_count?: number;\n total_message_sent?: number;\n thread_metadata?: { archive_timestamp?: string };\n }>;\n };\n\n // Filter threads that belong to our channel\n const channelThreads = (activeData.threads || []).filter(\n (t) => t.parent_id === discordChannelId\n );\n\n // Also fetch archived public threads\n let archivedThreads: typeof channelThreads = [];\n try {\n const archivedResponse = await this.discordFetch(\n `/channels/${discordChannelId}/threads/archived/public?limit=${options.limit || 50}`,\n \"GET\"\n );\n const archivedData = (await archivedResponse.json()) as {\n threads: typeof channelThreads;\n };\n archivedThreads = archivedData.threads || [];\n } catch {\n // Archived threads may not be available (permissions)\n this.logger.debug(\n \"Could not fetch archived threads (may lack permissions)\"\n );\n }\n\n // Merge and deduplicate\n const allThreads = [...channelThreads, ...archivedThreads];\n const seen = new Set<string>();\n const uniqueThreads = allThreads.filter((t) => {\n if (seen.has(t.id)) {\n return false;\n }\n seen.add(t.id);\n return true;\n });\n\n // Apply limit\n const limit = options.limit || 50;\n const limitedThreads = uniqueThreads.slice(0, limit);\n\n // Convert to ThreadSummary - fetch the first message of each thread\n const threads: ThreadSummary[] = [];\n for (const thread of limitedThreads) {\n const threadId = this.encodeThreadId({\n guildId,\n channelId: discordChannelId,\n threadId: thread.id,\n });\n\n // Fetch the first message of the thread (which is the root/starter message)\n try {\n const msgsResponse = await this.discordFetch(\n `/channels/${thread.id}/messages?limit=1&after=0`,\n \"GET\"\n );\n const msgs = (await msgsResponse.json()) as APIMessage[];\n const rootMsg = msgs[0];\n\n if (rootMsg) {\n threads.push({\n id: threadId,\n rootMessage: this.parseDiscordMessage(rootMsg, threadId),\n replyCount: thread.total_message_sent ?? thread.message_count,\n lastReplyAt: thread.thread_metadata?.archive_timestamp\n ? new Date(thread.thread_metadata.archive_timestamp)\n : undefined,\n });\n }\n } catch {\n // If we can't fetch the root message, create a placeholder\n threads.push({\n id: threadId,\n rootMessage: new Message({\n id: thread.id,\n threadId,\n text: thread.name,\n formatted: this.formatConverter.toAst(thread.name),\n raw: thread,\n author: {\n userId: \"unknown\",\n userName: \"unknown\",\n fullName: \"unknown\",\n isBot: false,\n isMe: false,\n },\n metadata: { dateSent: new Date(), edited: false },\n attachments: [],\n }),\n replyCount: thread.total_message_sent ?? thread.message_count,\n });\n }\n }\n\n this.logger.debug(\"Discord API: listThreads result\", {\n threadCount: threads.length,\n });\n\n return {\n threads,\n nextCursor: uniqueThreads.length > limit ? String(limit) : undefined,\n };\n }\n\n /**\n * Fetch Discord channel info/metadata.\n */\n async fetchChannelInfo(channelId: string): Promise<ChannelInfo> {\n const parts = channelId.split(\":\");\n const discordChannelId = parts[2];\n if (!discordChannelId) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n this.logger.debug(\"Discord API: GET channel info\", {\n channelId: discordChannelId,\n });\n\n const response = await this.discordFetch(\n `/channels/${discordChannelId}`,\n \"GET\"\n );\n const channel = (await response.json()) as {\n id: string;\n name?: string;\n type: ChannelType;\n member_count?: number;\n };\n\n return {\n id: channelId,\n name: channel.name,\n isDM:\n channel.type === ChannelType.DM || channel.type === ChannelType.GroupDM,\n memberCount: channel.member_count,\n metadata: {\n channelType: channel.type,\n raw: channel,\n },\n };\n }\n\n /**\n * Post a message to channel top-level (not in a thread).\n * Posts directly to the parent channel without a thread reference.\n */\n async postChannelMessage(\n channelId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<unknown>> {\n const parts = channelId.split(\":\");\n const discordChannelId = parts[2];\n if (!discordChannelId) {\n throw new ValidationError(\n \"discord\",\n `Invalid Discord channel ID: ${channelId}`\n );\n }\n\n // Build message payload\n const payload: DiscordMessagePayload = {};\n const embeds: APIEmbed[] = [];\n const components: DiscordActionRow[] = [];\n\n const card = extractCard(message);\n if (card) {\n const cardPayload = cardToDiscordPayload(card);\n embeds.push(...cardPayload.embeds);\n components.push(...cardPayload.components);\n payload.content = this.truncateContent(cardToFallbackText(card));\n } else {\n payload.content = this.truncateContent(\n convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"discord\"\n )\n );\n }\n\n if (embeds.length > 0) {\n payload.embeds = embeds;\n }\n if (components.length > 0) {\n payload.components = components;\n }\n\n const files = extractFiles(message);\n const slashResponse = this.tryPostSlashResponse(channelId, payload, files);\n if (slashResponse) {\n return slashResponse;\n }\n if (files.length > 0) {\n return this.postMessageWithFiles(\n discordChannelId,\n channelId,\n payload,\n files\n );\n }\n\n this.logger.debug(\"Discord API: POST channel message\", {\n channelId: discordChannelId,\n contentLength: payload.content?.length || 0,\n });\n\n const response = await this.discordFetch(\n `/channels/${discordChannelId}/messages`,\n \"POST\",\n payload\n );\n\n const result = (await response.json()) as APIMessage;\n\n return {\n id: result.id,\n threadId: channelId,\n raw: result,\n };\n }\n\n /**\n * Normalize a Discord emoji to our standard EmojiValue format.\n */\n private normalizeDiscordEmoji(emojiName: string): EmojiValue {\n // Map common Discord unicode emoji to our standard names\n const unicodeToName: Record<string, string> = {\n \"👍\": \"thumbs_up\",\n \"👎\": \"thumbs_down\",\n \"❤️\": \"heart\",\n \"❤\": \"heart\",\n \"🔥\": \"fire\",\n \"🚀\": \"rocket\",\n \"🙌\": \"raised_hands\",\n \"✅\": \"check\",\n \"❌\": \"x\",\n \"👋\": \"wave\",\n \"🤔\": \"thinking\",\n \"😊\": \"smile\",\n \"😂\": \"laugh\",\n \"🎉\": \"party\",\n \"⭐\": \"star\",\n \"✨\": \"sparkles\",\n \"👀\": \"eyes\",\n \"💯\": \"100\",\n };\n\n // Check if it's a unicode emoji we recognize\n const normalizedName = unicodeToName[emojiName] || emojiName;\n return getEmoji(normalizedName);\n }\n}\n\n/**\n * Create a Discord adapter instance.\n */\nexport function createDiscordAdapter(\n config?: DiscordAdapterConfig\n): DiscordAdapter {\n return new DiscordAdapter(config ?? {});\n}\n\n// Re-export card converter for advanced use\nexport { cardToDiscordPayload, cardToFallbackText } from \"./cards\";\n\n// Re-export format converter for advanced use\nexport {\n DiscordFormatConverter,\n DiscordFormatConverter as DiscordMarkdownConverter,\n} from \"./markdown\";\n// Re-export types\nexport type { DiscordAdapterConfig, DiscordThreadId } from \"./types\";\n","/**\n * Discord Embed and Component converter for cross-platform cards.\n *\n * Converts CardElement to Discord Embeds and Action Row Components.\n * @see https://discord.com/developers/docs/resources/message#embed-object\n * @see https://discord.com/developers/docs/interactions/message-components\n */\n\nimport { renderGfmTable } from \"@chat-adapter/shared\";\nimport type {\n ActionsElement,\n ButtonElement,\n CardChild,\n CardElement,\n FieldsElement,\n LinkButtonElement,\n SectionElement,\n TextElement,\n} from \"chat\";\nimport {\n cardChildToFallbackText,\n convertEmojiPlaceholders,\n tableElementToAscii,\n} from \"chat\";\nimport type { APIEmbed, APIEmbedField } from \"discord-api-types/v10\";\nimport { ButtonStyle } from \"discord-api-types/v10\";\nimport type { DiscordActionRow, DiscordButton } from \"./types\";\n\n/**\n * Convert emoji placeholders to Discord format.\n */\nfunction convertEmoji(text: string): string {\n return convertEmojiPlaceholders(text, \"discord\");\n}\n\n/**\n * Convert a CardElement to Discord message payload (embeds + components).\n */\nexport function cardToDiscordPayload(card: CardElement): {\n embeds: APIEmbed[];\n components: DiscordActionRow[];\n} {\n const embed: APIEmbed = {};\n const fields: APIEmbedField[] = [];\n const components: DiscordActionRow[] = [];\n\n // Set title and description (with emoji conversion)\n if (card.title) {\n embed.title = convertEmoji(card.title);\n }\n\n if (card.subtitle) {\n embed.description = convertEmoji(card.subtitle);\n }\n\n // Set header image\n if (card.imageUrl) {\n embed.image = {\n url: card.imageUrl,\n };\n }\n\n // Set color (default to Discord blurple)\n embed.color = 0x5865f2;\n\n // Process children\n const textParts: string[] = [];\n\n for (const child of card.children) {\n processChild(child, textParts, fields, components);\n }\n\n // If we have text parts and no description, set them as description\n if (textParts.length > 0) {\n if (embed.description) {\n embed.description += `\\n\\n${textParts.join(\"\\n\\n\")}`;\n } else {\n embed.description = textParts.join(\"\\n\\n\");\n }\n }\n\n // Add fields if we have any\n if (fields.length > 0) {\n embed.fields = fields;\n }\n\n return {\n embeds: [embed],\n components,\n };\n}\n\n/**\n * Process a card child element.\n */\nfunction processChild(\n child: CardChild,\n textParts: string[],\n fields: APIEmbedField[],\n components: DiscordActionRow[]\n): void {\n switch (child.type) {\n case \"text\":\n textParts.push(convertTextElement(child));\n break;\n case \"image\":\n // Discord embeds can only have one image, handled at card level\n // Additional images could be added as separate embeds\n break;\n case \"divider\":\n // No direct equivalent, add a horizontal line marker\n textParts.push(\"───────────\");\n break;\n case \"actions\":\n components.push(...convertActionsToRows(child));\n break;\n case \"section\":\n processSectionElement(child, textParts, fields, components);\n break;\n case \"fields\":\n convertFieldsElement(child, fields);\n break;\n case \"link\":\n textParts.push(`[${convertEmoji(child.label)}](${child.url})`);\n break;\n case \"table\": {\n // Render as GFM markdown table in embed description\n textParts.push(renderGfmTable(child).join(\"\\n\"));\n break;\n }\n default: {\n const text = cardChildToFallbackText(child);\n if (text) {\n textParts.push(text);\n }\n break;\n }\n }\n}\n\n/**\n * Convert a text element to Discord markdown.\n */\nfunction convertTextElement(element: TextElement): string {\n let text = convertEmoji(element.content);\n\n // Apply style\n if (element.style === \"bold\") {\n text = `**${text}**`;\n } else if (element.style === \"muted\") {\n // Discord doesn't have muted, use italic as approximation\n text = `*${text}*`;\n }\n\n return text;\n}\n\n/**\n * Convert an actions element to Discord action rows.\n * Discord limits each action row to 5 components, so we chunk buttons.\n */\nfunction convertActionsToRows(element: ActionsElement): DiscordActionRow[] {\n const buttons: DiscordButton[] = element.children\n .filter((child) => child.type === \"button\" || child.type === \"link-button\")\n .map((button) => {\n if (button.type === \"link-button\") {\n return convertLinkButtonElement(button);\n }\n return convertButtonElement(button);\n });\n\n // Discord allows max 5 buttons per action row\n const rows: DiscordActionRow[] = [];\n for (let i = 0; i < buttons.length; i += 5) {\n rows.push({\n type: 1, // Action Row\n components: buttons.slice(i, i + 5),\n });\n }\n return rows;\n}\n\n/**\n * Convert a button element to a Discord button.\n */\nfunction convertButtonElement(button: ButtonElement): DiscordButton {\n const discordButton: DiscordButton = {\n type: 2, // Button\n style: getButtonStyle(button.style),\n label: button.label,\n custom_id: button.id,\n };\n\n if (button.disabled) {\n discordButton.disabled = true;\n }\n\n return discordButton;\n}\n\n/**\n * Convert a link button element to a Discord link button.\n */\nfunction convertLinkButtonElement(button: LinkButtonElement): DiscordButton {\n return {\n type: 2, // Button\n style: ButtonStyle.Link,\n label: button.label,\n url: button.url,\n };\n}\n\n/**\n * Map button style to Discord button style.\n */\nfunction getButtonStyle(style?: ButtonElement[\"style\"]): ButtonStyle {\n switch (style) {\n case \"primary\":\n return ButtonStyle.Primary;\n case \"danger\":\n return ButtonStyle.Danger;\n default:\n return ButtonStyle.Secondary;\n }\n}\n\n/**\n * Process a section element.\n */\nfunction processSectionElement(\n element: SectionElement,\n textParts: string[],\n fields: APIEmbedField[],\n components: DiscordActionRow[]\n): void {\n for (const child of element.children) {\n processChild(child, textParts, fields, components);\n }\n}\n\n/**\n * Convert fields element to Discord embed fields.\n */\nfunction convertFieldsElement(\n element: FieldsElement,\n fields: APIEmbedField[]\n): void {\n for (const field of element.children) {\n fields.push({\n name: convertEmoji(field.label),\n value: convertEmoji(field.value),\n inline: true, // Discord fields can be inline\n });\n }\n}\n\n/**\n * Generate fallback text from a card element.\n * Used when embeds aren't supported or for notifications.\n */\nexport function cardToFallbackText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(`**${convertEmoji(card.title)}**`);\n }\n\n if (card.subtitle) {\n parts.push(convertEmoji(card.subtitle));\n }\n\n for (const child of card.children) {\n const text = childToFallbackText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n\n/**\n * Convert a card child element to fallback text.\n */\nfunction childToFallbackText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return convertEmoji(child.content);\n case \"fields\":\n return child.children\n .map((f) => `**${convertEmoji(f.label)}**: ${convertEmoji(f.value)}`)\n .join(\"\\n\");\n case \"actions\":\n // Actions are interactive-only — exclude from fallback text.\n // See: https://docs.slack.dev/reference/methods/chat.postMessage\n return null;\n case \"section\":\n return child.children\n .map((c) => childToFallbackText(c))\n .filter(Boolean)\n .join(\"\\n\");\n case \"table\":\n return `\\`\\`\\`\\n${tableElementToAscii(child.headers, child.rows)}\\n\\`\\`\\``;\n case \"divider\":\n return \"---\";\n default:\n return cardChildToFallbackText(child);\n }\n}\n","/**\n * Discord-specific format conversion using AST-based parsing.\n *\n * Discord uses standard markdown with some extensions:\n * - Bold: **text** (standard)\n * - Italic: *text* or _text_ (standard)\n * - Strikethrough: ~~text~~ (standard GFM)\n * - Links: [text](url) (standard)\n * - User mentions: <@userId>\n * - Channel mentions: <#channelId>\n * - Role mentions: <@&roleId>\n * - Custom emoji: <:name:id> or <a:name:id> (animated)\n * - Spoiler: ||text||\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Content,\n getNodeChildren,\n isBlockquoteNode,\n isCodeNode,\n isDeleteNode,\n isEmphasisNode,\n isInlineCodeNode,\n isLinkNode,\n isListNode,\n isParagraphNode,\n isStrongNode,\n isTableNode,\n isTextNode,\n parseMarkdown,\n type Root,\n tableToAscii,\n} from \"chat\";\n\nexport class DiscordFormatConverter extends BaseFormatConverter {\n /**\n * Convert @mentions to Discord format in plain text.\n * @name → <@name>\n */\n private convertMentionsToDiscord(text: string): string {\n return text.replace(/@(\\w+)/g, \"<@$1>\");\n }\n\n /**\n * Override renderPostable to convert @mentions in plain strings.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return this.convertMentionsToDiscord(message);\n }\n if (\"raw\" in message) {\n return this.convertMentionsToDiscord(message.raw);\n }\n if (\"markdown\" in message) {\n return this.fromAst(parseMarkdown(message.markdown));\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return \"\";\n }\n\n /**\n * Render an AST to Discord markdown format.\n */\n fromAst(ast: Root): string {\n return this.fromAstWithNodeConverter(ast, (node) =>\n this.nodeToDiscordMarkdown(node)\n );\n }\n\n /**\n * Parse Discord markdown into an AST.\n */\n toAst(discordMarkdown: string): Root {\n // Convert Discord-specific formats to standard markdown, then parse\n let markdown = discordMarkdown;\n\n // User mentions: <@userId> or <@!userId> -> @userId\n markdown = markdown.replace(/<@!?(\\w+)>/g, \"@$1\");\n\n // Channel mentions: <#channelId> -> #channelId\n markdown = markdown.replace(/<#(\\w+)>/g, \"#$1\");\n\n // Role mentions: <@&roleId> -> @&roleId\n markdown = markdown.replace(/<@&(\\w+)>/g, \"@&$1\");\n\n // Custom emoji: <:name:id> or <a:name:id> -> :name:\n markdown = markdown.replace(/<a?:(\\w+):\\d+>/g, \":$1:\");\n\n // Spoiler tags: ||text|| -> [spoiler: text]\n // (no direct markdown equivalent, convert to placeholder)\n markdown = markdown.replace(/\\|\\|([^|]+)\\|\\|/g, \"[spoiler: $1]\");\n\n return parseMarkdown(markdown);\n }\n\n private nodeToDiscordMarkdown(node: Content): string {\n // Use type guards for type-safe node handling\n if (isParagraphNode(node)) {\n return getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n }\n\n if (isTextNode(node)) {\n // Convert @mentions to Discord format <@mention>\n return node.value.replace(/@(\\w+)/g, \"<@$1>\");\n }\n\n if (isStrongNode(node)) {\n // Standard markdown **text**\n const content = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n return `**${content}**`;\n }\n\n if (isEmphasisNode(node)) {\n // Standard markdown *text*\n const content = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n return `*${content}*`;\n }\n\n if (isDeleteNode(node)) {\n // Standard GFM ~~text~~\n const content = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n return `~~${content}~~`;\n }\n\n if (isInlineCodeNode(node)) {\n return `\\`${node.value}\\``;\n }\n\n if (isCodeNode(node)) {\n return `\\`\\`\\`${node.lang || \"\"}\\n${node.value}\\n\\`\\`\\``;\n }\n\n if (isLinkNode(node)) {\n const linkText = getNodeChildren(node)\n .map((child) => this.nodeToDiscordMarkdown(child))\n .join(\"\");\n // Standard markdown [text](url)\n return `[${linkText}](${node.url})`;\n }\n\n if (isBlockquoteNode(node)) {\n return getNodeChildren(node)\n .map((child) => `> ${this.nodeToDiscordMarkdown(child)}`)\n .join(\"\\n\");\n }\n\n if (isListNode(node)) {\n return this.renderList(node, 0, (child) =>\n this.nodeToDiscordMarkdown(child)\n );\n }\n\n if (node.type === \"break\") {\n return \"\\n\";\n }\n\n if (node.type === \"thematicBreak\") {\n return \"---\";\n }\n\n if (isTableNode(node)) {\n return `\\`\\`\\`\\n${tableToAscii(node)}\\n\\`\\`\\``;\n }\n\n return this.defaultNodeToText(node, (child) =>\n this.nodeToDiscordMarkdown(child)\n );\n }\n}\n","/**\n * Discord adapter types.\n */\n\nimport type { Logger } from \"chat\";\nimport type {\n APIEmbed,\n APIMessage,\n ButtonStyle,\n ChannelType,\n InteractionType,\n} from \"discord-api-types/v10\";\n\n/**\n * Discord adapter configuration.\n */\nexport interface DiscordAdapterConfig {\n /** Discord application ID. Defaults to DISCORD_APPLICATION_ID env var. */\n applicationId?: string;\n /** Discord bot token. Defaults to DISCORD_BOT_TOKEN env var. */\n botToken?: string;\n /** Logger instance for error reporting. Defaults to ConsoleLogger. */\n logger?: Logger;\n /** Role IDs that should trigger mention handlers (in addition to direct user mentions). Defaults to DISCORD_MENTION_ROLE_IDS env var (comma-separated). */\n mentionRoleIds?: string[];\n /** Discord application public key for webhook signature verification. Defaults to DISCORD_PUBLIC_KEY env var. */\n publicKey?: string;\n /** Override bot username (optional) */\n userName?: string;\n}\n\n/**\n * Discord thread ID components.\n * Used for encoding/decoding thread IDs.\n */\nexport interface DiscordThreadId {\n /** Channel ID */\n channelId: string;\n /** Guild ID, or \"@me\" for DMs */\n guildId: string;\n /** Thread ID (if message is in a thread) */\n threadId?: string;\n}\n\n/**\n * Per-request slash command context used while resolving deferred responses.\n */\nexport interface DiscordSlashCommandContext {\n channelId: string;\n initialResponseSent: boolean;\n interactionToken: string;\n}\n\n/**\n * Async request context for Discord webhook handling.\n */\nexport interface DiscordRequestContext {\n slashCommand?: DiscordSlashCommandContext;\n}\n\n/**\n * Incoming Discord interaction from webhook.\n */\nexport interface DiscordInteraction {\n application_id: string;\n channel?: {\n id: string;\n type: ChannelType;\n name?: string;\n /** Parent channel ID (present when channel is a thread) */\n parent_id?: string;\n };\n channel_id?: string;\n data?: DiscordInteractionData;\n guild_id?: string;\n id: string;\n member?: {\n user: DiscordUser;\n nick?: string;\n roles: string[];\n joined_at: string;\n };\n message?: APIMessage;\n token: string;\n type: InteractionType;\n user?: DiscordUser;\n version: number;\n}\n\n/**\n * Discord user object.\n */\nexport interface DiscordUser {\n avatar?: string;\n bot?: boolean;\n discriminator: string;\n global_name?: string;\n id: string;\n username: string;\n}\n\n/**\n * Discord interaction data (for components/commands).\n */\nexport interface DiscordInteractionData {\n component_type?: number;\n custom_id?: string;\n name?: string;\n options?: DiscordCommandOption[];\n type?: number;\n values?: string[];\n}\n\n/**\n * Discord command option.\n */\nexport interface DiscordCommandOption {\n name: string;\n options?: DiscordCommandOption[];\n type: number;\n value?: string | number | boolean;\n}\n\n/**\n * Discord emoji.\n */\nexport interface DiscordEmoji {\n animated?: boolean;\n id?: string;\n name: string;\n}\n\n/**\n * Discord button component.\n */\nexport interface DiscordButton {\n custom_id?: string;\n disabled?: boolean;\n emoji?: DiscordEmoji;\n label?: string;\n style: ButtonStyle;\n type: 2; // Component type for button\n url?: string;\n}\n\n/**\n * Discord action row component.\n */\nexport interface DiscordActionRow {\n components: DiscordButton[];\n type: 1; // Component type for action row\n}\n\n/**\n * Discord message create payload.\n */\nexport interface DiscordMessagePayload {\n allowed_mentions?: {\n parse?: (\"roles\" | \"users\" | \"everyone\")[];\n roles?: string[];\n users?: string[];\n replied_user?: boolean;\n };\n attachments?: {\n id: string;\n filename: string;\n description?: string;\n }[];\n components?: DiscordActionRow[];\n content?: string;\n embeds?: APIEmbed[];\n message_reference?: {\n message_id: string;\n fail_if_not_exists?: boolean;\n };\n}\n\n/**\n * Discord interaction response types.\n * Note: Only the types currently used are defined here.\n * Additional types: ChannelMessageWithSource (4), UpdateMessage (7)\n */\nexport const InteractionResponseType = {\n /** ACK and edit later (deferred) */\n DeferredChannelMessageWithSource: 5,\n /** ACK component interaction, update message later */\n DeferredUpdateMessage: 6,\n} as const;\n\nexport type InteractionResponseType =\n (typeof InteractionResponseType)[keyof typeof InteractionResponseType];\n\n/**\n * Discord interaction response.\n */\nexport interface DiscordInteractionResponse {\n data?: DiscordMessagePayload;\n type: InteractionResponseType;\n}\n\n// ============================================================================\n// Gateway Forwarded Events\n// These types represent Gateway WebSocket events forwarded to the webhook endpoint\n// ============================================================================\n\n/**\n * Known Gateway event types that have specific handlers.\n * Other event types are still forwarded but processed generically.\n */\nexport type DiscordGatewayEventType =\n | \"GATEWAY_MESSAGE_CREATE\"\n | \"GATEWAY_MESSAGE_REACTION_ADD\"\n | \"GATEWAY_MESSAGE_REACTION_REMOVE\"\n | `GATEWAY_${string}`; // Allow any Gateway event type\n\n/**\n * A Gateway event forwarded to the webhook endpoint.\n * All Gateway events are forwarded, even ones without specific handlers.\n */\nexport interface DiscordForwardedEvent {\n /** Event-specific data - structure varies by event type */\n data: DiscordGatewayMessageData | DiscordGatewayReactionData | unknown;\n /** Unix timestamp when the event was received */\n timestamp: number;\n /** Event type identifier (prefixed with GATEWAY_) */\n type: DiscordGatewayEventType;\n}\n\n/**\n * Message data from a MESSAGE_CREATE Gateway event.\n */\nexport interface DiscordGatewayMessageData {\n /** File attachments */\n attachments: Array<{\n id: string;\n url: string;\n filename: string;\n content_type?: string;\n size: number;\n }>;\n /** Message author */\n author: {\n id: string;\n username: string;\n global_name?: string;\n bot: boolean;\n };\n /** Channel where the message was sent */\n channel_id: string;\n /** Channel type (11 = public thread, 12 = private thread) */\n channel_type?: number;\n /** Message content */\n content: string;\n /** Guild ID, or null for DMs */\n guild_id: string | null;\n /** Message ID */\n id: string;\n /** Whether the bot was mentioned */\n is_mention?: boolean;\n /** Role IDs mentioned in the message */\n mention_roles?: string[];\n /** Users mentioned in the message */\n mentions: Array<{ id: string; username: string }>;\n /** Thread info if message is in a thread */\n thread?: {\n id: string;\n parent_id: string;\n };\n /** ISO timestamp */\n timestamp: string;\n}\n\n/**\n * Reaction data from REACTION_ADD or REACTION_REMOVE Gateway events.\n */\nexport interface DiscordGatewayReactionData {\n /** Channel containing the message */\n channel_id: string;\n /** Channel type (11 = public thread, 12 = private thread) */\n channel_type?: number;\n /** Emoji used for the reaction */\n emoji: {\n name: string | null;\n id: string | null;\n };\n /** Guild ID, or null for DMs */\n guild_id: string | null;\n /** Member details (for guild reactions) */\n member?: {\n user: {\n id: string;\n username: string;\n global_name?: string;\n bot?: boolean;\n };\n };\n /** ID of the message that was reacted to */\n message_id: string;\n /** User details (for DMs) */\n user?: {\n id: string;\n username: string;\n bot?: boolean;\n };\n /** User who added/removed the reaction */\n user_id: string;\n}\n"],"mappings":"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","names":["convertEmojiPlaceholders","convertEmojiPlaceholders"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chat-adapter/discord",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.18.0",
|
|
4
4
|
"description": "Discord adapter for chat",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"discord-api-types": "^0.37.119",
|
|
20
20
|
"discord-interactions": "^4.4.0",
|
|
21
21
|
"discord.js": "^14.25.1",
|
|
22
|
-
"@chat-adapter/shared": "4.
|
|
23
|
-
"chat": "4.
|
|
22
|
+
"@chat-adapter/shared": "4.18.0",
|
|
23
|
+
"chat": "4.18.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^25.3.2",
|