@scotthamilton77/discord-bot-lib 0.1.0 → 0.1.2

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
@@ -1,6 +1,9 @@
1
1
  # @scotthamilton77/discord-bot-lib
2
2
 
3
- <!-- Badges: npm version, CI status, license, Node version — add when publishing -->
3
+ [![npm version](https://img.shields.io/npm/v/@scotthamilton77/discord-bot-lib)](https://www.npmjs.com/package/@scotthamilton77/discord-bot-lib)
4
+ [![CI](https://github.com/scotthamilton77/discord-bot-lib/actions/workflows/ci.yml/badge.svg)](https://github.com/scotthamilton77/discord-bot-lib/actions/workflows/ci.yml)
5
+ [![License: ISC](https://img.shields.io/npm/l/@scotthamilton77/discord-bot-lib)](./LICENSE)
6
+ [![Node >= 20](https://img.shields.io/node/v/@scotthamilton77/discord-bot-lib)](https://nodejs.org/)
4
7
 
5
8
  TypeScript library for managing Discord bot identities, messaging, and multi-bot coordination. Wraps discord.js with a clean API surface: factory-based construction, event-driven callbacks, async iterables, and built-in message chunking.
6
9
 
package/dist/index.cjs CHANGED
@@ -145,6 +145,7 @@ var DEFAULT_INTENTS = [
145
145
  import_discord.GatewayIntentBits.MessageContent,
146
146
  import_discord.GatewayIntentBits.DirectMessages
147
147
  ];
148
+ var DEFAULT_PARTIALS = [import_discord.Partials.Channel, import_discord.Partials.Message];
148
149
  var Bot = class _Bot {
149
150
  id;
150
151
  name;
@@ -190,7 +191,8 @@ var Bot = class _Bot {
190
191
  }
191
192
  const includeBotMessages = config.includeBotMessages ?? false;
192
193
  const client = new import_discord.Client({
193
- intents: config.intents ?? [...DEFAULT_INTENTS]
194
+ intents: config.intents ?? [...DEFAULT_INTENTS],
195
+ partials: config.partials ?? [...DEFAULT_PARTIALS]
194
196
  });
195
197
  const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages);
196
198
  await bot.connect(config.token);
@@ -446,7 +448,8 @@ var Bot = class _Bot {
446
448
  if (message.author.bot && !this._includeBotMessages) return;
447
449
  const event = this.toMessageEvent(message);
448
450
  const botUser = this.client.user;
449
- if (botUser && message.mentions.has(botUser.id)) {
451
+ const isDM = message.channel.type === import_discord.ChannelType.DM;
452
+ if (botUser && (message.mentions.has(botUser.id) || isDM)) {
450
453
  for (const handler of this.mentionHandlers) {
451
454
  handler(event);
452
455
  }
@@ -616,7 +619,7 @@ var BotOnboarding = class {
616
619
  "Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.",
617
620
  async (...args) => {
618
621
  const token = args[0];
619
- const client = new import_discord2.Client({ intents: [...DEFAULT_INTENTS] });
622
+ const client = new import_discord2.Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });
620
623
  try {
621
624
  await client.login(token);
622
625
  await client.destroy();
@@ -640,7 +643,7 @@ var BotOnboarding = class {
640
643
  error: "Token not set \u2014 complete the provide_token step first."
641
644
  };
642
645
  }
643
- const client = new import_discord2.Client({ intents: [...DEFAULT_INTENTS] });
646
+ const client = new import_discord2.Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });
644
647
  try {
645
648
  await client.login(this._token);
646
649
  if (client.guilds.cache.size === 0) {
@@ -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, 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\ntype MentionHandler = (message: MessageEvent) => void;\ntype ReplyHandler = (message: MessageEvent, original: SentMessage) => void;\ntype MessageHandler = (message: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\ninterface ChannelSubscription {\n filter?: MessageFilter;\n handler: MessageHandler;\n}\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 mentionHandlers: MentionHandler[] = [];\n private readonly replyHandlers: ReplyHandler[] = [];\n private readonly channelSubscriptions = new Map<string, ChannelSubscription[]>();\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 });\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 = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n let message: Message;\n try {\n message = await channel.messages.fetch(messageId);\n } catch (error) {\n throw new Error(\n `Cannot reply to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 } catch (error) {\n throw new Error(\n `Cannot fetch attachments for message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n // --- Receiving (event-driven) ---\n\n onMention(handler: MentionHandler): void {\n this.mentionHandlers.push(handler);\n }\n\n onReply(handler: ReplyHandler): void {\n this.replyHandlers.push(handler);\n }\n\n onMessage(channelId: string, handler: MessageHandler): void;\n onMessage(channelId: string, opts: { filter: MessageFilter }, handler: MessageHandler): void;\n onMessage(\n channelId: string,\n handlerOrOpts: MessageHandler | { filter: MessageFilter },\n maybeHandler?: MessageHandler,\n ): void {\n let filter: MessageFilter | undefined;\n let handler: MessageHandler;\n\n if (typeof handlerOrOpts === \"function\") {\n handler = handlerOrOpts;\n } else {\n filter = handlerOrOpts.filter;\n if (!maybeHandler) {\n throw new Error(\"Handler is required when providing filter options\");\n }\n handler = maybeHandler;\n }\n\n const subs = this.channelSubscriptions.get(channelId) ?? [];\n subs.push({ filter, handler });\n this.channelSubscriptions.set(channelId, subs);\n }\n\n // --- Receiving (async iterable) ---\n\n mentions(): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n this.onMention((event) => {\n buffer.push(event);\n });\n return buffer;\n }\n\n replies(): AsyncIterable<[MessageEvent, SentMessage]> {\n const buffer = new EventBuffer<[MessageEvent, SentMessage]>();\n this.onReply((event, original) => {\n buffer.push([event, original]);\n });\n return buffer;\n }\n\n messages(channelId: string, options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n if (options?.filter) {\n this.onMessage(channelId, { filter: options.filter }, (event) => {\n buffer.push(event);\n });\n } else {\n this.onMessage(channelId, (event) => {\n buffer.push(event);\n });\n }\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): void {\n this.errorHandlers.push(handler);\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._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 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 // 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 const botUser = this.client.user;\n\n // Check mention handlers\n if (botUser && message.mentions.has(botUser.id)) {\n for (const handler of this.mentionHandlers) {\n handler(event);\n }\n }\n\n // Check reply handlers\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n for (const handler of this.replyHandlers) {\n handler(event, original);\n }\n }\n }\n\n // Check channel subscriptions\n const subs = this.channelSubscriptions.get(message.channelId);\n if (subs) {\n for (const sub of subs) {\n if (sub.filter && !sub.filter(event)) continue;\n sub.handler(event);\n }\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n private toMessageEvent(message: Message): MessageEvent {\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 };\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 mentions(), replies(),\n * or messages() on a Bot creates its own buffer. Do not share a single\n * EventBuffer between 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\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 this.closed = true;\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 };\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 } 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] });\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] });\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 mentionHandlers: ((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 bot.onMention((event) => {\n for (const handler of this.mentionHandlers) {\n handler(event, bot);\n }\n });\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n });\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 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 onMention(callback: (event: MessageEvent, bot: Bot) => void): void {\n this.mentionHandlers.push(callback);\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","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 FileAttachment,\n FetchedMessage,\n FetchHistoryOptions,\n OnboardingStep,\n OnboardingStepStatus,\n StepResult,\n ManagerEvents,\n} from \"./types.js\";\n","import { Client, ChannelType, 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 MentionHandler = (message: MessageEvent) => void;\ntype ReplyHandler = (message: MessageEvent, original: SentMessage) => void;\ntype MessageHandler = (message: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\ninterface ChannelSubscription {\n filter?: MessageFilter;\n handler: MessageHandler;\n}\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 mentionHandlers: MentionHandler[] = [];\n private readonly replyHandlers: ReplyHandler[] = [];\n private readonly channelSubscriptions = new Map<string, ChannelSubscription[]>();\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 = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n let message: Message;\n try {\n message = await channel.messages.fetch(messageId);\n } catch (error) {\n throw new Error(\n `Cannot reply to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 } catch (error) {\n throw new Error(\n `Cannot fetch attachments for message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n // --- Receiving (event-driven) ---\n\n onMention(handler: MentionHandler): void {\n this.mentionHandlers.push(handler);\n }\n\n onReply(handler: ReplyHandler): void {\n this.replyHandlers.push(handler);\n }\n\n onMessage(channelId: string, handler: MessageHandler): void;\n onMessage(channelId: string, opts: { filter: MessageFilter }, handler: MessageHandler): void;\n onMessage(\n channelId: string,\n handlerOrOpts: MessageHandler | { filter: MessageFilter },\n maybeHandler?: MessageHandler,\n ): void {\n let filter: MessageFilter | undefined;\n let handler: MessageHandler;\n\n if (typeof handlerOrOpts === \"function\") {\n handler = handlerOrOpts;\n } else {\n filter = handlerOrOpts.filter;\n if (!maybeHandler) {\n throw new Error(\"Handler is required when providing filter options\");\n }\n handler = maybeHandler;\n }\n\n const subs = this.channelSubscriptions.get(channelId) ?? [];\n subs.push({ filter, handler });\n this.channelSubscriptions.set(channelId, subs);\n }\n\n // --- Receiving (async iterable) ---\n\n mentions(): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n this.onMention((event) => {\n buffer.push(event);\n });\n return buffer;\n }\n\n replies(): AsyncIterable<[MessageEvent, SentMessage]> {\n const buffer = new EventBuffer<[MessageEvent, SentMessage]>();\n this.onReply((event, original) => {\n buffer.push([event, original]);\n });\n return buffer;\n }\n\n messages(channelId: string, options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n if (options?.filter) {\n this.onMessage(channelId, { filter: options.filter }, (event) => {\n buffer.push(event);\n });\n } else {\n this.onMessage(channelId, (event) => {\n buffer.push(event);\n });\n }\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): void {\n this.errorHandlers.push(handler);\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._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 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 // 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 const botUser = this.client.user;\n\n // Check mention handlers — DMs are implicit mentions.\n // Note: a DM reply to a tracked message fires BOTH onMention and onReply.\n const isDM = message.channel.type === ChannelType.DM;\n if (botUser && (message.mentions.has(botUser.id) || isDM)) {\n for (const handler of this.mentionHandlers) {\n handler(event);\n }\n }\n\n // Check reply handlers\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n for (const handler of this.replyHandlers) {\n handler(event, original);\n }\n }\n }\n\n // Check channel subscriptions\n const subs = this.channelSubscriptions.get(message.channelId);\n if (subs) {\n for (const sub of subs) {\n if (sub.filter && !sub.filter(event)) continue;\n sub.handler(event);\n }\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n private toMessageEvent(message: Message): MessageEvent {\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 };\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 mentions(), replies(),\n * or messages() on a Bot creates its own buffer. Do not share a single\n * EventBuffer between 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\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 this.closed = true;\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 };\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 mentionHandlers: ((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 bot.onMention((event) => {\n for (const handler of this.mentionHandlers) {\n handler(event, bot);\n }\n });\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n });\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 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 onMention(callback: (event: MessageEvent, bot: Bot) => void): void {\n this.mentionHandlers.push(callback);\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","import_discord"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { GatewayIntentBits, Message } from 'discord.js';
1
+ import { GatewayIntentBits, Partials, Message } from 'discord.js';
2
2
 
3
3
  type BotStatus = "unregistered" | "configuring" | "connecting" | "verifying" | "ready" | "disconnected" | "failed";
4
4
  interface BotStateAdapter {
@@ -12,6 +12,7 @@ interface BotConfig {
12
12
  token: string;
13
13
  channels?: string[];
14
14
  intents?: GatewayIntentBits[];
15
+ partials?: Partials[];
15
16
  stateAdapter?: BotStateAdapter;
16
17
  sentMessageCacheSize?: number;
17
18
  includeBotMessages?: boolean;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GatewayIntentBits, Message } from 'discord.js';
1
+ import { GatewayIntentBits, Partials, Message } from 'discord.js';
2
2
 
3
3
  type BotStatus = "unregistered" | "configuring" | "connecting" | "verifying" | "ready" | "disconnected" | "failed";
4
4
  interface BotStateAdapter {
@@ -12,6 +12,7 @@ interface BotConfig {
12
12
  token: string;
13
13
  channels?: string[];
14
14
  intents?: GatewayIntentBits[];
15
+ partials?: Partials[];
15
16
  stateAdapter?: BotStateAdapter;
16
17
  sentMessageCacheSize?: number;
17
18
  includeBotMessages?: boolean;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/bot.ts
2
- import { Client, GatewayIntentBits, Events, LimitedCollection, AttachmentBuilder } from "discord.js";
2
+ import { Client, ChannelType, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder } from "discord.js";
3
3
  import { stat } from "fs/promises";
4
4
 
5
5
  // src/event-buffer.ts
@@ -109,6 +109,7 @@ var DEFAULT_INTENTS = [
109
109
  GatewayIntentBits.MessageContent,
110
110
  GatewayIntentBits.DirectMessages
111
111
  ];
112
+ var DEFAULT_PARTIALS = [Partials.Channel, Partials.Message];
112
113
  var Bot = class _Bot {
113
114
  id;
114
115
  name;
@@ -154,7 +155,8 @@ var Bot = class _Bot {
154
155
  }
155
156
  const includeBotMessages = config.includeBotMessages ?? false;
156
157
  const client = new Client({
157
- intents: config.intents ?? [...DEFAULT_INTENTS]
158
+ intents: config.intents ?? [...DEFAULT_INTENTS],
159
+ partials: config.partials ?? [...DEFAULT_PARTIALS]
158
160
  });
159
161
  const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages);
160
162
  await bot.connect(config.token);
@@ -410,7 +412,8 @@ var Bot = class _Bot {
410
412
  if (message.author.bot && !this._includeBotMessages) return;
411
413
  const event = this.toMessageEvent(message);
412
414
  const botUser = this.client.user;
413
- if (botUser && message.mentions.has(botUser.id)) {
415
+ const isDM = message.channel.type === ChannelType.DM;
416
+ if (botUser && (message.mentions.has(botUser.id) || isDM)) {
414
417
  for (const handler of this.mentionHandlers) {
415
418
  handler(event);
416
419
  }
@@ -580,7 +583,7 @@ var BotOnboarding = class {
580
583
  "Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.",
581
584
  async (...args) => {
582
585
  const token = args[0];
583
- const client = new Client2({ intents: [...DEFAULT_INTENTS] });
586
+ const client = new Client2({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });
584
587
  try {
585
588
  await client.login(token);
586
589
  await client.destroy();
@@ -604,7 +607,7 @@ var BotOnboarding = class {
604
607
  error: "Token not set \u2014 complete the provide_token step first."
605
608
  };
606
609
  }
607
- const client = new Client2({ intents: [...DEFAULT_INTENTS] });
610
+ const client = new Client2({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });
608
611
  try {
609
612
  await client.login(this._token);
610
613
  if (client.guilds.cache.size === 0) {
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, 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\ntype MentionHandler = (message: MessageEvent) => void;\ntype ReplyHandler = (message: MessageEvent, original: SentMessage) => void;\ntype MessageHandler = (message: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\ninterface ChannelSubscription {\n filter?: MessageFilter;\n handler: MessageHandler;\n}\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 mentionHandlers: MentionHandler[] = [];\n private readonly replyHandlers: ReplyHandler[] = [];\n private readonly channelSubscriptions = new Map<string, ChannelSubscription[]>();\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 });\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 = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n let message: Message;\n try {\n message = await channel.messages.fetch(messageId);\n } catch (error) {\n throw new Error(\n `Cannot reply to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 } catch (error) {\n throw new Error(\n `Cannot fetch attachments for message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n // --- Receiving (event-driven) ---\n\n onMention(handler: MentionHandler): void {\n this.mentionHandlers.push(handler);\n }\n\n onReply(handler: ReplyHandler): void {\n this.replyHandlers.push(handler);\n }\n\n onMessage(channelId: string, handler: MessageHandler): void;\n onMessage(channelId: string, opts: { filter: MessageFilter }, handler: MessageHandler): void;\n onMessage(\n channelId: string,\n handlerOrOpts: MessageHandler | { filter: MessageFilter },\n maybeHandler?: MessageHandler,\n ): void {\n let filter: MessageFilter | undefined;\n let handler: MessageHandler;\n\n if (typeof handlerOrOpts === \"function\") {\n handler = handlerOrOpts;\n } else {\n filter = handlerOrOpts.filter;\n if (!maybeHandler) {\n throw new Error(\"Handler is required when providing filter options\");\n }\n handler = maybeHandler;\n }\n\n const subs = this.channelSubscriptions.get(channelId) ?? [];\n subs.push({ filter, handler });\n this.channelSubscriptions.set(channelId, subs);\n }\n\n // --- Receiving (async iterable) ---\n\n mentions(): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n this.onMention((event) => {\n buffer.push(event);\n });\n return buffer;\n }\n\n replies(): AsyncIterable<[MessageEvent, SentMessage]> {\n const buffer = new EventBuffer<[MessageEvent, SentMessage]>();\n this.onReply((event, original) => {\n buffer.push([event, original]);\n });\n return buffer;\n }\n\n messages(channelId: string, options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n if (options?.filter) {\n this.onMessage(channelId, { filter: options.filter }, (event) => {\n buffer.push(event);\n });\n } else {\n this.onMessage(channelId, (event) => {\n buffer.push(event);\n });\n }\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): void {\n this.errorHandlers.push(handler);\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._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 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 // 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 const botUser = this.client.user;\n\n // Check mention handlers\n if (botUser && message.mentions.has(botUser.id)) {\n for (const handler of this.mentionHandlers) {\n handler(event);\n }\n }\n\n // Check reply handlers\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n for (const handler of this.replyHandlers) {\n handler(event, original);\n }\n }\n }\n\n // Check channel subscriptions\n const subs = this.channelSubscriptions.get(message.channelId);\n if (subs) {\n for (const sub of subs) {\n if (sub.filter && !sub.filter(event)) continue;\n sub.handler(event);\n }\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n private toMessageEvent(message: Message): MessageEvent {\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 };\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 mentions(), replies(),\n * or messages() on a Bot creates its own buffer. Do not share a single\n * EventBuffer between 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\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 this.closed = true;\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 };\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 } 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] });\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] });\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 mentionHandlers: ((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 bot.onMention((event) => {\n for (const handler of this.mentionHandlers) {\n handler(event, bot);\n }\n });\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n });\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 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 onMention(callback: (event: MessageEvent, bot: Bot) => void): void {\n this.mentionHandlers.push(callback);\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, ChannelType, 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 MentionHandler = (message: MessageEvent) => void;\ntype ReplyHandler = (message: MessageEvent, original: SentMessage) => void;\ntype MessageHandler = (message: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\ninterface ChannelSubscription {\n filter?: MessageFilter;\n handler: MessageHandler;\n}\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 mentionHandlers: MentionHandler[] = [];\n private readonly replyHandlers: ReplyHandler[] = [];\n private readonly channelSubscriptions = new Map<string, ChannelSubscription[]>();\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 = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n let message: Message;\n try {\n message = await channel.messages.fetch(messageId);\n } catch (error) {\n throw new Error(\n `Cannot reply to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\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 } catch (error) {\n throw new Error(\n `Cannot fetch attachments for message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n // --- Receiving (event-driven) ---\n\n onMention(handler: MentionHandler): void {\n this.mentionHandlers.push(handler);\n }\n\n onReply(handler: ReplyHandler): void {\n this.replyHandlers.push(handler);\n }\n\n onMessage(channelId: string, handler: MessageHandler): void;\n onMessage(channelId: string, opts: { filter: MessageFilter }, handler: MessageHandler): void;\n onMessage(\n channelId: string,\n handlerOrOpts: MessageHandler | { filter: MessageFilter },\n maybeHandler?: MessageHandler,\n ): void {\n let filter: MessageFilter | undefined;\n let handler: MessageHandler;\n\n if (typeof handlerOrOpts === \"function\") {\n handler = handlerOrOpts;\n } else {\n filter = handlerOrOpts.filter;\n if (!maybeHandler) {\n throw new Error(\"Handler is required when providing filter options\");\n }\n handler = maybeHandler;\n }\n\n const subs = this.channelSubscriptions.get(channelId) ?? [];\n subs.push({ filter, handler });\n this.channelSubscriptions.set(channelId, subs);\n }\n\n // --- Receiving (async iterable) ---\n\n mentions(): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n this.onMention((event) => {\n buffer.push(event);\n });\n return buffer;\n }\n\n replies(): AsyncIterable<[MessageEvent, SentMessage]> {\n const buffer = new EventBuffer<[MessageEvent, SentMessage]>();\n this.onReply((event, original) => {\n buffer.push([event, original]);\n });\n return buffer;\n }\n\n messages(channelId: string, options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const buffer = new EventBuffer<MessageEvent>();\n if (options?.filter) {\n this.onMessage(channelId, { filter: options.filter }, (event) => {\n buffer.push(event);\n });\n } else {\n this.onMessage(channelId, (event) => {\n buffer.push(event);\n });\n }\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): void {\n this.errorHandlers.push(handler);\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._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 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 // 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 const botUser = this.client.user;\n\n // Check mention handlers — DMs are implicit mentions.\n // Note: a DM reply to a tracked message fires BOTH onMention and onReply.\n const isDM = message.channel.type === ChannelType.DM;\n if (botUser && (message.mentions.has(botUser.id) || isDM)) {\n for (const handler of this.mentionHandlers) {\n handler(event);\n }\n }\n\n // Check reply handlers\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n for (const handler of this.replyHandlers) {\n handler(event, original);\n }\n }\n }\n\n // Check channel subscriptions\n const subs = this.channelSubscriptions.get(message.channelId);\n if (subs) {\n for (const sub of subs) {\n if (sub.filter && !sub.filter(event)) continue;\n sub.handler(event);\n }\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n private toMessageEvent(message: Message): MessageEvent {\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 };\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 mentions(), replies(),\n * or messages() on a Bot creates its own buffer. Do not share a single\n * EventBuffer between 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\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 this.closed = true;\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 };\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 mentionHandlers: ((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 bot.onMention((event) => {\n for (const handler of this.mentionHandlers) {\n handler(event, bot);\n }\n });\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n });\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 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 onMention(callback: (event: MessageEvent, bot: Bot) => void): void {\n this.mentionHandlers.push(callback);\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scotthamilton77/discord-bot-lib",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript library for managing Discord bot identities, messaging, and multi-bot coordination",
5
5
  "type": "module",
6
6
  "sideEffects": false,