@scotthamilton77/discord-bot-lib 1.0.3 → 1.1.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/README.md CHANGED
@@ -55,6 +55,40 @@ bot.on("error", (err) => console.error("Bot error:", err.message));
55
55
  await bot.disconnect();
56
56
  ```
57
57
 
58
+ ## Typing Events
59
+
60
+ Listen for when users start typing. **Requires explicit intents** -- `GuildMessageTyping` and/or `DirectMessageTyping` are not included in the default intents, and Discord silently drops the events without them.
61
+
62
+ ```typescript
63
+ import { Bot } from "@scotthamilton77/discord-bot-lib";
64
+ import { GatewayIntentBits } from "discord.js";
65
+
66
+ const bot = await Bot.fromConfig({
67
+ id: "typing-bot",
68
+ name: "TypingBot",
69
+ token: process.env.DISCORD_TOKEN!,
70
+ intents: [
71
+ GatewayIntentBits.Guilds,
72
+ GatewayIntentBits.GuildMessages,
73
+ GatewayIntentBits.MessageContent,
74
+ GatewayIntentBits.DirectMessages,
75
+ GatewayIntentBits.GuildMessageTyping, // required for guild typing
76
+ GatewayIntentBits.DirectMessageTyping, // required for DM typing
77
+ ],
78
+ });
79
+
80
+ // Returns an unsubscribe function (same pattern as onMessage)
81
+ const unsubscribe = bot.onTypingStart((event) => {
82
+ console.log(`User ${event.userId} started typing in ${event.channelId}`);
83
+ if (event.guildId === null) {
84
+ console.log("(this is a DM)");
85
+ }
86
+ });
87
+
88
+ // Throws if bot was not configured with typing intents
89
+ // bot.onTypingStart(handler) → Error: "onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents"
90
+ ```
91
+
58
92
  ## Async Iterables
59
93
 
60
94
  All messages are also available as an `AsyncIterable`, useful for sequential processing with `for await`:
@@ -183,6 +217,10 @@ await bot.react(channelId, messageId, "\ud83d\udc4d");
183
217
 
184
218
  // Edit a message
185
219
  await bot.editMessage(channelId, messageId, "Updated content");
220
+
221
+ // Typing indicator (shows "Bot is typing..." for ~10s)
222
+ await bot.sendTyping(channelId);
223
+ await bot.sendDMTyping(userId);
186
224
  ```
187
225
 
188
226
  ## Fetching History
@@ -270,6 +308,7 @@ const { buffer, filename, contentType } = await downloadAttachment(attachment);
270
308
  | `BotStatus` | `"unregistered" \| "configuring" \| "connecting" \| "verifying" \| "ready" \| "disconnected" \| "failed"` |
271
309
  | `MessageEvent` | Incoming message with routing metadata (`isDM`, `isMention`, `isReply`, `replyTo`), author, content, channelId, mentions, and `raw` discord.js Message. |
272
310
  | `SentMessage` | Tracked outgoing message with messageId, channelId, timestamp, and `raw` escape hatch. |
311
+ | `TypingEvent` | Typing indicator with `channelId`, `userId`, `startedAt`, `guildId` (null for DMs), and `raw` discord.js Typing. |
273
312
  | `MessageContent` | `string \| { content?: string; embeds?: unknown[]; files?: FileAttachment[] }` |
274
313
  | `FetchedMessage` | Lightweight message from `fetchMessages()` / `fetchHistory()`. |
275
314
  | `FetchHistoryOptions` | Discriminated union: `{ after: string } \| { before: string }` with optional `limit`. |
package/dist/index.cjs CHANGED
@@ -164,14 +164,18 @@ var Bot = class _Bot {
164
164
  sentMessages;
165
165
  _connectedAt = null;
166
166
  messageHandlers = [];
167
+ typingHandlers = [];
167
168
  errorHandlers = [];
168
169
  _includeBotMessages;
169
- constructor(id, name, client, sentMessageCacheSize, includeBotMessages) {
170
+ resolvedIntents;
171
+ typingListenerAttached = false;
172
+ constructor(id, name, client, sentMessageCacheSize, includeBotMessages, resolvedIntents) {
170
173
  this.id = id;
171
174
  this.name = name;
172
175
  this.client = client;
173
176
  this.sentMessages = new import_discord.LimitedCollection({ maxSize: sentMessageCacheSize });
174
177
  this._includeBotMessages = includeBotMessages;
178
+ this.resolvedIntents = resolvedIntents;
175
179
  this._status = "connecting";
176
180
  }
177
181
  get includeBotMessages() {
@@ -198,11 +202,12 @@ var Bot = class _Bot {
198
202
  );
199
203
  }
200
204
  const includeBotMessages = config.includeBotMessages ?? false;
205
+ const intents = config.intents ?? [...DEFAULT_INTENTS];
201
206
  const client = new import_discord.Client({
202
- intents: config.intents ?? [...DEFAULT_INTENTS],
207
+ intents,
203
208
  partials: config.partials ?? [...DEFAULT_PARTIALS]
204
209
  });
205
- const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages);
210
+ const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);
206
211
  await bot.connect(config.token);
207
212
  bot.verify();
208
213
  bot.setupEventHandlers();
@@ -261,6 +266,31 @@ var Bot = class _Bot {
261
266
  );
262
267
  }
263
268
  }
269
+ async sendTyping(channelId) {
270
+ const channel = await this.fetchTextChannel(channelId);
271
+ const typingChannel = channel;
272
+ if (typeof typingChannel.sendTyping !== "function") {
273
+ throw new Error(`Channel ${channelId} does not support typing indicators`);
274
+ }
275
+ try {
276
+ await typingChannel.sendTyping();
277
+ } catch (error) {
278
+ throw new Error(
279
+ `Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`
280
+ );
281
+ }
282
+ }
283
+ async sendDMTyping(userId) {
284
+ try {
285
+ const user = await this.client.users.fetch(userId);
286
+ const dm = await user.createDM();
287
+ await dm.sendTyping();
288
+ } catch (error) {
289
+ throw new Error(
290
+ `Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`
291
+ );
292
+ }
293
+ }
264
294
  async fetchMessages(channelId, limit) {
265
295
  const channel = await this.fetchTextChannel(channelId);
266
296
  const clamped = Math.max(1, Math.min(limit ?? 20, 100));
@@ -324,6 +354,36 @@ var Bot = class _Bot {
324
354
  if (index !== -1) this.messageHandlers.splice(index, 1);
325
355
  };
326
356
  }
357
+ onTypingStart(handler) {
358
+ const hasGuildTyping = this.resolvedIntents.includes(import_discord.GatewayIntentBits.GuildMessageTyping);
359
+ const hasDMTyping = this.resolvedIntents.includes(import_discord.GatewayIntentBits.DirectMessageTyping);
360
+ if (!hasGuildTyping && !hasDMTyping) {
361
+ throw new Error(
362
+ "onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. Add them to the intents array in BotConfig."
363
+ );
364
+ }
365
+ if (!this.typingListenerAttached) {
366
+ this.typingListenerAttached = true;
367
+ this.client.on(import_discord.Events.TypingStart, (typing) => {
368
+ try {
369
+ const event = this.toTypingEvent(typing);
370
+ for (const h of this.typingHandlers) {
371
+ h(event);
372
+ }
373
+ } catch (error) {
374
+ this.emitError(error);
375
+ }
376
+ });
377
+ }
378
+ this.typingHandlers.push(handler);
379
+ let removed = false;
380
+ return () => {
381
+ if (removed) return;
382
+ removed = true;
383
+ const index = this.typingHandlers.indexOf(handler);
384
+ if (index !== -1) this.typingHandlers.splice(index, 1);
385
+ };
386
+ }
327
387
  // --- Receiving (async iterable) ---
328
388
  messages(options) {
329
389
  const cleanup = {};
@@ -358,6 +418,8 @@ var Bot = class _Bot {
358
418
  this.client.removeAllListeners();
359
419
  await this.client.destroy();
360
420
  this.messageHandlers.length = 0;
421
+ this.typingHandlers.length = 0;
422
+ this.typingListenerAttached = false;
361
423
  this.errorHandlers.length = 0;
362
424
  this._status = "disconnected";
363
425
  this._connectedAt = null;
@@ -496,6 +558,15 @@ var Bot = class _Bot {
496
558
  replyTo
497
559
  };
498
560
  }
561
+ toTypingEvent(typing) {
562
+ return {
563
+ channelId: typing.channel.id,
564
+ userId: typing.user.id,
565
+ startedAt: typing.startedAt,
566
+ guildId: typing.guild?.id ?? null,
567
+ raw: typing
568
+ };
569
+ }
499
570
  async sendPayloads(content, dispatcher) {
500
571
  const files = typeof content !== "string" && content.files?.length ? await validateAndBuildAttachments(content.files) : void 0;
501
572
  const payloads = toChunkedPayloads(content, files);
@@ -702,6 +773,7 @@ var ConnectorManager = class {
702
773
  botMap = /* @__PURE__ */ new Map();
703
774
  botUnsubscribers = /* @__PURE__ */ new Map();
704
775
  messageHandlers = [];
776
+ typingHandlers = [];
705
777
  lifecycleHandlers = /* @__PURE__ */ new Map();
706
778
  addBot(bot) {
707
779
  if (this.botMap.has(bot.id)) {
@@ -721,6 +793,9 @@ var ConnectorManager = class {
721
793
  this.emitLifecycle("botError", bot, error);
722
794
  })
723
795
  );
796
+ if (this.typingHandlers.length > 0) {
797
+ this.subscribeTyping(bot, unsubs);
798
+ }
724
799
  this.botUnsubscribers.set(bot.id, unsubs);
725
800
  }
726
801
  getBot(id) {
@@ -747,11 +822,35 @@ var ConnectorManager = class {
747
822
  onMessage(handler) {
748
823
  this.messageHandlers.push(handler);
749
824
  }
825
+ onTypingStart(handler) {
826
+ const isFirst = this.typingHandlers.length === 0;
827
+ this.typingHandlers.push(handler);
828
+ if (isFirst) {
829
+ for (const [id, bot] of this.botMap) {
830
+ const unsubs = this.botUnsubscribers.get(id) ?? [];
831
+ this.subscribeTyping(bot, unsubs);
832
+ }
833
+ }
834
+ }
750
835
  on(event, callback) {
751
836
  const handlers = this.lifecycleHandlers.get(event) ?? [];
752
837
  handlers.push(callback);
753
838
  this.lifecycleHandlers.set(event, handlers);
754
839
  }
840
+ subscribeTyping(bot, unsubs) {
841
+ try {
842
+ unsubs.push(
843
+ bot.onTypingStart((event) => {
844
+ for (const handler of this.typingHandlers) {
845
+ handler(event, bot);
846
+ }
847
+ })
848
+ );
849
+ } catch (error) {
850
+ const isIntentError = error instanceof Error && error.message.startsWith("onTypingStart requires");
851
+ if (!isIntentError) throw error;
852
+ }
853
+ }
755
854
  emitLifecycle(event, ...args) {
756
855
  const handlers = this.lifecycleHandlers.get(event);
757
856
  if (handlers) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["// discord-bot-lib — public API\n\nexport { Bot, DEFAULT_SENT_MESSAGE_CACHE_SIZE } from \"./bot.js\";\nexport { BotOnboarding } from \"./onboarding.js\";\nexport { ConnectorManager } from \"./connector-manager.js\";\nexport { EventBuffer } from \"./event-buffer.js\";\nexport {\n chunkMessage,\n DISCORD_MAX_MESSAGE_LENGTH,\n} from \"./message-utils.js\";\nexport {\n MAX_ATTACHMENT_BYTES,\n validateAttachmentSize,\n sanitizeAttachmentName,\n downloadAttachment,\n} from \"./attachment-utils.js\";\n\nexport type {\n AttachmentLike,\n DownloadableAttachment,\n DownloadedAttachment,\n} from \"./attachment-utils.js\";\n\nexport type {\n Author,\n BotConfig,\n BotStatus,\n BotStateAdapter,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n FileAttachment,\n FetchedMessage,\n FetchHistoryOptions,\n OnboardingStep,\n OnboardingStepStatus,\n StepResult,\n ManagerEvents,\n} from \"./types.js\";\n","import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n\n const client = new Client({\n intents: config.intents ?? [...DEFAULT_INTENTS],\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,qBAAgH;AAChH,sBAAqB;;;ACId,IAAM,cAAN,MAAiD;AAAA,EAC9C,SAAc,CAAC;AAAA,EACf,UAAuD;AAAA,EACvD,SAAS;AAAA,EACA;AAAA,EAEjB,YAAY,SAAoC;AAC9C,SAAK,UAAU,SAAS;AAAA,EAC1B;AAAA,EAEA,KAAK,OAAgB;AACnB,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,KAAK;AACf,WAAK,UAAU;AACf,QAAE,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,IAC1B,OAAO;AACL,WAAK,OAAO,KAAK,KAAK;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,UAAU;AACf,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,KAAK;AACf,WAAK,UAAU;AACf,QAAE,EAAE,OAAO,QAA2B,MAAM,KAAK,CAAC;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,CAAC,OAAO,aAAa,IAAsB;AACzC,WAAO;AAAA,MACL,MAAM,MAAkC;AACtC,YAAI,KAAK,OAAO,SAAS,GAAG;AAE1B,gBAAM,QAAQ,KAAK,OAAO,MAAM;AAChC,iBAAO,QAAQ,QAAQ,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,QAC/C;AACA,YAAI,KAAK,QAAQ;AACf,iBAAO,QAAQ,QAAQ;AAAA,YACrB,OAAO;AAAA,YACP,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,eAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,eAAK,UAAU;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,MAAkC;AACxC,aAAK,MAAM;AACX,eAAO,QAAQ,QAAQ,EAAE,OAAO,QAA2B,MAAM,KAAK,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACF;;;AC1DO,IAAM,6BAA6B;AAWnC,SAAS,aACd,MACA,QAAgB,4BACN;AACV,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,MAAI,KAAK,UAAU,MAAO,QAAO,CAAC,IAAI;AAEtC,QAAM,SAAmB,CAAC;AAC1B,QAAM,YAAY,QAAQ;AAC1B,MAAI,OAAO;AAEX,SAAO,KAAK,SAAS,OAAO;AAC1B,UAAM,OAAO,KAAK,YAAY,QAAQ,KAAK;AAC3C,UAAM,OAAO,KAAK,YAAY,MAAM,KAAK;AACzC,UAAM,QAAQ,KAAK,YAAY,KAAK,KAAK;AACzC,UAAM,MACJ,OAAO,YACH,OACA,OAAO,YACL,OACA,QAAQ,IACN,QACA;AAEV,WAAO,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC;AAC9B,WAAO,KAAK,MAAM,GAAG,EAAE,QAAQ,QAAQ,EAAE;AAAA,EAC3C;AAEA,MAAI,KAAM,QAAO,KAAK,IAAI;AAE1B,SAAO;AACT;;;AC1CO,IAAM,uBAAuB,KAAK,OAAO;AAezC,SAAS,uBAAuB,YAAkC;AACvE,MAAI,WAAW,OAAO,sBAAsB;AAC1C,UAAM,YAAY,WAAW,OAAO,OAAO,MAAM,QAAQ,CAAC;AAC1D,UAAM,UAAU,uBAAuB,OAAO;AAC9C,UAAM,WAAW,WAAW,OAAO,KAAK,WAAW,IAAI,KAAK;AAC5D,UAAM,IAAI;AAAA,MACR,yBAAyB,QAAQ,SAAS,OAAO,OAAO,CAAC,aAAa,QAAQ;AAAA,IAChF;AAAA,EACF;AACF;AAOA,IAAM,eAAe;AAEd,SAAS,uBACd,YACQ;AACR,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,OAAO,IAAI,QAAQ,cAAc,EAAE,EAAE,WAAW,GAAG;AACtD,WAAO,WAAW;AAAA,EACpB;AACA,SAAO,IAAI,QAAQ,cAAc,GAAG;AACtC;AAsBA,eAAsB,mBACpB,YAC+B;AAC/B,yBAAuB,UAAU;AAEjC,QAAM,MAAM,MAAM,MAAM,WAAW,GAAG;AACtC,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI;AAAA,MACR,kCAAkC,OAAO,IAAI,MAAM,CAAC,IAAI,IAAI,UAAU;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,KAAK,MAAM,IAAI,YAAY;AACjC,QAAM,SAAS,OAAO,KAAK,IAAI,GAAG,GAAG,UAAU;AAC/C,QAAM,WAAW,uBAAuB,UAAU;AAClD,QAAM,cAAc,WAAW,eAAe;AAE9C,SAAO,EAAE,QAAQ,UAAU,YAAY;AACzC;;;AHnEO,IAAM,kCAAkC;AAExC,IAAM,kBAAkB;AAAA,EAC7B,iCAAkB;AAAA,EAClB,iCAAkB;AAAA,EAClB,iCAAkB;AAAA,EAClB,iCAAkB;AACpB;AAEO,IAAM,mBAAmB,CAAC,wBAAS,SAAS,wBAAS,OAAO;AAK5D,IAAM,MAAN,MAAM,KAAI;AAAA,EACN;AAAA,EACA;AAAA,EAED;AAAA,EACS;AAAA,EACA;AAAA,EACT,eAA4B;AAAA,EACnB,kBAAoC,CAAC;AAAA,EACrC,gBAAgC,CAAC;AAAA,EACjC;AAAA,EAET,YACN,IACA,MACA,QACA,sBACA,oBACA;AACA,SAAK,KAAK;AACV,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,eAAe,IAAI,iCAAkB,EAAE,SAAS,qBAAqB,CAAC;AAC3E,SAAK,sBAAsB;AAC3B,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,IAAI,qBAA8B;AAChC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAAqB;AACvB,WAAO,KAAK,OAAO,OAAO,MAAM;AAAA,EAClC;AAAA,EAEA,IAAI,cAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,WAAW,QAAiC;AACvD,UAAM,YAAY,OAAO,wBAAwB;AACjD,QAAI,YAAY,KAAK,CAAC,OAAO,UAAU,SAAS,GAAG;AACjD,YAAM,IAAI;AAAA,QACR,wDAAwD,OAAO,SAAS,CAAC;AAAA,MAC3E;AAAA,IACF;AAEA,UAAM,qBAAqB,OAAO,sBAAsB;AAExD,UAAM,SAAS,IAAI,sBAAO;AAAA,MACxB,SAAS,OAAO,WAAW,CAAC,GAAG,eAAe;AAAA,MAC9C,UAAU,OAAO,YAAY,CAAC,GAAG,gBAAgB;AAAA,IACnD,CAAC;AAED,UAAM,MAAM,IAAI,KAAI,OAAO,IAAI,OAAO,MAAM,QAAQ,WAAW,kBAAkB;AAEjF,UAAM,IAAI,QAAQ,OAAO,KAAK;AAC9B,QAAI,OAAO;AACX,QAAI,mBAAmB;AACvB,QAAI,UAAU;AACd,QAAI,eAAe,oBAAI,KAAK;AAE5B,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,MAAM,KAAK,WAAmB,SAAiD;AAC7E,UAAM,UAAU,MAAM,KAAK,iBAAiB,SAAS;AACrD,QAAI,EAAE,UAAU,UAAU;AACxB,YAAM,IAAI,MAAM,WAAW,SAAS,4BAA4B;AAAA,IAClE;AACA,WAAO,KAAK;AAAA,MAAa;AAAA,MAAS,CAAC,YAChC,QAAiE,KAAK,OAAO;AAAA,IAChF;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,QAAgB,SAAiD;AAC5E,UAAM,OAAO,MAAM,KAAK,OAAO,MAAM,MAAM,MAAM;AACjD,WAAO,KAAK;AAAA,MAAa;AAAA,MAAS,CAAC,YACjC,KAAK,KAAK,OAA0C;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,WAAmB,WAAmB,SAAiD;AACjG,UAAM,EAAE,SAAS,QAAQ,IAAI,MAAM,KAAK,aAAa,WAAW,WAAW,UAAU;AACrF,QAAI,EAAE,UAAU,UAAU;AACxB,YAAM,IAAI,MAAM,WAAW,SAAS,4BAA4B;AAAA,IAClE;AACA,WAAO,KAAK,aAAa,SAAS,CAAC,SAAS,MAAM;AAChD,UAAI,MAAM,GAAG;AACX,eAAO,QAAQ,MAAM,OAA0C;AAAA,MACjE;AACA,aAAQ,QAAiE,KAAK,OAAO;AAAA,IACvF,CAAC;AAAA,EACH;AAAA;AAAA,EAIA,MAAM,MAAM,WAAmB,WAAmB,OAA8B;AAC9E,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,aAAa,WAAW,WAAW,UAAU;AAC5E,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK;AAAA,IAC3B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,2BAA2B,SAAS,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MACjG;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,WAAmB,WAAmB,SAAgC;AACtF,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,aAAa,WAAW,WAAW,MAAM;AACxE,QAAI;AACF,YAAM,QAAQ,KAAK,OAAO;AAAA,IAC5B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,uBAAuB,SAAS,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAC7F;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,WAAmB,OAA2C;AAChF,UAAM,UAAU,MAAM,KAAK,iBAAiB,SAAS;AACrD,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,IAAI,GAAG,CAAC;AACtD,UAAM,WAAW,MAAM,QAAQ,SAAS,MAAM,EAAE,OAAO,QAAQ,CAAC;AAChE,WAAO,CAAC,GAAG,SAAS,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,KAAK,iBAAiB,CAAC,CAAC;AAAA,EAC7E;AAAA,EAEA,OAAO,aACL,WACA,SACkC;AAClC,UAAM,EAAE,OAAO,QAAQ,QAAQ,IAAI,IAAI;AAGvC,QAAI,SAAS,QAAQ;AACnB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,QAAI;AACJ,QAAI;AACJ,QAAI,OAAO;AACT,eAAS;AACT,kBAAY;AAAA,IACd,WAAW,QAAQ;AACjB,eAAS;AACT,kBAAY;AAAA,IACd,OAAO;AACL,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAEA,UAAM,iBAAiB,KAAK,IAAI,OAAO,GAAI;AAC3C,UAAM,UAAU,MAAM,KAAK,iBAAiB,SAAS;AAErD,QAAI,eAAe;AAEnB,WAAO,eAAe,gBAAgB;AACpC,YAAM,WAAW,KAAK,IAAI,KAAK,iBAAiB,YAAY;AAE5D,YAAM,UAAU,MAAM,KAAK,mBAAmB,SAAS;AAAA,QACrD,OAAO;AAAA,QACP,CAAC,SAAS,GAAG;AAAA,MACf,CAAC;AAED,UAAI,QAAQ,SAAS,EAAG;AAExB,YAAM,cAAc,CAAC,GAAG,QAAQ,OAAO,CAAC;AACxC,YAAM,WAAW,YAAY,IAAI,CAAC,MAAM,KAAK,iBAAiB,CAAC,CAAC;AAEhE,YAAM;AACN,sBAAgB,SAAS;AAEzB,YAAM,cAAc,YAAY,GAAG,EAAE;AACrC,UAAI,CAAC,YAAa;AAClB,eAAS,YAAY;AAErB,UAAI,QAAQ,OAAO,SAAU;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,MAAM,sBACJ,WACA,WACmC;AACnC,UAAM,EAAE,QAAQ,IAAI,MAAM,KAAK,aAAa,WAAW,WAAW,uBAAuB;AACzF,WAAO,CAAC,GAAG,QAAQ,YAAY,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS;AAAA,MACrD,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,KAAK,IAAI;AAAA,MACT,aAAa,IAAI;AAAA,IACnB,EAAE;AAAA,EACJ;AAAA;AAAA,EAIA,UAAU,SAAqC;AAC7C,SAAK,gBAAgB,KAAK,OAAO;AACjC,QAAI,UAAU;AACd,WAAO,MAAM;AACX,UAAI,QAAS;AACb,gBAAU;AACV,YAAM,QAAQ,KAAK,gBAAgB,QAAQ,OAAO;AAClD,UAAI,UAAU,GAAI,MAAK,gBAAgB,OAAO,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AAAA;AAAA,EAIA,SAAS,SAAmE;AAC1E,UAAM,UAAkC,CAAC;AACzC,UAAM,SAAS,IAAI,YAA0B;AAAA,MAC3C,SAAS,MAAM,QAAQ,QAAQ;AAAA,IACjC,CAAC;AACD,YAAQ,QAAQ,KAAK,UAAU,CAAC,UAAU;AACxC,UAAI,SAAS,UAAU,CAAC,QAAQ,OAAO,KAAK,EAAG;AAC/C,aAAO,KAAK,KAAK;AAAA,IACnB,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,GAAG,QAAiB,SAAmC;AACrD,SAAK,cAAc,KAAK,OAAO;AAC/B,QAAI,UAAU;AACd,WAAO,MAAM;AACX,UAAI,QAAS;AACb,gBAAU;AACV,YAAM,QAAQ,KAAK,cAAc,QAAQ,OAAO;AAChD,UAAI,UAAU,GAAI,MAAK,cAAc,OAAO,OAAO,CAAC;AAAA,IACtD;AAAA,EACF;AAAA,EAEQ,UAAU,OAAsB;AACtC,UAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,eAAW,WAAW,KAAK,eAAe;AACxC,cAAQ,GAAG;AAAA,IACb;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,aAA4B;AAChC,SAAK,OAAO,mBAAmB;AAC/B,UAAM,KAAK,OAAO,QAAQ;AAC1B,SAAK,gBAAgB,SAAS;AAC9B,SAAK,cAAc,SAAS;AAC5B,SAAK,UAAU;AACf,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA,EAIA,MAAc,iBAAiB,WAAmB;AAChD,UAAM,UAAU,MAAM,KAAK,OAAO,SAAS,MAAM,SAAS;AAC1D,QAAI,CAAC,SAAS,YAAY,GAAG;AAC3B,YAAM,IAAI;AAAA,QACR,WAAW,SAAS;AAAA,MACtB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aACZ,WACA,WACA,MACsF;AACtF,UAAM,UAAU,MAAM,KAAK,iBAAiB,SAAS;AACrD,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,SAAS,MAAM,SAAS;AACtD,aAAO,EAAE,SAAS,QAAQ;AAAA,IAC5B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,UAAU,IAAI,YAAY,SAAS,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAChG;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,iBAAiB,KAA8B;AACrD,WAAO;AAAA,MACL,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,QAAQ;AAAA,QACN,IAAI,IAAI,OAAO;AAAA,QACf,UAAU,IAAI,OAAO;AAAA,QACrB,KAAK,IAAI,OAAO;AAAA,MAClB;AAAA,MACA,SAAS,IAAI;AAAA,MACb,WAAW,IAAI;AAAA,MACf,iBAAiB,IAAI,YAAY;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,SACA,SACA,aAAa,KACkB;AAC/B,QAAI;AACF,aAAO,MAAM,QAAQ,SAAS,MAAM,OAAO;AAAA,IAC7C,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,gBAAgB,KAAK;AAC/C,cAAM,EAAE,WAAW,IAAI;AACvB,YAAI,OAAO,eAAe,YAAY,cAAc,YAAY;AAC9D,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAC9D,iBAAO,QAAQ,SAAS,MAAM,OAAO;AAAA,QACvC;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,QAAQ,OAA8B;AAClD,SAAK,UAAU;AACf,UAAM,KAAK,OAAO,MAAM,KAAK;AAAA,EAC/B;AAAA,EAEQ,SAAe;AACrB,SAAK,UAAU;AAEf,QAAI,KAAK,OAAO,OAAO,MAAM,SAAS,GAAG;AACvC,WAAK,UAAU;AACf,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,qBAA2B;AACjC,SAAK,OAAO,GAAG,sBAAO,eAAe,CAAC,YAAqB;AACzD,UAAI;AAEF,YAAI,CAAC,QAAQ,OAAQ;AAErB,YAAI,QAAQ,OAAO,OAAO,KAAK,OAAO,MAAM,GAAI;AAEhD,YAAI,QAAQ,OAAO,OAAO,CAAC,KAAK,oBAAqB;AAErD,cAAM,QAAQ,KAAK,eAAe,OAAO;AAEzC,mBAAW,WAAW,KAAK,iBAAiB;AAC1C,kBAAQ,KAAK;AAAA,QACf;AAAA,MACF,SAAS,OAAO;AACd,aAAK,UAAU,KAAK;AAAA,MACtB;AAAA,IACF,CAAC;AAKD,SAAK,OAAO,GAAG,OAAiB,CAAC,WAAsF;AACrH,UAAI,OAAO,MAAM,iBAAkB;AACnC,UAAI,OAAO,EAAE,SAAU;AACvB,YAAM,YAAY,OAAO,EAAE;AAC3B,YAAM,YAAY,OAAO,EAAE;AAC3B,UAAI,CAAC,aAAa,CAAC,UAAW;AAC9B,UAAI,KAAK,OAAO,SAAS,MAAM,IAAI,SAAS,EAAG;AAE/C,WAAK,KAAK,iBAAiB,WAAW,SAAS;AAAA,IACjD,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,iBAAiB,WAAmB,WAAkC;AAClF,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,OAAO,SAAS,MAAM,SAAS;AAC1D,UAAI,CAAC,SAAS,YAAY,EAAG;AAE7B,YAAM,UAAU,MAAM,QAAQ,SAAS,MAAM,SAAS;AAGtD,UAAI,CAAC,QAAQ,OAAQ;AACrB,UAAI,QAAQ,OAAO,OAAO,KAAK,OAAO,MAAM,GAAI;AAChD,UAAI,QAAQ,OAAO,OAAO,CAAC,KAAK,oBAAqB;AAErD,YAAM,QAAQ,KAAK,eAAe,OAAO;AACzC,iBAAW,WAAW,KAAK,iBAAiB;AAC1C,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AACd,WAAK,UAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,eAAe,SAAgC;AACrD,UAAM,OAAO,QAAQ,YAAY;AACjC,UAAM,UAAU,KAAK,OAAO;AAC5B,UAAM,YAAY,SAAS,UAAU,QAAQ,SAAS,IAAI,QAAQ,EAAE,IAAI;AAExE,QAAI,UAAU;AACd,QAAI,UAA8B;AAClC,QAAI,QAAQ,WAAW,WAAW;AAChC,YAAM,WAAW,KAAK,aAAa,IAAI,QAAQ,UAAU,SAAS;AAClE,UAAI,UAAU;AACZ,kBAAU;AACV,kBAAU;AAAA,MACZ;AAAA,IACF;AAEA,WAAO;AAAA,MACL,WAAW,QAAQ;AAAA,MACnB,QAAQ;AAAA,QACN,IAAI,QAAQ,OAAO;AAAA,QACnB,UAAU,QAAQ,OAAO;AAAA,QACzB,KAAK,QAAQ,OAAO;AAAA,MACtB;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ,WAAW;AAAA,MAC5B,WAAW,QAAQ;AAAA,MACnB,UAAU,CAAC,GAAG,QAAQ,SAAS,MAAM,KAAK,CAAC;AAAA,MAC3C,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,aACZ,SACA,YACwB;AACxB,UAAM,QACJ,OAAO,YAAY,YAAY,QAAQ,OAAO,SAC1C,MAAM,4BAA4B,QAAQ,KAAK,IAC/C;AACN,UAAM,WAAW,kBAAkB,SAAS,KAAK;AACjD,UAAM,UAAyB,CAAC;AAChC,QAAI,IAAI;AACR,eAAW,WAAW,UAAU;AAC9B,YAAM,MAAM,MAAM,WAAW,SAAS,GAAG;AACzC,cAAQ,KAAK,KAAK,iBAAiB,GAAG,CAAC;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,SAA+B;AACtD,UAAM,OAAoB;AAAA,MACxB,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB,WAAW,QAAQ;AAAA,MACnB,KAAK;AAAA,IACP;AACA,SAAK,aAAa,IAAI,KAAK,WAAW,IAAI;AAC1C,WAAO;AAAA,EACT;AACF;AAQA,SAAS,kBACP,SACA,OACkB;AAClB,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAMA,UAAS,aAAa,OAAO;AACnC,WAAOA,QAAO,IAAI,CAACC,OAAM,OAAO;AAAA,MAC9B,SAASA;AAAA,MACT,GAAI,MAAM,KAAK,OAAO,SAAS,EAAE,MAAM,IAAI,CAAC;AAAA,IAC9C,EAAE;AAAA,EACJ;AACA,QAAM,OAAO,QAAQ,WAAW;AAChC,QAAM,SAAS,aAAa,IAAI;AAChC,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,CAAC,EAAE,QAAQ,QAAQ,QAAQ,GAAI,OAAO,SAAS,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAAA,EACzE;AACA,SAAO,OAAO,IAAI,CAAC,OAAO,OAAO;AAAA,IAC/B,SAAS;AAAA,IACT,GAAI,MAAM,KAAK,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IAC9D,GAAI,MAAM,KAAK,OAAO,SAAS,EAAE,MAAM,IAAI,CAAC;AAAA,EAC9C,EAAE;AACJ;AAEA,IAAM,wBAAwB;AAE9B,eAAe,4BACb,OAC8B;AAC9B,MAAI,MAAM,SAAS,uBAAuB;AACxC,UAAM,IAAI;AAAA,MACR,yBAAyB,OAAO,MAAM,MAAM,CAAC,+BAA+B,OAAO,qBAAqB,CAAC;AAAA,IAC3G;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,MAAM;AAAA,MAAI,CAAC,SACT,OAAO,KAAK,SAAS,eACjB,sBAAK,KAAK,IAAI,EAAE,KAAK,CAAC,OAAO,GAAG,IAAI,IACpC,QAAQ,QAAQ,KAAK,KAAK,UAAU;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO,MAAM,IAAI,CAAC,MAAM,MAAM;AAC5B,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,SAAS,OAAW,OAAM,IAAI,MAAM,gCAAgC,KAAK,IAAI,EAAE;AACnF,2BAAuB,EAAE,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK,KAAK,CAAC;AAC/D,WAAO,IAAI,iCAAkB,KAAK,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,EAC7D,CAAC;AACH;;;AIhiBA,IAAAC,kBAAuB;AAIvB,SAAS,eAAe,KAAsB;AAC5C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAIA,IAAM,qBAAN,MAAmD;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEiB;AAAA,EACA;AAAA,EAEjB,YACE,IACA,OACA,cACA,QACA,eAAqC,CAAC,GACtC,gBAAsC,WACtC;AACA,SAAK,KAAK;AACV,SAAK,QAAQ;AACb,SAAK,eAAe;AACpB,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,YAAY,MAAsC;AAEtD,eAAW,OAAO,KAAK,eAAe;AACpC,UAAI,IAAI,WAAW,aAAa;AAC9B,cAAM,QAAQ,oBAAoB,KAAK,EAAE,kBAAkB,IAAI,EAAE;AACjE,aAAK,SAAS;AACd,aAAK,QAAQ;AACb,eAAO,EAAE,SAAS,OAAO,MAAM;AAAA,MACjC;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,GAAG,IAAI;AACzC,UAAI,OAAO,SAAS;AAClB,aAAK,SAAS;AACd,aAAK,QAAQ;AAEb,YAAI,KAAK,aAAa,WAAW,WAAW;AAC1C,eAAK,YAAY,SAAS;AAC1B,iBAAO,WAAW,KAAK;AAAA,QACzB;AAAA,MACF,OAAO;AACL,aAAK,SAAS;AACd,aAAK,QAAQ,OAAO;AAAA,MACtB;AACA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,GAAG;AAClC,WAAK,SAAS;AACd,WAAK,QAAQ;AACb,aAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,IAC1C;AAAA,EACF;AACF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EAED;AAAA,EACA;AAAA,EAER,YAAY,IAAY,MAAc;AACpC,SAAK,KAAK;AACV,SAAK,OAAO;AACZ,UAAM,eAAe,IAAI;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,SAAoB;AAC5B,cAAM,QAAQ,KAAK,CAAC;AACpB,cAAM,SAAS,IAAI,uBAAO,EAAE,SAAS,CAAC,GAAG,eAAe,GAAG,UAAU,CAAC,GAAG,gBAAgB,EAAE,CAAC;AAC5F,YAAI;AACF,gBAAM,OAAO,MAAM,KAAK;AACxB,gBAAM,OAAO,QAAQ;AACrB,eAAK,SAAS;AACd,iBAAO,EAAE,SAAS,KAAK;AAAA,QACzB,SAAS,KAAK;AACZ,iBAAO,EAAE,SAAS,OAAO,OAAO,eAAe,GAAG,EAAE;AAAA,QACtD;AAAA,MACF;AAAA,MACA,CAAC;AAAA,MACD;AAAA,IACF;AAEA,UAAM,iBAAiB,IAAI;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AACV,YAAI,CAAC,KAAK,QAAQ;AAChB,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO;AAAA,UACT;AAAA,QACF;AACA,cAAM,SAAS,IAAI,uBAAO,EAAE,SAAS,CAAC,GAAG,eAAe,GAAG,UAAU,CAAC,GAAG,gBAAgB,EAAE,CAAC;AAC5F,YAAI;AACF,gBAAM,OAAO,MAAM,KAAK,MAAM;AAC9B,cAAI,OAAO,OAAO,MAAM,SAAS,GAAG;AAClC,kBAAM,OAAO,QAAQ;AACrB,mBAAO,EAAE,SAAS,OAAO,OAAO,+BAA+B;AAAA,UACjE;AACA,gBAAM,OAAO,QAAQ;AACrB,iBAAO,EAAE,SAAS,KAAK;AAAA,QACzB,SAAS,KAAK;AACZ,iBAAO,EAAE,SAAS,OAAO,OAAO,eAAe,GAAG,EAAE;AAAA,QACtD;AAAA,MACF;AAAA,MACA,CAAC,YAAY;AAAA,IACf;AAEA,UAAM,oBAAoB,IAAI;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AACV,YAAI,CAAC,KAAK,QAAQ;AAChB,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO;AAAA,UACT;AAAA,QACF;AACA,YAAI;AACF,eAAK,OAAO,MAAM,IAAI,WAAW;AAAA,YAC/B,IAAI,KAAK;AAAA,YACT,MAAM,KAAK;AAAA,YACX,OAAO,KAAK;AAAA,UACd,CAAC;AACD,iBAAO,EAAE,SAAS,KAAK;AAAA,QACzB,SAAS,KAAK;AACZ,iBAAO,EAAE,SAAS,OAAO,OAAO,eAAe,GAAG,EAAE;AAAA,QACtD;AAAA,MACF;AAAA,MACA,CAAC,cAAc;AAAA,IACjB;AAGA,iBAAa,cAAc;AAC3B,mBAAe,cAAc;AAE7B,SAAK,QAAQ,CAAC,cAAc,gBAAgB,iBAAiB;AAAA,EAC/D;AAAA,EAEA,IAAI,MAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AACF;;;AC/JO,IAAM,mBAAN,MAAuB;AAAA,EACpB,SAAS,oBAAI,IAAiB;AAAA,EAC9B,mBAAmB,oBAAI,IAA4B;AAAA,EACnD,kBAA+D,CAAC;AAAA,EAChE,oBAAoB,oBAAI,IAAsD;AAAA,EAEtF,OAAO,KAAgB;AACrB,QAAI,KAAK,OAAO,IAAI,IAAI,EAAE,GAAG;AAC3B,YAAM,IAAI,MAAM,gBAAgB,IAAI,EAAE,mCAAmC;AAAA,IAC3E;AACA,SAAK,OAAO,IAAI,IAAI,IAAI,GAAG;AAE3B,UAAM,SAAyB,CAAC;AAChC,WAAO;AAAA,MACL,IAAI,UAAU,CAAC,UAAU;AACvB,mBAAW,WAAW,KAAK,iBAAiB;AAC1C,kBAAQ,OAAO,GAAG;AAAA,QACpB;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,MACL,IAAI,GAAG,SAAS,CAAC,UAAU;AACzB,aAAK,cAAc,YAAY,KAAK,KAAK;AAAA,MAC3C,CAAC;AAAA,IACH;AACA,SAAK,iBAAiB,IAAI,IAAI,IAAI,MAAM;AAAA,EAC1C;AAAA,EAEA,OAAO,IAA6B;AAClC,WAAO,KAAK,OAAO,IAAI,EAAE;AAAA,EAC3B;AAAA,EAEA,IAAI,OAAsB;AACxB,WAAO,KAAK,OAAO,OAAO;AAAA,EAC5B;AAAA,EAEA,UAAU,IAAkB;AAC1B,UAAM,SAAS,KAAK,iBAAiB,IAAI,EAAE;AAC3C,QAAI,QAAQ;AACV,iBAAW,SAAS,OAAQ,OAAM;AAClC,WAAK,iBAAiB,OAAO,EAAE;AAAA,IACjC;AACA,SAAK,OAAO,OAAO,EAAE;AAAA,EACvB;AAAA,EAEA,SAAyD;AACvD,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS;AAAA,MAC7C,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,IACd,EAAE;AAAA,EACJ;AAAA,EAEA,UAAU,SAAwD;AAChE,SAAK,gBAAgB,KAAK,OAAO;AAAA,EACnC;AAAA,EAEA,GAAG,OAAuB,UAA8C;AACtE,UAAM,WAAW,KAAK,kBAAkB,IAAI,KAAK,KAAK,CAAC;AACvD,aAAS,KAAK,QAAQ;AACtB,SAAK,kBAAkB,IAAI,OAAO,QAAQ;AAAA,EAC5C;AAAA,EAEQ,cAAc,UAA0B,MAAuB;AACrE,UAAM,WAAW,KAAK,kBAAkB,IAAI,KAAK;AACjD,QAAI,UAAU;AACZ,iBAAW,WAAW,UAAU;AAC9B,gBAAQ,GAAG,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAW,IAAY,MAA6B;AAClD,WAAO,IAAI,cAAc,IAAI,IAAI;AAAA,EACnC;AAAA,EAEA,MAAM,gBAA+B;AACnC,UAAM,QAAQ,IAAI,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,CAAC;AAAA,EAC5E;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,KAAK,cAAc;AAAA,EAC3B;AACF;","names":["chunks","text","import_discord"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["// discord-bot-lib — public API\n\nexport { Bot, DEFAULT_SENT_MESSAGE_CACHE_SIZE } from \"./bot.js\";\nexport { BotOnboarding } from \"./onboarding.js\";\nexport { ConnectorManager } from \"./connector-manager.js\";\nexport { EventBuffer } from \"./event-buffer.js\";\nexport {\n chunkMessage,\n DISCORD_MAX_MESSAGE_LENGTH,\n} from \"./message-utils.js\";\nexport {\n MAX_ATTACHMENT_BYTES,\n validateAttachmentSize,\n sanitizeAttachmentName,\n downloadAttachment,\n} from \"./attachment-utils.js\";\n\nexport type {\n AttachmentLike,\n DownloadableAttachment,\n DownloadedAttachment,\n} from \"./attachment-utils.js\";\n\nexport type {\n Author,\n BotConfig,\n BotStatus,\n BotStateAdapter,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n TypingEvent,\n FileAttachment,\n FetchedMessage,\n FetchHistoryOptions,\n OnboardingStep,\n OnboardingStepStatus,\n StepResult,\n ManagerEvents,\n} from \"./types.js\";\n","import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message, type Typing } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n TypingEvent,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype TypingHandler = (event: TypingEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly typingHandlers: TypingHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n private readonly resolvedIntents: readonly GatewayIntentBits[];\n private typingListenerAttached = false;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n resolvedIntents: readonly GatewayIntentBits[],\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this.resolvedIntents = resolvedIntents;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n const intents = config.intents ?? [...DEFAULT_INTENTS];\n\n const client = new Client({\n intents,\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendTyping(channelId: string): Promise<void> {\n const channel = await this.fetchTextChannel(channelId);\n const typingChannel = channel as unknown as { sendTyping?: () => Promise<void> };\n if (typeof typingChannel.sendTyping !== \"function\") {\n throw new Error(`Channel ${channelId} does not support typing indicators`);\n }\n try {\n await typingChannel.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendDMTyping(userId: string): Promise<void> {\n try {\n const user = await this.client.users.fetch(userId);\n const dm = await user.createDM();\n await dm.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n onTypingStart(handler: TypingHandler): () => void {\n const hasGuildTyping = this.resolvedIntents.includes(GatewayIntentBits.GuildMessageTyping);\n const hasDMTyping = this.resolvedIntents.includes(GatewayIntentBits.DirectMessageTyping);\n if (!hasGuildTyping && !hasDMTyping) {\n throw new Error(\n \"onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. \" +\n \"Add them to the intents array in BotConfig.\",\n );\n }\n\n if (!this.typingListenerAttached) {\n this.typingListenerAttached = true;\n this.client.on(Events.TypingStart, (typing: Typing) => {\n try {\n const event = this.toTypingEvent(typing);\n for (const h of this.typingHandlers) {\n h(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n this.typingHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.typingHandlers.indexOf(handler);\n if (index !== -1) this.typingHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.typingHandlers.length = 0;\n this.typingListenerAttached = false;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private toTypingEvent(typing: Typing): TypingEvent {\n return {\n channelId: typing.channel.id,\n userId: typing.user.id,\n startedAt: typing.startedAt,\n guildId: typing.guild?.id ?? null,\n raw: typing,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent, TypingEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private typingHandlers: ((event: TypingEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n if (this.typingHandlers.length > 0) {\n this.subscribeTyping(bot, unsubs);\n }\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void {\n const isFirst = this.typingHandlers.length === 0;\n this.typingHandlers.push(handler);\n if (isFirst) {\n for (const [id, bot] of this.botMap) {\n const unsubs = this.botUnsubscribers.get(id) ?? [];\n this.subscribeTyping(bot, unsubs);\n }\n }\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private subscribeTyping(bot: Bot, unsubs: (() => void)[]): void {\n try {\n unsubs.push(\n bot.onTypingStart((event) => {\n for (const handler of this.typingHandlers) {\n handler(event, bot);\n }\n }),\n );\n } catch (error) {\n // Re-throw unexpected errors; only suppress the known intent-check error\n const isIntentError = error instanceof Error && error.message.startsWith(\"onTypingStart requires\");\n if (!isIntentError) throw error;\n }\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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","names":["chunks","text","import_discord"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { GatewayIntentBits, Partials, Message } from 'discord.js';
1
+ import { GatewayIntentBits, Partials, Message, Typing } from 'discord.js';
2
2
 
3
3
  type BotStatus = "unregistered" | "configuring" | "connecting" | "verifying" | "ready" | "disconnected" | "failed";
4
4
  interface BotStateAdapter {
@@ -76,6 +76,13 @@ type FetchHistoryOptions = {
76
76
  limit?: number;
77
77
  };
78
78
  type MessageFilter = (message: MessageEvent) => boolean;
79
+ interface TypingEvent {
80
+ channelId: string;
81
+ userId: string;
82
+ startedAt: Date;
83
+ guildId: string | null;
84
+ raw: Typing;
85
+ }
79
86
  type OnboardingStepStatus = "pending" | "ready" | "completed" | "failed";
80
87
  interface StepResult {
81
88
  success: boolean;
@@ -143,6 +150,7 @@ declare function downloadAttachment(attachment: DownloadableAttachment): Promise
143
150
 
144
151
  declare const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;
145
152
  type MessageHandler = (event: MessageEvent) => void;
153
+ type TypingHandler = (event: TypingEvent) => void;
146
154
  type ErrorHandler = (error: Error) => void;
147
155
  declare class Bot {
148
156
  readonly id: string;
@@ -152,8 +160,11 @@ declare class Bot {
152
160
  private readonly sentMessages;
153
161
  private _connectedAt;
154
162
  private readonly messageHandlers;
163
+ private readonly typingHandlers;
155
164
  private readonly errorHandlers;
156
165
  private readonly _includeBotMessages;
166
+ private readonly resolvedIntents;
167
+ private typingListenerAttached;
157
168
  private constructor();
158
169
  get includeBotMessages(): boolean;
159
170
  get status(): BotStatus;
@@ -169,10 +180,13 @@ declare class Bot {
169
180
  reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]>;
170
181
  react(channelId: string, messageId: string, emoji: string): Promise<void>;
171
182
  editMessage(channelId: string, messageId: string, content: string): Promise<void>;
183
+ sendTyping(channelId: string): Promise<void>;
184
+ sendDMTyping(userId: string): Promise<void>;
172
185
  fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]>;
173
186
  fetchHistory(channelId: string, options: FetchHistoryOptions): AsyncGenerator<FetchedMessage[]>;
174
187
  getMessageAttachments(channelId: string, messageId: string): Promise<DownloadableAttachment[]>;
175
188
  onMessage(handler: MessageHandler): () => void;
189
+ onTypingStart(handler: TypingHandler): () => void;
176
190
  messages(options?: {
177
191
  filter?: MessageFilter;
178
192
  }): AsyncIterable<MessageEvent>;
@@ -188,6 +202,7 @@ declare class Bot {
188
202
  private setupEventHandlers;
189
203
  private handleUncachedDM;
190
204
  private toMessageEvent;
205
+ private toTypingEvent;
191
206
  private sendPayloads;
192
207
  private trackSentMessage;
193
208
  }
@@ -207,6 +222,7 @@ declare class ConnectorManager {
207
222
  private botMap;
208
223
  private botUnsubscribers;
209
224
  private messageHandlers;
225
+ private typingHandlers;
210
226
  private lifecycleHandlers;
211
227
  addBot(bot: Bot): void;
212
228
  getBot(id: string): Bot | undefined;
@@ -218,7 +234,9 @@ declare class ConnectorManager {
218
234
  status: string;
219
235
  }[];
220
236
  onMessage(handler: (event: MessageEvent, bot: Bot) => void): void;
237
+ onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void;
221
238
  on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void;
239
+ private subscribeTyping;
222
240
  private emitLifecycle;
223
241
  onboardBot(id: string, name: string): BotOnboarding;
224
242
  disconnectAll(): Promise<void>;
@@ -256,4 +274,4 @@ declare const DISCORD_MAX_MESSAGE_LENGTH = 2000;
256
274
  */
257
275
  declare function chunkMessage(text: string, limit?: number): string[];
258
276
 
259
- export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
277
+ export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, type TypingEvent, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GatewayIntentBits, Partials, Message } from 'discord.js';
1
+ import { GatewayIntentBits, Partials, Message, Typing } from 'discord.js';
2
2
 
3
3
  type BotStatus = "unregistered" | "configuring" | "connecting" | "verifying" | "ready" | "disconnected" | "failed";
4
4
  interface BotStateAdapter {
@@ -76,6 +76,13 @@ type FetchHistoryOptions = {
76
76
  limit?: number;
77
77
  };
78
78
  type MessageFilter = (message: MessageEvent) => boolean;
79
+ interface TypingEvent {
80
+ channelId: string;
81
+ userId: string;
82
+ startedAt: Date;
83
+ guildId: string | null;
84
+ raw: Typing;
85
+ }
79
86
  type OnboardingStepStatus = "pending" | "ready" | "completed" | "failed";
80
87
  interface StepResult {
81
88
  success: boolean;
@@ -143,6 +150,7 @@ declare function downloadAttachment(attachment: DownloadableAttachment): Promise
143
150
 
144
151
  declare const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;
145
152
  type MessageHandler = (event: MessageEvent) => void;
153
+ type TypingHandler = (event: TypingEvent) => void;
146
154
  type ErrorHandler = (error: Error) => void;
147
155
  declare class Bot {
148
156
  readonly id: string;
@@ -152,8 +160,11 @@ declare class Bot {
152
160
  private readonly sentMessages;
153
161
  private _connectedAt;
154
162
  private readonly messageHandlers;
163
+ private readonly typingHandlers;
155
164
  private readonly errorHandlers;
156
165
  private readonly _includeBotMessages;
166
+ private readonly resolvedIntents;
167
+ private typingListenerAttached;
157
168
  private constructor();
158
169
  get includeBotMessages(): boolean;
159
170
  get status(): BotStatus;
@@ -169,10 +180,13 @@ declare class Bot {
169
180
  reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]>;
170
181
  react(channelId: string, messageId: string, emoji: string): Promise<void>;
171
182
  editMessage(channelId: string, messageId: string, content: string): Promise<void>;
183
+ sendTyping(channelId: string): Promise<void>;
184
+ sendDMTyping(userId: string): Promise<void>;
172
185
  fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]>;
173
186
  fetchHistory(channelId: string, options: FetchHistoryOptions): AsyncGenerator<FetchedMessage[]>;
174
187
  getMessageAttachments(channelId: string, messageId: string): Promise<DownloadableAttachment[]>;
175
188
  onMessage(handler: MessageHandler): () => void;
189
+ onTypingStart(handler: TypingHandler): () => void;
176
190
  messages(options?: {
177
191
  filter?: MessageFilter;
178
192
  }): AsyncIterable<MessageEvent>;
@@ -188,6 +202,7 @@ declare class Bot {
188
202
  private setupEventHandlers;
189
203
  private handleUncachedDM;
190
204
  private toMessageEvent;
205
+ private toTypingEvent;
191
206
  private sendPayloads;
192
207
  private trackSentMessage;
193
208
  }
@@ -207,6 +222,7 @@ declare class ConnectorManager {
207
222
  private botMap;
208
223
  private botUnsubscribers;
209
224
  private messageHandlers;
225
+ private typingHandlers;
210
226
  private lifecycleHandlers;
211
227
  addBot(bot: Bot): void;
212
228
  getBot(id: string): Bot | undefined;
@@ -218,7 +234,9 @@ declare class ConnectorManager {
218
234
  status: string;
219
235
  }[];
220
236
  onMessage(handler: (event: MessageEvent, bot: Bot) => void): void;
237
+ onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void;
221
238
  on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void;
239
+ private subscribeTyping;
222
240
  private emitLifecycle;
223
241
  onboardBot(id: string, name: string): BotOnboarding;
224
242
  disconnectAll(): Promise<void>;
@@ -256,4 +274,4 @@ declare const DISCORD_MAX_MESSAGE_LENGTH = 2000;
256
274
  */
257
275
  declare function chunkMessage(text: string, limit?: number): string[];
258
276
 
259
- export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
277
+ export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, type TypingEvent, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
package/dist/index.js CHANGED
@@ -128,14 +128,18 @@ var Bot = class _Bot {
128
128
  sentMessages;
129
129
  _connectedAt = null;
130
130
  messageHandlers = [];
131
+ typingHandlers = [];
131
132
  errorHandlers = [];
132
133
  _includeBotMessages;
133
- constructor(id, name, client, sentMessageCacheSize, includeBotMessages) {
134
+ resolvedIntents;
135
+ typingListenerAttached = false;
136
+ constructor(id, name, client, sentMessageCacheSize, includeBotMessages, resolvedIntents) {
134
137
  this.id = id;
135
138
  this.name = name;
136
139
  this.client = client;
137
140
  this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });
138
141
  this._includeBotMessages = includeBotMessages;
142
+ this.resolvedIntents = resolvedIntents;
139
143
  this._status = "connecting";
140
144
  }
141
145
  get includeBotMessages() {
@@ -162,11 +166,12 @@ var Bot = class _Bot {
162
166
  );
163
167
  }
164
168
  const includeBotMessages = config.includeBotMessages ?? false;
169
+ const intents = config.intents ?? [...DEFAULT_INTENTS];
165
170
  const client = new Client({
166
- intents: config.intents ?? [...DEFAULT_INTENTS],
171
+ intents,
167
172
  partials: config.partials ?? [...DEFAULT_PARTIALS]
168
173
  });
169
- const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages);
174
+ const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);
170
175
  await bot.connect(config.token);
171
176
  bot.verify();
172
177
  bot.setupEventHandlers();
@@ -225,6 +230,31 @@ var Bot = class _Bot {
225
230
  );
226
231
  }
227
232
  }
233
+ async sendTyping(channelId) {
234
+ const channel = await this.fetchTextChannel(channelId);
235
+ const typingChannel = channel;
236
+ if (typeof typingChannel.sendTyping !== "function") {
237
+ throw new Error(`Channel ${channelId} does not support typing indicators`);
238
+ }
239
+ try {
240
+ await typingChannel.sendTyping();
241
+ } catch (error) {
242
+ throw new Error(
243
+ `Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`
244
+ );
245
+ }
246
+ }
247
+ async sendDMTyping(userId) {
248
+ try {
249
+ const user = await this.client.users.fetch(userId);
250
+ const dm = await user.createDM();
251
+ await dm.sendTyping();
252
+ } catch (error) {
253
+ throw new Error(
254
+ `Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`
255
+ );
256
+ }
257
+ }
228
258
  async fetchMessages(channelId, limit) {
229
259
  const channel = await this.fetchTextChannel(channelId);
230
260
  const clamped = Math.max(1, Math.min(limit ?? 20, 100));
@@ -288,6 +318,36 @@ var Bot = class _Bot {
288
318
  if (index !== -1) this.messageHandlers.splice(index, 1);
289
319
  };
290
320
  }
321
+ onTypingStart(handler) {
322
+ const hasGuildTyping = this.resolvedIntents.includes(GatewayIntentBits.GuildMessageTyping);
323
+ const hasDMTyping = this.resolvedIntents.includes(GatewayIntentBits.DirectMessageTyping);
324
+ if (!hasGuildTyping && !hasDMTyping) {
325
+ throw new Error(
326
+ "onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. Add them to the intents array in BotConfig."
327
+ );
328
+ }
329
+ if (!this.typingListenerAttached) {
330
+ this.typingListenerAttached = true;
331
+ this.client.on(Events.TypingStart, (typing) => {
332
+ try {
333
+ const event = this.toTypingEvent(typing);
334
+ for (const h of this.typingHandlers) {
335
+ h(event);
336
+ }
337
+ } catch (error) {
338
+ this.emitError(error);
339
+ }
340
+ });
341
+ }
342
+ this.typingHandlers.push(handler);
343
+ let removed = false;
344
+ return () => {
345
+ if (removed) return;
346
+ removed = true;
347
+ const index = this.typingHandlers.indexOf(handler);
348
+ if (index !== -1) this.typingHandlers.splice(index, 1);
349
+ };
350
+ }
291
351
  // --- Receiving (async iterable) ---
292
352
  messages(options) {
293
353
  const cleanup = {};
@@ -322,6 +382,8 @@ var Bot = class _Bot {
322
382
  this.client.removeAllListeners();
323
383
  await this.client.destroy();
324
384
  this.messageHandlers.length = 0;
385
+ this.typingHandlers.length = 0;
386
+ this.typingListenerAttached = false;
325
387
  this.errorHandlers.length = 0;
326
388
  this._status = "disconnected";
327
389
  this._connectedAt = null;
@@ -460,6 +522,15 @@ var Bot = class _Bot {
460
522
  replyTo
461
523
  };
462
524
  }
525
+ toTypingEvent(typing) {
526
+ return {
527
+ channelId: typing.channel.id,
528
+ userId: typing.user.id,
529
+ startedAt: typing.startedAt,
530
+ guildId: typing.guild?.id ?? null,
531
+ raw: typing
532
+ };
533
+ }
463
534
  async sendPayloads(content, dispatcher) {
464
535
  const files = typeof content !== "string" && content.files?.length ? await validateAndBuildAttachments(content.files) : void 0;
465
536
  const payloads = toChunkedPayloads(content, files);
@@ -666,6 +737,7 @@ var ConnectorManager = class {
666
737
  botMap = /* @__PURE__ */ new Map();
667
738
  botUnsubscribers = /* @__PURE__ */ new Map();
668
739
  messageHandlers = [];
740
+ typingHandlers = [];
669
741
  lifecycleHandlers = /* @__PURE__ */ new Map();
670
742
  addBot(bot) {
671
743
  if (this.botMap.has(bot.id)) {
@@ -685,6 +757,9 @@ var ConnectorManager = class {
685
757
  this.emitLifecycle("botError", bot, error);
686
758
  })
687
759
  );
760
+ if (this.typingHandlers.length > 0) {
761
+ this.subscribeTyping(bot, unsubs);
762
+ }
688
763
  this.botUnsubscribers.set(bot.id, unsubs);
689
764
  }
690
765
  getBot(id) {
@@ -711,11 +786,35 @@ var ConnectorManager = class {
711
786
  onMessage(handler) {
712
787
  this.messageHandlers.push(handler);
713
788
  }
789
+ onTypingStart(handler) {
790
+ const isFirst = this.typingHandlers.length === 0;
791
+ this.typingHandlers.push(handler);
792
+ if (isFirst) {
793
+ for (const [id, bot] of this.botMap) {
794
+ const unsubs = this.botUnsubscribers.get(id) ?? [];
795
+ this.subscribeTyping(bot, unsubs);
796
+ }
797
+ }
798
+ }
714
799
  on(event, callback) {
715
800
  const handlers = this.lifecycleHandlers.get(event) ?? [];
716
801
  handlers.push(callback);
717
802
  this.lifecycleHandlers.set(event, handlers);
718
803
  }
804
+ subscribeTyping(bot, unsubs) {
805
+ try {
806
+ unsubs.push(
807
+ bot.onTypingStart((event) => {
808
+ for (const handler of this.typingHandlers) {
809
+ handler(event, bot);
810
+ }
811
+ })
812
+ );
813
+ } catch (error) {
814
+ const isIntentError = error instanceof Error && error.message.startsWith("onTypingStart requires");
815
+ if (!isIntentError) throw error;
816
+ }
817
+ }
719
818
  emitLifecycle(event, ...args) {
720
819
  const handlers = this.lifecycleHandlers.get(event);
721
820
  if (handlers) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n\n const client = new Client({\n intents: config.intents ?? [...DEFAULT_INTENTS],\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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","names":["chunks","text","Client","Client"]}
1
+ {"version":3,"sources":["../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message, type Typing } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n TypingEvent,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype TypingHandler = (event: TypingEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly typingHandlers: TypingHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n private readonly resolvedIntents: readonly GatewayIntentBits[];\n private typingListenerAttached = false;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n resolvedIntents: readonly GatewayIntentBits[],\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this.resolvedIntents = resolvedIntents;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n const intents = config.intents ?? [...DEFAULT_INTENTS];\n\n const client = new Client({\n intents,\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendTyping(channelId: string): Promise<void> {\n const channel = await this.fetchTextChannel(channelId);\n const typingChannel = channel as unknown as { sendTyping?: () => Promise<void> };\n if (typeof typingChannel.sendTyping !== \"function\") {\n throw new Error(`Channel ${channelId} does not support typing indicators`);\n }\n try {\n await typingChannel.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendDMTyping(userId: string): Promise<void> {\n try {\n const user = await this.client.users.fetch(userId);\n const dm = await user.createDM();\n await dm.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n onTypingStart(handler: TypingHandler): () => void {\n const hasGuildTyping = this.resolvedIntents.includes(GatewayIntentBits.GuildMessageTyping);\n const hasDMTyping = this.resolvedIntents.includes(GatewayIntentBits.DirectMessageTyping);\n if (!hasGuildTyping && !hasDMTyping) {\n throw new Error(\n \"onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. \" +\n \"Add them to the intents array in BotConfig.\",\n );\n }\n\n if (!this.typingListenerAttached) {\n this.typingListenerAttached = true;\n this.client.on(Events.TypingStart, (typing: Typing) => {\n try {\n const event = this.toTypingEvent(typing);\n for (const h of this.typingHandlers) {\n h(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n this.typingHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.typingHandlers.indexOf(handler);\n if (index !== -1) this.typingHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.typingHandlers.length = 0;\n this.typingListenerAttached = false;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private toTypingEvent(typing: Typing): TypingEvent {\n return {\n channelId: typing.channel.id,\n userId: typing.user.id,\n startedAt: typing.startedAt,\n guildId: typing.guild?.id ?? null,\n raw: typing,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent, TypingEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private typingHandlers: ((event: TypingEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n if (this.typingHandlers.length > 0) {\n this.subscribeTyping(bot, unsubs);\n }\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void {\n const isFirst = this.typingHandlers.length === 0;\n this.typingHandlers.push(handler);\n if (isFirst) {\n for (const [id, bot] of this.botMap) {\n const unsubs = this.botUnsubscribers.get(id) ?? [];\n this.subscribeTyping(bot, unsubs);\n }\n }\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private subscribeTyping(bot: Bot, unsubs: (() => void)[]): void {\n try {\n unsubs.push(\n bot.onTypingStart((event) => {\n for (const handler of this.typingHandlers) {\n handler(event, bot);\n }\n }),\n );\n } catch (error) {\n // Re-throw unexpected errors; only suppress the known intent-check error\n const isIntentError = error instanceof Error && error.message.startsWith(\"onTypingStart requires\");\n if (!isIntentError) throw error;\n }\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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","names":["chunks","text","Client","Client"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scotthamilton77/discord-bot-lib",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "TypeScript library for managing Discord bot identities, messaging, and multi-bot coordination",
5
5
  "type": "module",
6
6
  "sideEffects": false,