@scotthamilton77/discord-bot-lib 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/index.cjs +102 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -2
- package/dist/index.d.ts +20 -2
- package/dist/index.js +102 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,40 @@ bot.on("error", (err) => console.error("Bot error:", err.message));
|
|
|
55
55
|
await bot.disconnect();
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Typing Events
|
|
59
|
+
|
|
60
|
+
Listen for when users start typing. **Requires explicit intents** -- `GuildMessageTyping` and/or `DirectMessageTyping` are not included in the default intents, and Discord silently drops the events without them.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { Bot } from "@scotthamilton77/discord-bot-lib";
|
|
64
|
+
import { GatewayIntentBits } from "discord.js";
|
|
65
|
+
|
|
66
|
+
const bot = await Bot.fromConfig({
|
|
67
|
+
id: "typing-bot",
|
|
68
|
+
name: "TypingBot",
|
|
69
|
+
token: process.env.DISCORD_TOKEN!,
|
|
70
|
+
intents: [
|
|
71
|
+
GatewayIntentBits.Guilds,
|
|
72
|
+
GatewayIntentBits.GuildMessages,
|
|
73
|
+
GatewayIntentBits.MessageContent,
|
|
74
|
+
GatewayIntentBits.DirectMessages,
|
|
75
|
+
GatewayIntentBits.GuildMessageTyping, // required for guild typing
|
|
76
|
+
GatewayIntentBits.DirectMessageTyping, // required for DM typing
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Returns an unsubscribe function (same pattern as onMessage)
|
|
81
|
+
const unsubscribe = bot.onTypingStart((event) => {
|
|
82
|
+
console.log(`User ${event.userId} started typing in ${event.channelId}`);
|
|
83
|
+
if (event.guildId === null) {
|
|
84
|
+
console.log("(this is a DM)");
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Throws if bot was not configured with typing intents
|
|
89
|
+
// bot.onTypingStart(handler) → Error: "onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents"
|
|
90
|
+
```
|
|
91
|
+
|
|
58
92
|
## Async Iterables
|
|
59
93
|
|
|
60
94
|
All messages are also available as an `AsyncIterable`, useful for sequential processing with `for await`:
|
|
@@ -183,6 +217,10 @@ await bot.react(channelId, messageId, "\ud83d\udc4d");
|
|
|
183
217
|
|
|
184
218
|
// Edit a message
|
|
185
219
|
await bot.editMessage(channelId, messageId, "Updated content");
|
|
220
|
+
|
|
221
|
+
// Typing indicator (shows "Bot is typing..." for ~10s)
|
|
222
|
+
await bot.sendTyping(channelId);
|
|
223
|
+
await bot.sendDMTyping(userId);
|
|
186
224
|
```
|
|
187
225
|
|
|
188
226
|
## Fetching History
|
|
@@ -270,6 +308,7 @@ const { buffer, filename, contentType } = await downloadAttachment(attachment);
|
|
|
270
308
|
| `BotStatus` | `"unregistered" \| "configuring" \| "connecting" \| "verifying" \| "ready" \| "disconnected" \| "failed"` |
|
|
271
309
|
| `MessageEvent` | Incoming message with routing metadata (`isDM`, `isMention`, `isReply`, `replyTo`), author, content, channelId, mentions, and `raw` discord.js Message. |
|
|
272
310
|
| `SentMessage` | Tracked outgoing message with messageId, channelId, timestamp, and `raw` escape hatch. |
|
|
311
|
+
| `TypingEvent` | Typing indicator with `channelId`, `userId`, `startedAt`, `guildId` (null for DMs), and `raw` discord.js Typing. |
|
|
273
312
|
| `MessageContent` | `string \| { content?: string; embeds?: unknown[]; files?: FileAttachment[] }` |
|
|
274
313
|
| `FetchedMessage` | Lightweight message from `fetchMessages()` / `fetchHistory()`. |
|
|
275
314
|
| `FetchHistoryOptions` | Discriminated union: `{ after: string } \| { before: string }` with optional `limit`. |
|
package/dist/index.cjs
CHANGED
|
@@ -164,14 +164,18 @@ var Bot = class _Bot {
|
|
|
164
164
|
sentMessages;
|
|
165
165
|
_connectedAt = null;
|
|
166
166
|
messageHandlers = [];
|
|
167
|
+
typingHandlers = [];
|
|
167
168
|
errorHandlers = [];
|
|
168
169
|
_includeBotMessages;
|
|
169
|
-
|
|
170
|
+
resolvedIntents;
|
|
171
|
+
typingListenerAttached = false;
|
|
172
|
+
constructor(id, name, client, sentMessageCacheSize, includeBotMessages, resolvedIntents) {
|
|
170
173
|
this.id = id;
|
|
171
174
|
this.name = name;
|
|
172
175
|
this.client = client;
|
|
173
176
|
this.sentMessages = new import_discord.LimitedCollection({ maxSize: sentMessageCacheSize });
|
|
174
177
|
this._includeBotMessages = includeBotMessages;
|
|
178
|
+
this.resolvedIntents = resolvedIntents;
|
|
175
179
|
this._status = "connecting";
|
|
176
180
|
}
|
|
177
181
|
get includeBotMessages() {
|
|
@@ -198,11 +202,12 @@ var Bot = class _Bot {
|
|
|
198
202
|
);
|
|
199
203
|
}
|
|
200
204
|
const includeBotMessages = config.includeBotMessages ?? false;
|
|
205
|
+
const intents = config.intents ?? [...DEFAULT_INTENTS];
|
|
201
206
|
const client = new import_discord.Client({
|
|
202
|
-
intents
|
|
207
|
+
intents,
|
|
203
208
|
partials: config.partials ?? [...DEFAULT_PARTIALS]
|
|
204
209
|
});
|
|
205
|
-
const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages);
|
|
210
|
+
const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);
|
|
206
211
|
await bot.connect(config.token);
|
|
207
212
|
bot.verify();
|
|
208
213
|
bot.setupEventHandlers();
|
|
@@ -261,6 +266,31 @@ var Bot = class _Bot {
|
|
|
261
266
|
);
|
|
262
267
|
}
|
|
263
268
|
}
|
|
269
|
+
async sendTyping(channelId) {
|
|
270
|
+
const channel = await this.fetchTextChannel(channelId);
|
|
271
|
+
const typingChannel = channel;
|
|
272
|
+
if (typeof typingChannel.sendTyping !== "function") {
|
|
273
|
+
throw new Error(`Channel ${channelId} does not support typing indicators`);
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
await typingChannel.sendTyping();
|
|
277
|
+
} catch (error) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async sendDMTyping(userId) {
|
|
284
|
+
try {
|
|
285
|
+
const user = await this.client.users.fetch(userId);
|
|
286
|
+
const dm = await user.createDM();
|
|
287
|
+
await dm.sendTyping();
|
|
288
|
+
} catch (error) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
264
294
|
async fetchMessages(channelId, limit) {
|
|
265
295
|
const channel = await this.fetchTextChannel(channelId);
|
|
266
296
|
const clamped = Math.max(1, Math.min(limit ?? 20, 100));
|
|
@@ -324,6 +354,36 @@ var Bot = class _Bot {
|
|
|
324
354
|
if (index !== -1) this.messageHandlers.splice(index, 1);
|
|
325
355
|
};
|
|
326
356
|
}
|
|
357
|
+
onTypingStart(handler) {
|
|
358
|
+
const hasGuildTyping = this.resolvedIntents.includes(import_discord.GatewayIntentBits.GuildMessageTyping);
|
|
359
|
+
const hasDMTyping = this.resolvedIntents.includes(import_discord.GatewayIntentBits.DirectMessageTyping);
|
|
360
|
+
if (!hasGuildTyping && !hasDMTyping) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
"onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. Add them to the intents array in BotConfig."
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
if (!this.typingListenerAttached) {
|
|
366
|
+
this.typingListenerAttached = true;
|
|
367
|
+
this.client.on(import_discord.Events.TypingStart, (typing) => {
|
|
368
|
+
try {
|
|
369
|
+
const event = this.toTypingEvent(typing);
|
|
370
|
+
for (const h of this.typingHandlers) {
|
|
371
|
+
h(event);
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
this.emitError(error);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
this.typingHandlers.push(handler);
|
|
379
|
+
let removed = false;
|
|
380
|
+
return () => {
|
|
381
|
+
if (removed) return;
|
|
382
|
+
removed = true;
|
|
383
|
+
const index = this.typingHandlers.indexOf(handler);
|
|
384
|
+
if (index !== -1) this.typingHandlers.splice(index, 1);
|
|
385
|
+
};
|
|
386
|
+
}
|
|
327
387
|
// --- Receiving (async iterable) ---
|
|
328
388
|
messages(options) {
|
|
329
389
|
const cleanup = {};
|
|
@@ -358,6 +418,8 @@ var Bot = class _Bot {
|
|
|
358
418
|
this.client.removeAllListeners();
|
|
359
419
|
await this.client.destroy();
|
|
360
420
|
this.messageHandlers.length = 0;
|
|
421
|
+
this.typingHandlers.length = 0;
|
|
422
|
+
this.typingListenerAttached = false;
|
|
361
423
|
this.errorHandlers.length = 0;
|
|
362
424
|
this._status = "disconnected";
|
|
363
425
|
this._connectedAt = null;
|
|
@@ -496,6 +558,15 @@ var Bot = class _Bot {
|
|
|
496
558
|
replyTo
|
|
497
559
|
};
|
|
498
560
|
}
|
|
561
|
+
toTypingEvent(typing) {
|
|
562
|
+
return {
|
|
563
|
+
channelId: typing.channel.id,
|
|
564
|
+
userId: typing.user.id,
|
|
565
|
+
startedAt: typing.startedAt,
|
|
566
|
+
guildId: typing.guild?.id ?? null,
|
|
567
|
+
raw: typing
|
|
568
|
+
};
|
|
569
|
+
}
|
|
499
570
|
async sendPayloads(content, dispatcher) {
|
|
500
571
|
const files = typeof content !== "string" && content.files?.length ? await validateAndBuildAttachments(content.files) : void 0;
|
|
501
572
|
const payloads = toChunkedPayloads(content, files);
|
|
@@ -702,6 +773,7 @@ var ConnectorManager = class {
|
|
|
702
773
|
botMap = /* @__PURE__ */ new Map();
|
|
703
774
|
botUnsubscribers = /* @__PURE__ */ new Map();
|
|
704
775
|
messageHandlers = [];
|
|
776
|
+
typingHandlers = [];
|
|
705
777
|
lifecycleHandlers = /* @__PURE__ */ new Map();
|
|
706
778
|
addBot(bot) {
|
|
707
779
|
if (this.botMap.has(bot.id)) {
|
|
@@ -721,6 +793,9 @@ var ConnectorManager = class {
|
|
|
721
793
|
this.emitLifecycle("botError", bot, error);
|
|
722
794
|
})
|
|
723
795
|
);
|
|
796
|
+
if (this.typingHandlers.length > 0) {
|
|
797
|
+
this.subscribeTyping(bot, unsubs);
|
|
798
|
+
}
|
|
724
799
|
this.botUnsubscribers.set(bot.id, unsubs);
|
|
725
800
|
}
|
|
726
801
|
getBot(id) {
|
|
@@ -747,11 +822,35 @@ var ConnectorManager = class {
|
|
|
747
822
|
onMessage(handler) {
|
|
748
823
|
this.messageHandlers.push(handler);
|
|
749
824
|
}
|
|
825
|
+
onTypingStart(handler) {
|
|
826
|
+
const isFirst = this.typingHandlers.length === 0;
|
|
827
|
+
this.typingHandlers.push(handler);
|
|
828
|
+
if (isFirst) {
|
|
829
|
+
for (const [id, bot] of this.botMap) {
|
|
830
|
+
const unsubs = this.botUnsubscribers.get(id) ?? [];
|
|
831
|
+
this.subscribeTyping(bot, unsubs);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
750
835
|
on(event, callback) {
|
|
751
836
|
const handlers = this.lifecycleHandlers.get(event) ?? [];
|
|
752
837
|
handlers.push(callback);
|
|
753
838
|
this.lifecycleHandlers.set(event, handlers);
|
|
754
839
|
}
|
|
840
|
+
subscribeTyping(bot, unsubs) {
|
|
841
|
+
try {
|
|
842
|
+
unsubs.push(
|
|
843
|
+
bot.onTypingStart((event) => {
|
|
844
|
+
for (const handler of this.typingHandlers) {
|
|
845
|
+
handler(event, bot);
|
|
846
|
+
}
|
|
847
|
+
})
|
|
848
|
+
);
|
|
849
|
+
} catch (error) {
|
|
850
|
+
const isIntentError = error instanceof Error && error.message.startsWith("onTypingStart requires");
|
|
851
|
+
if (!isIntentError) throw error;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
755
854
|
emitLifecycle(event, ...args) {
|
|
756
855
|
const handlers = this.lifecycleHandlers.get(event);
|
|
757
856
|
if (handlers) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["// discord-bot-lib — public API\n\nexport { Bot, DEFAULT_SENT_MESSAGE_CACHE_SIZE } from \"./bot.js\";\nexport { BotOnboarding } from \"./onboarding.js\";\nexport { ConnectorManager } from \"./connector-manager.js\";\nexport { EventBuffer } from \"./event-buffer.js\";\nexport {\n chunkMessage,\n DISCORD_MAX_MESSAGE_LENGTH,\n} from \"./message-utils.js\";\nexport {\n MAX_ATTACHMENT_BYTES,\n validateAttachmentSize,\n sanitizeAttachmentName,\n downloadAttachment,\n} from \"./attachment-utils.js\";\n\nexport type {\n AttachmentLike,\n DownloadableAttachment,\n DownloadedAttachment,\n} from \"./attachment-utils.js\";\n\nexport type {\n Author,\n BotConfig,\n BotStatus,\n BotStateAdapter,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n FileAttachment,\n FetchedMessage,\n FetchHistoryOptions,\n OnboardingStep,\n OnboardingStepStatus,\n StepResult,\n ManagerEvents,\n} from \"./types.js\";\n","import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n\n const client = new Client({\n intents: config.intents ?? [...DEFAULT_INTENTS],\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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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 TypingEvent,\n FileAttachment,\n FetchedMessage,\n FetchHistoryOptions,\n OnboardingStep,\n OnboardingStepStatus,\n StepResult,\n ManagerEvents,\n} from \"./types.js\";\n","import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message, type Typing } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n TypingEvent,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype TypingHandler = (event: TypingEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly typingHandlers: TypingHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n private readonly resolvedIntents: readonly GatewayIntentBits[];\n private typingListenerAttached = false;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n resolvedIntents: readonly GatewayIntentBits[],\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this.resolvedIntents = resolvedIntents;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n const intents = config.intents ?? [...DEFAULT_INTENTS];\n\n const client = new Client({\n intents,\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendTyping(channelId: string): Promise<void> {\n const channel = await this.fetchTextChannel(channelId);\n const typingChannel = channel as unknown as { sendTyping?: () => Promise<void> };\n if (typeof typingChannel.sendTyping !== \"function\") {\n throw new Error(`Channel ${channelId} does not support typing indicators`);\n }\n try {\n await typingChannel.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendDMTyping(userId: string): Promise<void> {\n try {\n const user = await this.client.users.fetch(userId);\n const dm = await user.createDM();\n await dm.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n onTypingStart(handler: TypingHandler): () => void {\n const hasGuildTyping = this.resolvedIntents.includes(GatewayIntentBits.GuildMessageTyping);\n const hasDMTyping = this.resolvedIntents.includes(GatewayIntentBits.DirectMessageTyping);\n if (!hasGuildTyping && !hasDMTyping) {\n throw new Error(\n \"onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. \" +\n \"Add them to the intents array in BotConfig.\",\n );\n }\n\n if (!this.typingListenerAttached) {\n this.typingListenerAttached = true;\n this.client.on(Events.TypingStart, (typing: Typing) => {\n try {\n const event = this.toTypingEvent(typing);\n for (const h of this.typingHandlers) {\n h(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n this.typingHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.typingHandlers.indexOf(handler);\n if (index !== -1) this.typingHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.typingHandlers.length = 0;\n this.typingListenerAttached = false;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private toTypingEvent(typing: Typing): TypingEvent {\n return {\n channelId: typing.channel.id,\n userId: typing.user.id,\n startedAt: typing.startedAt,\n guildId: typing.guild?.id ?? null,\n raw: typing,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent, TypingEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private typingHandlers: ((event: TypingEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n if (this.typingHandlers.length > 0) {\n this.subscribeTyping(bot, unsubs);\n }\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void {\n const isFirst = this.typingHandlers.length === 0;\n this.typingHandlers.push(handler);\n if (isFirst) {\n for (const [id, bot] of this.botMap) {\n const unsubs = this.botUnsubscribers.get(id) ?? [];\n this.subscribeTyping(bot, unsubs);\n }\n }\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private subscribeTyping(bot: Bot, unsubs: (() => void)[]): void {\n try {\n unsubs.push(\n bot.onTypingStart((event) => {\n for (const handler of this.typingHandlers) {\n handler(event, bot);\n }\n }),\n );\n } catch (error) {\n // Re-throw unexpected errors; only suppress the known intent-check error\n const isIntentError = error instanceof Error && error.message.startsWith(\"onTypingStart requires\");\n if (!isIntentError) throw error;\n }\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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","names":["chunks","text","import_discord"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GatewayIntentBits, Partials, Message } from 'discord.js';
|
|
1
|
+
import { GatewayIntentBits, Partials, Message, Typing } from 'discord.js';
|
|
2
2
|
|
|
3
3
|
type BotStatus = "unregistered" | "configuring" | "connecting" | "verifying" | "ready" | "disconnected" | "failed";
|
|
4
4
|
interface BotStateAdapter {
|
|
@@ -76,6 +76,13 @@ type FetchHistoryOptions = {
|
|
|
76
76
|
limit?: number;
|
|
77
77
|
};
|
|
78
78
|
type MessageFilter = (message: MessageEvent) => boolean;
|
|
79
|
+
interface TypingEvent {
|
|
80
|
+
channelId: string;
|
|
81
|
+
userId: string;
|
|
82
|
+
startedAt: Date;
|
|
83
|
+
guildId: string | null;
|
|
84
|
+
raw: Typing;
|
|
85
|
+
}
|
|
79
86
|
type OnboardingStepStatus = "pending" | "ready" | "completed" | "failed";
|
|
80
87
|
interface StepResult {
|
|
81
88
|
success: boolean;
|
|
@@ -143,6 +150,7 @@ declare function downloadAttachment(attachment: DownloadableAttachment): Promise
|
|
|
143
150
|
|
|
144
151
|
declare const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;
|
|
145
152
|
type MessageHandler = (event: MessageEvent) => void;
|
|
153
|
+
type TypingHandler = (event: TypingEvent) => void;
|
|
146
154
|
type ErrorHandler = (error: Error) => void;
|
|
147
155
|
declare class Bot {
|
|
148
156
|
readonly id: string;
|
|
@@ -152,8 +160,11 @@ declare class Bot {
|
|
|
152
160
|
private readonly sentMessages;
|
|
153
161
|
private _connectedAt;
|
|
154
162
|
private readonly messageHandlers;
|
|
163
|
+
private readonly typingHandlers;
|
|
155
164
|
private readonly errorHandlers;
|
|
156
165
|
private readonly _includeBotMessages;
|
|
166
|
+
private readonly resolvedIntents;
|
|
167
|
+
private typingListenerAttached;
|
|
157
168
|
private constructor();
|
|
158
169
|
get includeBotMessages(): boolean;
|
|
159
170
|
get status(): BotStatus;
|
|
@@ -169,10 +180,13 @@ declare class Bot {
|
|
|
169
180
|
reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]>;
|
|
170
181
|
react(channelId: string, messageId: string, emoji: string): Promise<void>;
|
|
171
182
|
editMessage(channelId: string, messageId: string, content: string): Promise<void>;
|
|
183
|
+
sendTyping(channelId: string): Promise<void>;
|
|
184
|
+
sendDMTyping(userId: string): Promise<void>;
|
|
172
185
|
fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]>;
|
|
173
186
|
fetchHistory(channelId: string, options: FetchHistoryOptions): AsyncGenerator<FetchedMessage[]>;
|
|
174
187
|
getMessageAttachments(channelId: string, messageId: string): Promise<DownloadableAttachment[]>;
|
|
175
188
|
onMessage(handler: MessageHandler): () => void;
|
|
189
|
+
onTypingStart(handler: TypingHandler): () => void;
|
|
176
190
|
messages(options?: {
|
|
177
191
|
filter?: MessageFilter;
|
|
178
192
|
}): AsyncIterable<MessageEvent>;
|
|
@@ -188,6 +202,7 @@ declare class Bot {
|
|
|
188
202
|
private setupEventHandlers;
|
|
189
203
|
private handleUncachedDM;
|
|
190
204
|
private toMessageEvent;
|
|
205
|
+
private toTypingEvent;
|
|
191
206
|
private sendPayloads;
|
|
192
207
|
private trackSentMessage;
|
|
193
208
|
}
|
|
@@ -207,6 +222,7 @@ declare class ConnectorManager {
|
|
|
207
222
|
private botMap;
|
|
208
223
|
private botUnsubscribers;
|
|
209
224
|
private messageHandlers;
|
|
225
|
+
private typingHandlers;
|
|
210
226
|
private lifecycleHandlers;
|
|
211
227
|
addBot(bot: Bot): void;
|
|
212
228
|
getBot(id: string): Bot | undefined;
|
|
@@ -218,7 +234,9 @@ declare class ConnectorManager {
|
|
|
218
234
|
status: string;
|
|
219
235
|
}[];
|
|
220
236
|
onMessage(handler: (event: MessageEvent, bot: Bot) => void): void;
|
|
237
|
+
onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void;
|
|
221
238
|
on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void;
|
|
239
|
+
private subscribeTyping;
|
|
222
240
|
private emitLifecycle;
|
|
223
241
|
onboardBot(id: string, name: string): BotOnboarding;
|
|
224
242
|
disconnectAll(): Promise<void>;
|
|
@@ -256,4 +274,4 @@ declare const DISCORD_MAX_MESSAGE_LENGTH = 2000;
|
|
|
256
274
|
*/
|
|
257
275
|
declare function chunkMessage(text: string, limit?: number): string[];
|
|
258
276
|
|
|
259
|
-
export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
|
|
277
|
+
export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, type TypingEvent, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GatewayIntentBits, Partials, Message } from 'discord.js';
|
|
1
|
+
import { GatewayIntentBits, Partials, Message, Typing } from 'discord.js';
|
|
2
2
|
|
|
3
3
|
type BotStatus = "unregistered" | "configuring" | "connecting" | "verifying" | "ready" | "disconnected" | "failed";
|
|
4
4
|
interface BotStateAdapter {
|
|
@@ -76,6 +76,13 @@ type FetchHistoryOptions = {
|
|
|
76
76
|
limit?: number;
|
|
77
77
|
};
|
|
78
78
|
type MessageFilter = (message: MessageEvent) => boolean;
|
|
79
|
+
interface TypingEvent {
|
|
80
|
+
channelId: string;
|
|
81
|
+
userId: string;
|
|
82
|
+
startedAt: Date;
|
|
83
|
+
guildId: string | null;
|
|
84
|
+
raw: Typing;
|
|
85
|
+
}
|
|
79
86
|
type OnboardingStepStatus = "pending" | "ready" | "completed" | "failed";
|
|
80
87
|
interface StepResult {
|
|
81
88
|
success: boolean;
|
|
@@ -143,6 +150,7 @@ declare function downloadAttachment(attachment: DownloadableAttachment): Promise
|
|
|
143
150
|
|
|
144
151
|
declare const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;
|
|
145
152
|
type MessageHandler = (event: MessageEvent) => void;
|
|
153
|
+
type TypingHandler = (event: TypingEvent) => void;
|
|
146
154
|
type ErrorHandler = (error: Error) => void;
|
|
147
155
|
declare class Bot {
|
|
148
156
|
readonly id: string;
|
|
@@ -152,8 +160,11 @@ declare class Bot {
|
|
|
152
160
|
private readonly sentMessages;
|
|
153
161
|
private _connectedAt;
|
|
154
162
|
private readonly messageHandlers;
|
|
163
|
+
private readonly typingHandlers;
|
|
155
164
|
private readonly errorHandlers;
|
|
156
165
|
private readonly _includeBotMessages;
|
|
166
|
+
private readonly resolvedIntents;
|
|
167
|
+
private typingListenerAttached;
|
|
157
168
|
private constructor();
|
|
158
169
|
get includeBotMessages(): boolean;
|
|
159
170
|
get status(): BotStatus;
|
|
@@ -169,10 +180,13 @@ declare class Bot {
|
|
|
169
180
|
reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]>;
|
|
170
181
|
react(channelId: string, messageId: string, emoji: string): Promise<void>;
|
|
171
182
|
editMessage(channelId: string, messageId: string, content: string): Promise<void>;
|
|
183
|
+
sendTyping(channelId: string): Promise<void>;
|
|
184
|
+
sendDMTyping(userId: string): Promise<void>;
|
|
172
185
|
fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]>;
|
|
173
186
|
fetchHistory(channelId: string, options: FetchHistoryOptions): AsyncGenerator<FetchedMessage[]>;
|
|
174
187
|
getMessageAttachments(channelId: string, messageId: string): Promise<DownloadableAttachment[]>;
|
|
175
188
|
onMessage(handler: MessageHandler): () => void;
|
|
189
|
+
onTypingStart(handler: TypingHandler): () => void;
|
|
176
190
|
messages(options?: {
|
|
177
191
|
filter?: MessageFilter;
|
|
178
192
|
}): AsyncIterable<MessageEvent>;
|
|
@@ -188,6 +202,7 @@ declare class Bot {
|
|
|
188
202
|
private setupEventHandlers;
|
|
189
203
|
private handleUncachedDM;
|
|
190
204
|
private toMessageEvent;
|
|
205
|
+
private toTypingEvent;
|
|
191
206
|
private sendPayloads;
|
|
192
207
|
private trackSentMessage;
|
|
193
208
|
}
|
|
@@ -207,6 +222,7 @@ declare class ConnectorManager {
|
|
|
207
222
|
private botMap;
|
|
208
223
|
private botUnsubscribers;
|
|
209
224
|
private messageHandlers;
|
|
225
|
+
private typingHandlers;
|
|
210
226
|
private lifecycleHandlers;
|
|
211
227
|
addBot(bot: Bot): void;
|
|
212
228
|
getBot(id: string): Bot | undefined;
|
|
@@ -218,7 +234,9 @@ declare class ConnectorManager {
|
|
|
218
234
|
status: string;
|
|
219
235
|
}[];
|
|
220
236
|
onMessage(handler: (event: MessageEvent, bot: Bot) => void): void;
|
|
237
|
+
onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void;
|
|
221
238
|
on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void;
|
|
239
|
+
private subscribeTyping;
|
|
222
240
|
private emitLifecycle;
|
|
223
241
|
onboardBot(id: string, name: string): BotOnboarding;
|
|
224
242
|
disconnectAll(): Promise<void>;
|
|
@@ -256,4 +274,4 @@ declare const DISCORD_MAX_MESSAGE_LENGTH = 2000;
|
|
|
256
274
|
*/
|
|
257
275
|
declare function chunkMessage(text: string, limit?: number): string[];
|
|
258
276
|
|
|
259
|
-
export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
|
|
277
|
+
export { type AttachmentLike, type Author, Bot, type BotConfig, BotOnboarding, type BotStateAdapter, type BotStatus, ConnectorManager, DEFAULT_SENT_MESSAGE_CACHE_SIZE, DISCORD_MAX_MESSAGE_LENGTH, type DownloadableAttachment, type DownloadedAttachment, EventBuffer, type FetchHistoryOptions, type FetchedMessage, type FileAttachment, MAX_ATTACHMENT_BYTES, type ManagerEvents, type MessageContent, type MessageEvent, type MessageFilter, type OnboardingStep, type OnboardingStepStatus, type SentMessage, type StepResult, type TypingEvent, chunkMessage, downloadAttachment, sanitizeAttachmentName, validateAttachmentSize };
|
package/dist/index.js
CHANGED
|
@@ -128,14 +128,18 @@ var Bot = class _Bot {
|
|
|
128
128
|
sentMessages;
|
|
129
129
|
_connectedAt = null;
|
|
130
130
|
messageHandlers = [];
|
|
131
|
+
typingHandlers = [];
|
|
131
132
|
errorHandlers = [];
|
|
132
133
|
_includeBotMessages;
|
|
133
|
-
|
|
134
|
+
resolvedIntents;
|
|
135
|
+
typingListenerAttached = false;
|
|
136
|
+
constructor(id, name, client, sentMessageCacheSize, includeBotMessages, resolvedIntents) {
|
|
134
137
|
this.id = id;
|
|
135
138
|
this.name = name;
|
|
136
139
|
this.client = client;
|
|
137
140
|
this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });
|
|
138
141
|
this._includeBotMessages = includeBotMessages;
|
|
142
|
+
this.resolvedIntents = resolvedIntents;
|
|
139
143
|
this._status = "connecting";
|
|
140
144
|
}
|
|
141
145
|
get includeBotMessages() {
|
|
@@ -162,11 +166,12 @@ var Bot = class _Bot {
|
|
|
162
166
|
);
|
|
163
167
|
}
|
|
164
168
|
const includeBotMessages = config.includeBotMessages ?? false;
|
|
169
|
+
const intents = config.intents ?? [...DEFAULT_INTENTS];
|
|
165
170
|
const client = new Client({
|
|
166
|
-
intents
|
|
171
|
+
intents,
|
|
167
172
|
partials: config.partials ?? [...DEFAULT_PARTIALS]
|
|
168
173
|
});
|
|
169
|
-
const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages);
|
|
174
|
+
const bot = new _Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);
|
|
170
175
|
await bot.connect(config.token);
|
|
171
176
|
bot.verify();
|
|
172
177
|
bot.setupEventHandlers();
|
|
@@ -225,6 +230,31 @@ var Bot = class _Bot {
|
|
|
225
230
|
);
|
|
226
231
|
}
|
|
227
232
|
}
|
|
233
|
+
async sendTyping(channelId) {
|
|
234
|
+
const channel = await this.fetchTextChannel(channelId);
|
|
235
|
+
const typingChannel = channel;
|
|
236
|
+
if (typeof typingChannel.sendTyping !== "function") {
|
|
237
|
+
throw new Error(`Channel ${channelId} does not support typing indicators`);
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
await typingChannel.sendTyping();
|
|
241
|
+
} catch (error) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async sendDMTyping(userId) {
|
|
248
|
+
try {
|
|
249
|
+
const user = await this.client.users.fetch(userId);
|
|
250
|
+
const dm = await user.createDM();
|
|
251
|
+
await dm.sendTyping();
|
|
252
|
+
} catch (error) {
|
|
253
|
+
throw new Error(
|
|
254
|
+
`Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
228
258
|
async fetchMessages(channelId, limit) {
|
|
229
259
|
const channel = await this.fetchTextChannel(channelId);
|
|
230
260
|
const clamped = Math.max(1, Math.min(limit ?? 20, 100));
|
|
@@ -288,6 +318,36 @@ var Bot = class _Bot {
|
|
|
288
318
|
if (index !== -1) this.messageHandlers.splice(index, 1);
|
|
289
319
|
};
|
|
290
320
|
}
|
|
321
|
+
onTypingStart(handler) {
|
|
322
|
+
const hasGuildTyping = this.resolvedIntents.includes(GatewayIntentBits.GuildMessageTyping);
|
|
323
|
+
const hasDMTyping = this.resolvedIntents.includes(GatewayIntentBits.DirectMessageTyping);
|
|
324
|
+
if (!hasGuildTyping && !hasDMTyping) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
"onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. Add them to the intents array in BotConfig."
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (!this.typingListenerAttached) {
|
|
330
|
+
this.typingListenerAttached = true;
|
|
331
|
+
this.client.on(Events.TypingStart, (typing) => {
|
|
332
|
+
try {
|
|
333
|
+
const event = this.toTypingEvent(typing);
|
|
334
|
+
for (const h of this.typingHandlers) {
|
|
335
|
+
h(event);
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
this.emitError(error);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
this.typingHandlers.push(handler);
|
|
343
|
+
let removed = false;
|
|
344
|
+
return () => {
|
|
345
|
+
if (removed) return;
|
|
346
|
+
removed = true;
|
|
347
|
+
const index = this.typingHandlers.indexOf(handler);
|
|
348
|
+
if (index !== -1) this.typingHandlers.splice(index, 1);
|
|
349
|
+
};
|
|
350
|
+
}
|
|
291
351
|
// --- Receiving (async iterable) ---
|
|
292
352
|
messages(options) {
|
|
293
353
|
const cleanup = {};
|
|
@@ -322,6 +382,8 @@ var Bot = class _Bot {
|
|
|
322
382
|
this.client.removeAllListeners();
|
|
323
383
|
await this.client.destroy();
|
|
324
384
|
this.messageHandlers.length = 0;
|
|
385
|
+
this.typingHandlers.length = 0;
|
|
386
|
+
this.typingListenerAttached = false;
|
|
325
387
|
this.errorHandlers.length = 0;
|
|
326
388
|
this._status = "disconnected";
|
|
327
389
|
this._connectedAt = null;
|
|
@@ -460,6 +522,15 @@ var Bot = class _Bot {
|
|
|
460
522
|
replyTo
|
|
461
523
|
};
|
|
462
524
|
}
|
|
525
|
+
toTypingEvent(typing) {
|
|
526
|
+
return {
|
|
527
|
+
channelId: typing.channel.id,
|
|
528
|
+
userId: typing.user.id,
|
|
529
|
+
startedAt: typing.startedAt,
|
|
530
|
+
guildId: typing.guild?.id ?? null,
|
|
531
|
+
raw: typing
|
|
532
|
+
};
|
|
533
|
+
}
|
|
463
534
|
async sendPayloads(content, dispatcher) {
|
|
464
535
|
const files = typeof content !== "string" && content.files?.length ? await validateAndBuildAttachments(content.files) : void 0;
|
|
465
536
|
const payloads = toChunkedPayloads(content, files);
|
|
@@ -666,6 +737,7 @@ var ConnectorManager = class {
|
|
|
666
737
|
botMap = /* @__PURE__ */ new Map();
|
|
667
738
|
botUnsubscribers = /* @__PURE__ */ new Map();
|
|
668
739
|
messageHandlers = [];
|
|
740
|
+
typingHandlers = [];
|
|
669
741
|
lifecycleHandlers = /* @__PURE__ */ new Map();
|
|
670
742
|
addBot(bot) {
|
|
671
743
|
if (this.botMap.has(bot.id)) {
|
|
@@ -685,6 +757,9 @@ var ConnectorManager = class {
|
|
|
685
757
|
this.emitLifecycle("botError", bot, error);
|
|
686
758
|
})
|
|
687
759
|
);
|
|
760
|
+
if (this.typingHandlers.length > 0) {
|
|
761
|
+
this.subscribeTyping(bot, unsubs);
|
|
762
|
+
}
|
|
688
763
|
this.botUnsubscribers.set(bot.id, unsubs);
|
|
689
764
|
}
|
|
690
765
|
getBot(id) {
|
|
@@ -711,11 +786,35 @@ var ConnectorManager = class {
|
|
|
711
786
|
onMessage(handler) {
|
|
712
787
|
this.messageHandlers.push(handler);
|
|
713
788
|
}
|
|
789
|
+
onTypingStart(handler) {
|
|
790
|
+
const isFirst = this.typingHandlers.length === 0;
|
|
791
|
+
this.typingHandlers.push(handler);
|
|
792
|
+
if (isFirst) {
|
|
793
|
+
for (const [id, bot] of this.botMap) {
|
|
794
|
+
const unsubs = this.botUnsubscribers.get(id) ?? [];
|
|
795
|
+
this.subscribeTyping(bot, unsubs);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
714
799
|
on(event, callback) {
|
|
715
800
|
const handlers = this.lifecycleHandlers.get(event) ?? [];
|
|
716
801
|
handlers.push(callback);
|
|
717
802
|
this.lifecycleHandlers.set(event, handlers);
|
|
718
803
|
}
|
|
804
|
+
subscribeTyping(bot, unsubs) {
|
|
805
|
+
try {
|
|
806
|
+
unsubs.push(
|
|
807
|
+
bot.onTypingStart((event) => {
|
|
808
|
+
for (const handler of this.typingHandlers) {
|
|
809
|
+
handler(event, bot);
|
|
810
|
+
}
|
|
811
|
+
})
|
|
812
|
+
);
|
|
813
|
+
} catch (error) {
|
|
814
|
+
const isIntentError = error instanceof Error && error.message.startsWith("onTypingStart requires");
|
|
815
|
+
if (!isIntentError) throw error;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
719
818
|
emitLifecycle(event, ...args) {
|
|
720
819
|
const handlers = this.lifecycleHandlers.get(event);
|
|
721
820
|
if (handlers) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n\n const client = new Client({\n intents: config.intents ?? [...DEFAULT_INTENTS],\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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","names":["chunks","text","Client","Client"]}
|
|
1
|
+
{"version":3,"sources":["../src/bot.ts","../src/event-buffer.ts","../src/message-utils.ts","../src/attachment-utils.ts","../src/onboarding.ts","../src/connector-manager.ts"],"sourcesContent":["import { Client, GatewayIntentBits, Partials, Events, LimitedCollection, AttachmentBuilder, type Message, type Typing } from \"discord.js\";\nimport { stat } from \"node:fs/promises\";\nimport type {\n BotConfig,\n BotStatus,\n FetchedMessage,\n FetchHistoryOptions,\n FileAttachment,\n MessageEvent,\n SentMessage,\n MessageContent,\n MessageFilter,\n TypingEvent,\n} from \"./types.js\";\nimport { EventBuffer } from \"./event-buffer.js\";\nimport { chunkMessage } from \"./message-utils.js\";\nimport { validateAttachmentSize, type DownloadableAttachment } from \"./attachment-utils.js\";\n\nexport const DEFAULT_SENT_MESSAGE_CACHE_SIZE = 1000;\n\nexport const DEFAULT_INTENTS = [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n] as const;\n\nexport const DEFAULT_PARTIALS = [Partials.Channel, Partials.Message] as const;\n\ntype MessageHandler = (event: MessageEvent) => void;\ntype TypingHandler = (event: TypingEvent) => void;\ntype ErrorHandler = (error: Error) => void;\n\nexport class Bot {\n readonly id: string;\n readonly name: string;\n\n private _status: BotStatus;\n private readonly client: Client;\n private readonly sentMessages: LimitedCollection<string, SentMessage>;\n private _connectedAt: Date | null = null;\n private readonly messageHandlers: MessageHandler[] = [];\n private readonly typingHandlers: TypingHandler[] = [];\n private readonly errorHandlers: ErrorHandler[] = [];\n private readonly _includeBotMessages: boolean;\n private readonly resolvedIntents: readonly GatewayIntentBits[];\n private typingListenerAttached = false;\n\n private constructor(\n id: string,\n name: string,\n client: Client,\n sentMessageCacheSize: number,\n includeBotMessages: boolean,\n resolvedIntents: readonly GatewayIntentBits[],\n ) {\n this.id = id;\n this.name = name;\n this.client = client;\n this.sentMessages = new LimitedCollection({ maxSize: sentMessageCacheSize });\n this._includeBotMessages = includeBotMessages;\n this.resolvedIntents = resolvedIntents;\n this._status = \"connecting\";\n }\n\n get includeBotMessages(): boolean {\n return this._includeBotMessages;\n }\n\n get status(): BotStatus {\n return this._status;\n }\n\n get guildCount(): number {\n return this.client.guilds.cache.size;\n }\n\n get connectedAt(): Date | null {\n return this._connectedAt;\n }\n\n /**\n * Fast path: create a bot from a complete config, connect, verify, and\n * return a ready Bot — or throw with diagnostics.\n */\n static async fromConfig(config: BotConfig): Promise<Bot> {\n const cacheSize = config.sentMessageCacheSize ?? DEFAULT_SENT_MESSAGE_CACHE_SIZE;\n if (cacheSize < 1 || !Number.isInteger(cacheSize)) {\n throw new Error(\n `sentMessageCacheSize must be a positive integer, got ${String(cacheSize)}`,\n );\n }\n\n const includeBotMessages = config.includeBotMessages ?? false;\n const intents = config.intents ?? [...DEFAULT_INTENTS];\n\n const client = new Client({\n intents,\n partials: config.partials ?? [...DEFAULT_PARTIALS],\n });\n\n const bot = new Bot(config.id, config.name, client, cacheSize, includeBotMessages, intents);\n\n await bot.connect(config.token);\n bot.verify();\n bot.setupEventHandlers();\n bot._status = \"ready\";\n bot._connectedAt = new Date();\n\n return bot;\n }\n\n // --- Sending ---\n\n async send(channelId: string, content: MessageContent): Promise<SentMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload) =>\n (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload),\n );\n }\n\n async sendDM(userId: string, content: MessageContent): Promise<SentMessage[]> {\n const user = await this.client.users.fetch(userId);\n return this.sendPayloads(content, (payload) =>\n user.send(payload as Parameters<typeof user.send>[0]),\n );\n }\n\n async reply(channelId: string, messageId: string, content: MessageContent): Promise<SentMessage[]> {\n const { channel, message } = await this.fetchMessage(channelId, messageId, \"reply to\");\n if (!(\"send\" in channel)) {\n throw new Error(`Channel ${channelId} is not a sendable channel`);\n }\n return this.sendPayloads(content, (payload, i) => {\n if (i === 0) {\n return message.reply(payload as Parameters<Message[\"reply\"]>[0]);\n }\n return (channel as unknown as { send(opts: unknown): Promise<Message> }).send(payload);\n });\n }\n\n // --- Channel operations ---\n\n async react(channelId: string, messageId: string, emoji: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"react to\");\n try {\n await message.react(emoji);\n } catch (error) {\n throw new Error(\n `Cannot react to message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async editMessage(channelId: string, messageId: string, content: string): Promise<void> {\n const { message } = await this.fetchMessage(channelId, messageId, \"edit\");\n try {\n await message.edit(content);\n } catch (error) {\n throw new Error(\n `Cannot edit message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendTyping(channelId: string): Promise<void> {\n const channel = await this.fetchTextChannel(channelId);\n const typingChannel = channel as unknown as { sendTyping?: () => Promise<void> };\n if (typeof typingChannel.sendTyping !== \"function\") {\n throw new Error(`Channel ${channelId} does not support typing indicators`);\n }\n try {\n await typingChannel.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to channel ${channelId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async sendDMTyping(userId: string): Promise<void> {\n try {\n const user = await this.client.users.fetch(userId);\n const dm = await user.createDM();\n await dm.sendTyping();\n } catch (error) {\n throw new Error(\n `Cannot send typing to user ${userId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async fetchMessages(channelId: string, limit?: number): Promise<FetchedMessage[]> {\n const channel = await this.fetchTextChannel(channelId);\n const clamped = Math.max(1, Math.min(limit ?? 20, 100));\n const messages = await channel.messages.fetch({ limit: clamped });\n return [...messages.values()].reverse().map((m) => this.toFetchedMessage(m));\n }\n\n async *fetchHistory(\n channelId: string,\n options: FetchHistoryOptions,\n ): AsyncGenerator<FetchedMessage[]> {\n const { after, before, limit = 500 } = options;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS consumers\n if (after && before) {\n throw new Error('Cannot specify both \"after\" and \"before\"');\n }\n\n let cursor: string;\n let direction: \"after\" | \"before\";\n if (after) {\n cursor = after;\n direction = \"after\";\n } else if (before) {\n cursor = before;\n direction = \"before\";\n } else {\n throw new Error('At least one of \"after\" or \"before\" must be specified');\n }\n\n const effectiveLimit = Math.min(limit, 5000);\n const channel = await this.fetchTextChannel(channelId);\n\n let totalFetched = 0;\n\n while (totalFetched < effectiveLimit) {\n const pageSize = Math.min(100, effectiveLimit - totalFetched);\n\n const fetched = await this.fetchPageWithRetry(channel, {\n limit: pageSize,\n [direction]: cursor,\n });\n\n if (fetched.size === 0) break;\n\n const rawMessages = [...fetched.values()];\n const messages = rawMessages.map((m) => this.toFetchedMessage(m));\n\n yield messages;\n totalFetched += messages.length;\n\n const lastMessage = rawMessages.at(-1);\n if (!lastMessage) break;\n cursor = lastMessage.id;\n\n if (fetched.size < pageSize) break;\n }\n }\n\n async getMessageAttachments(\n channelId: string,\n messageId: string,\n ): Promise<DownloadableAttachment[]> {\n const { message } = await this.fetchMessage(channelId, messageId, \"fetch attachments for\");\n return [...message.attachments.values()].map((att) => ({\n id: att.id,\n size: att.size,\n name: att.name,\n url: att.url,\n contentType: att.contentType,\n }));\n }\n\n // --- Receiving (event-driven) ---\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) this.messageHandlers.splice(index, 1);\n };\n }\n\n onTypingStart(handler: TypingHandler): () => void {\n const hasGuildTyping = this.resolvedIntents.includes(GatewayIntentBits.GuildMessageTyping);\n const hasDMTyping = this.resolvedIntents.includes(GatewayIntentBits.DirectMessageTyping);\n if (!hasGuildTyping && !hasDMTyping) {\n throw new Error(\n \"onTypingStart requires GuildMessageTyping and/or DirectMessageTyping intents. \" +\n \"Add them to the intents array in BotConfig.\",\n );\n }\n\n if (!this.typingListenerAttached) {\n this.typingListenerAttached = true;\n this.client.on(Events.TypingStart, (typing: Typing) => {\n try {\n const event = this.toTypingEvent(typing);\n for (const h of this.typingHandlers) {\n h(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n }\n\n this.typingHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.typingHandlers.indexOf(handler);\n if (index !== -1) this.typingHandlers.splice(index, 1);\n };\n }\n\n // --- Receiving (async iterable) ---\n\n messages(options?: { filter?: MessageFilter }): AsyncIterable<MessageEvent> {\n const cleanup: { unsub?: () => void } = {};\n const buffer = new EventBuffer<MessageEvent>({\n onClose: () => cleanup.unsub?.(),\n });\n cleanup.unsub = this.onMessage((event) => {\n if (options?.filter && !options.filter(event)) return;\n buffer.push(event);\n });\n return buffer;\n }\n\n // --- Error handling ---\n\n on(_event: \"error\", handler: ErrorHandler): () => void {\n this.errorHandlers.push(handler);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n const index = this.errorHandlers.indexOf(handler);\n if (index !== -1) this.errorHandlers.splice(index, 1);\n };\n }\n\n private emitError(error: unknown): void {\n const err = error instanceof Error ? error : new Error(String(error));\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n // --- Lifecycle ---\n\n async disconnect(): Promise<void> {\n this.client.removeAllListeners();\n await this.client.destroy();\n this.messageHandlers.length = 0;\n this.typingHandlers.length = 0;\n this.typingListenerAttached = false;\n this.errorHandlers.length = 0;\n this._status = \"disconnected\";\n this._connectedAt = null;\n }\n\n // --- Private ---\n\n private async fetchTextChannel(channelId: string) {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) {\n throw new Error(\n `Channel ${channelId} is not a text-based channel or does not exist`,\n );\n }\n return channel;\n }\n\n private async fetchMessage(\n channelId: string,\n messageId: string,\n verb: string,\n ): Promise<{ channel: Awaited<ReturnType<Bot[\"fetchTextChannel\"]>>; message: Message }> {\n const channel = await this.fetchTextChannel(channelId);\n try {\n const message = await channel.messages.fetch(messageId);\n return { channel, message };\n } catch (error) {\n throw new Error(\n `Cannot ${verb} message ${messageId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private toFetchedMessage(msg: Message): FetchedMessage {\n return {\n messageId: msg.id,\n channelId: msg.channelId,\n author: {\n id: msg.author.id,\n username: msg.author.username,\n bot: msg.author.bot,\n },\n content: msg.content,\n timestamp: msg.createdAt,\n attachmentCount: msg.attachments.size,\n };\n }\n\n private async fetchPageWithRetry(\n channel: { messages: { fetch(opts: unknown): Promise<Map<string, Message>> } },\n options: Record<string, unknown>,\n maxRetryMs = 5000,\n ): Promise<Map<string, Message>> {\n try {\n return await channel.messages.fetch(options);\n } catch (err) {\n if (err instanceof Error && \"retryAfter\" in err) {\n const { retryAfter } = err;\n if (typeof retryAfter === \"number\" && retryAfter <= maxRetryMs) {\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n return channel.messages.fetch(options);\n }\n }\n throw err;\n }\n }\n\n private async connect(token: string): Promise<void> {\n this._status = \"connecting\";\n await this.client.login(token);\n }\n\n private verify(): void {\n this._status = \"verifying\";\n\n if (this.client.guilds.cache.size === 0) {\n this._status = \"failed\";\n throw new Error(\n \"Bot is not in any guild. Invite the bot to at least one server before connecting.\",\n );\n }\n }\n\n private setupEventHandlers(): void {\n this.client.on(Events.MessageCreate, (message: Message) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n // Always ignore own messages (prevent infinite loops)\n if (message.author.id === this.client.user?.id) return;\n // Ignore other bots unless opted in\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n });\n\n // Workaround: discord.js v14 silently drops messageCreate for DM channels\n // not in cache — MessageCreateAction.getChannel() fails to construct a\n // DMChannel when the raw payload lacks a `type` field. Catch these here.\n this.client.on(\"raw\" as string, (packet: { t: string; d: { id?: string; channel_id?: string; guild_id?: string } }) => {\n if (packet.t !== \"MESSAGE_CREATE\") return;\n if (packet.d.guild_id) return;\n const channelId = packet.d.channel_id;\n const messageId = packet.d.id;\n if (!channelId || !messageId) return;\n if (this.client.channels.cache.has(channelId)) return;\n\n void this.handleUncachedDM(channelId, messageId);\n });\n }\n\n private async handleUncachedDM(channelId: string, messageId: string): Promise<void> {\n try {\n const channel = await this.client.channels.fetch(channelId);\n if (!channel?.isTextBased()) return;\n\n const message = await channel.messages.fetch(messageId);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null with Partials.Message\n if (!message.author) return;\n if (message.author.id === this.client.user?.id) return;\n if (message.author.bot && !this._includeBotMessages) return;\n\n const event = this.toMessageEvent(message);\n for (const handler of this.messageHandlers) {\n handler(event);\n }\n } catch (error) {\n this.emitError(error);\n }\n }\n\n private toMessageEvent(message: Message): MessageEvent {\n const isDM = message.guildId === null;\n const botUser = this.client.user;\n const isMention = isDM || (botUser ? message.mentions.has(botUser.id) : false);\n\n let isReply = false;\n let replyTo: SentMessage | null = null;\n if (message.reference?.messageId) {\n const original = this.sentMessages.get(message.reference.messageId);\n if (original) {\n isReply = true;\n replyTo = original;\n }\n }\n\n return {\n messageId: message.id,\n author: {\n id: message.author.id,\n username: message.author.username,\n bot: message.author.bot,\n },\n content: message.content,\n channelId: message.channelId,\n guildId: message.guildId ?? null,\n timestamp: message.createdAt,\n mentions: [...message.mentions.users.keys()],\n raw: message,\n isDM,\n isMention,\n isReply,\n replyTo,\n };\n }\n\n private toTypingEvent(typing: Typing): TypingEvent {\n return {\n channelId: typing.channel.id,\n userId: typing.user.id,\n startedAt: typing.startedAt,\n guildId: typing.guild?.id ?? null,\n raw: typing,\n };\n }\n\n private async sendPayloads(\n content: MessageContent,\n dispatcher: (payload: ChunkedPayload, index: number) => Promise<Message>,\n ): Promise<SentMessage[]> {\n const files =\n typeof content !== \"string\" && content.files?.length\n ? await validateAndBuildAttachments(content.files)\n : undefined;\n const payloads = toChunkedPayloads(content, files);\n const results: SentMessage[] = [];\n let i = 0;\n for (const payload of payloads) {\n const msg = await dispatcher(payload, i++);\n results.push(this.trackSentMessage(msg));\n }\n return results;\n }\n\n private trackSentMessage(message: Message): SentMessage {\n const sent: SentMessage = {\n messageId: message.id,\n channelId: message.channelId,\n timestamp: message.createdAt,\n raw: message,\n };\n this.sentMessages.set(sent.messageId, sent);\n return sent;\n }\n}\n\ninterface ChunkedPayload {\n content?: string;\n embeds?: unknown[];\n files?: AttachmentBuilder[];\n}\n\nfunction toChunkedPayloads(\n content: MessageContent,\n files?: AttachmentBuilder[],\n): ChunkedPayload[] {\n if (typeof content === \"string\") {\n const chunks = chunkMessage(content);\n return chunks.map((text, i) => ({\n content: text,\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n }\n const text = content.content ?? \"\";\n const chunks = chunkMessage(text);\n if (chunks.length === 0) {\n return [{ embeds: content.embeds, ...(files?.length ? { files } : {}) }];\n }\n return chunks.map((chunk, i) => ({\n content: chunk,\n ...(i === 0 && content.embeds ? { embeds: content.embeds } : {}),\n ...(i === 0 && files?.length ? { files } : {}),\n }));\n}\n\nconst MAX_FILES_PER_MESSAGE = 10;\n\nasync function validateAndBuildAttachments(\n files: FileAttachment[],\n): Promise<AttachmentBuilder[]> {\n if (files.length > MAX_FILES_PER_MESSAGE) {\n throw new Error(\n `Too many attachments: ${String(files.length)} exceeds Discord's limit of ${String(MAX_FILES_PER_MESSAGE)} attachments per message`,\n );\n }\n\n const sizes = await Promise.all(\n files.map((file) =>\n typeof file.data === \"string\"\n ? stat(file.data).then((st) => st.size)\n : Promise.resolve(file.data.byteLength),\n ),\n );\n\n return files.map((file, i) => {\n const size = sizes[i];\n if (size === undefined) throw new Error(`Missing size for attachment: ${file.name}`);\n validateAttachmentSize({ size, name: file.name, id: file.name });\n return new AttachmentBuilder(file.data, { name: file.name });\n });\n}\n","/**\n * Single-consumer async iterable buffer. Each call to messages() on a Bot\n * creates its own buffer. Do not share a single EventBuffer between\n * multiple for-await consumers.\n */\nexport class EventBuffer<T> implements AsyncIterable<T> {\n private buffer: T[] = [];\n private resolve: ((value: IteratorResult<T>) => void) | null = null;\n private closed = false;\n private readonly onClose: (() => void) | undefined;\n\n constructor(options?: { onClose?: () => void }) {\n this.onClose = options?.onClose;\n }\n\n push(value: T): void {\n if (this.closed) return;\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value, done: false });\n } else {\n this.buffer.push(value);\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.onClose?.();\n if (this.resolve) {\n const r = this.resolve;\n this.resolve = null;\n r({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.buffer.length > 0) {\n // Length check guarantees shift() returns a value\n const value = this.buffer.shift() as T;\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) {\n return Promise.resolve({\n value: undefined as unknown as T,\n done: true,\n });\n }\n return new Promise((resolve) => {\n this.resolve = resolve;\n });\n },\n return: (): Promise<IteratorResult<T>> => {\n this.close();\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n },\n };\n }\n}\n","// Message utility functions for discord-bot-lib\n\n/** Discord's hard limit on message length. */\nexport const DISCORD_MAX_MESSAGE_LENGTH = 2000;\n\n/**\n * Splits text into chunks that fit within Discord's message length limit.\n * Prefers splitting at paragraph boundaries (\\n\\n), then line breaks (\\n),\n * then spaces, then hard-cuts as a last resort.\n *\n * @param text - The text to split\n * @param limit - Maximum characters per chunk (default: {@link DISCORD_MAX_MESSAGE_LENGTH})\n * @returns Array of text chunks, each within the limit\n */\nexport function chunkMessage(\n text: string,\n limit: number = DISCORD_MAX_MESSAGE_LENGTH,\n): string[] {\n if (!text) return [];\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const halfLimit = limit / 2;\n let rest = text;\n\n while (rest.length > limit) {\n const para = rest.lastIndexOf(\"\\n\\n\", limit);\n const line = rest.lastIndexOf(\"\\n\", limit);\n const space = rest.lastIndexOf(\" \", limit);\n const cut =\n para > halfLimit\n ? para\n : line > halfLimit\n ? line\n : space > 0\n ? space\n : limit;\n\n chunks.push(rest.slice(0, cut));\n rest = rest.slice(cut).replace(/^\\n+/, \"\");\n }\n\n if (rest) chunks.push(rest);\n\n return chunks;\n}\n","// Attachment utility functions for discord-bot-lib\n\n/** Maximum attachment size in bytes (25 MB). Matches Discord's default file size limit. */\nexport const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024; // 26_214_400\n\n/** Shape compatible with discord.js Attachment for attachment utilities. */\nexport interface AttachmentLike {\n size: number;\n name?: string | null;\n id: string;\n}\n\n/**\n * Validates that an attachment does not exceed Discord's size limit.\n * Designed for use in both upload (sending files) and download paths.\n *\n * @throws Error if the attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n */\nexport function validateAttachmentSize(attachment: AttachmentLike): void {\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n const actualMB = (attachment.size / 1024 / 1024).toFixed(1);\n const limitMB = MAX_ATTACHMENT_BYTES / 1024 / 1024;\n const nameInfo = attachment.name ? `: ${attachment.name}` : \"\";\n throw new Error(\n `Attachment too large (${actualMB} MB > ${String(limitMB)} MB limit)${nameInfo}`,\n );\n }\n}\n\n/**\n * Sanitizes an attachment filename by replacing unsafe characters\n * (`[`, `]`, `\\r`, `\\n`, `;`) with underscores. Falls back to the\n * attachment ID when the name is missing or empty after sanitization.\n */\nconst UNSAFE_CHARS = /[[\\]\\r\\n;]/g;\n\nexport function sanitizeAttachmentName(\n attachment: Pick<AttachmentLike, \"name\" | \"id\">,\n): string {\n const raw = attachment.name;\n if (!raw || raw.replace(UNSAFE_CHARS, \"\").length === 0) {\n return attachment.id;\n }\n return raw.replace(UNSAFE_CHARS, \"_\");\n}\n\n/** Result of downloading a Discord attachment. */\nexport interface DownloadedAttachment {\n buffer: Buffer;\n filename: string;\n contentType: string;\n}\n\n/** Shape accepted by {@link downloadAttachment}. Compatible with discord.js `Attachment`. */\nexport interface DownloadableAttachment extends AttachmentLike {\n url: string;\n contentType?: string | null;\n}\n\n/**\n * Downloads a Discord message attachment to a raw buffer.\n * Validates size and sanitizes filename before returning.\n *\n * @throws Error if attachment exceeds {@link MAX_ATTACHMENT_BYTES}\n * @throws Error if the fetch response is not ok\n */\nexport async function downloadAttachment(\n attachment: DownloadableAttachment,\n): Promise<DownloadedAttachment> {\n validateAttachmentSize(attachment);\n\n const res = await fetch(attachment.url);\n if (!res.ok) {\n throw new Error(\n `Failed to download attachment: ${String(res.status)} ${res.statusText}`,\n );\n }\n\n const ab = await res.arrayBuffer();\n const buffer = Buffer.from(ab, 0, ab.byteLength);\n const filename = sanitizeAttachmentName(attachment);\n const contentType = attachment.contentType ?? \"application/octet-stream\";\n\n return { buffer, filename, contentType };\n}\n","import { Client } from \"discord.js\";\nimport type { OnboardingStep, OnboardingStepStatus, StepResult } from \"./types.js\";\nimport { Bot, DEFAULT_INTENTS, DEFAULT_PARTIALS } from \"./bot.js\";\n\nfunction toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ntype StepAction = (...args: unknown[]) => Promise<StepResult>;\n\nclass OnboardingStepImpl implements OnboardingStep {\n id: string;\n label: string;\n instructions: string;\n status: OnboardingStepStatus;\n error?: string;\n nextSibling?: OnboardingStepImpl;\n\n private readonly _action: StepAction;\n private readonly _dependencies: OnboardingStepImpl[];\n\n constructor(\n id: string,\n label: string,\n instructions: string,\n action: StepAction,\n dependencies: OnboardingStepImpl[] = [],\n initialStatus: OnboardingStepStatus = \"pending\",\n ) {\n this.id = id;\n this.label = label;\n this.instructions = instructions;\n this._action = action;\n this._dependencies = dependencies;\n this.status = initialStatus;\n }\n\n async complete(...args: unknown[]): Promise<StepResult> {\n // Check dependencies\n for (const dep of this._dependencies) {\n if (dep.status !== \"completed\") {\n const error = `Cannot complete '${this.id}': dependency '${dep.id}' is not completed.`;\n this.status = \"failed\";\n this.error = error;\n return { success: false, error };\n }\n }\n\n try {\n const result = await this._action(...args);\n if (result.success) {\n this.status = \"completed\";\n this.error = undefined;\n // Promote next step to ready\n if (this.nextSibling?.status === \"pending\") {\n this.nextSibling.status = \"ready\";\n result.nextStep = this.nextSibling;\n }\n } else {\n this.status = \"failed\";\n this.error = result.error;\n }\n return result;\n } catch (err) {\n const message = toErrorMessage(err);\n this.status = \"failed\";\n this.error = message;\n return { success: false, error: message };\n }\n }\n}\n\nexport class BotOnboarding {\n readonly id: string;\n readonly name: string;\n readonly steps: OnboardingStep[];\n\n private _bot: Bot | undefined;\n private _token: string | undefined;\n\n constructor(id: string, name: string) {\n this.id = id;\n this.name = name;\n const provideToken = new OnboardingStepImpl(\n \"provide_token\",\n \"Provide Bot Token\",\n \"Enter your Discord bot token. You can find this in the Discord Developer Portal under your application's Bot settings.\",\n async (...args: unknown[]) => {\n const token = args[0] as string;\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(token);\n await client.destroy();\n this._token = token;\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [],\n \"ready\",\n );\n\n const inviteToServer = new OnboardingStepImpl(\n \"invite_to_server\",\n \"Invite Bot to Server\",\n \"Add the bot to at least one Discord server using the OAuth2 URL from the Developer Portal.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n const client = new Client({ intents: [...DEFAULT_INTENTS], partials: [...DEFAULT_PARTIALS] });\n try {\n await client.login(this._token);\n if (client.guilds.cache.size === 0) {\n await client.destroy();\n return { success: false, error: \"Bot is not in any server yet\" };\n }\n await client.destroy();\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [provideToken],\n );\n\n const verifyPermissions = new OnboardingStepImpl(\n \"verify_permissions\",\n \"Verify Permissions\",\n \"Verify the bot has the required permissions in your server.\",\n async () => {\n if (!this._token) {\n return {\n success: false,\n error: \"Token not set — complete the provide_token step first.\",\n };\n }\n try {\n this._bot = await Bot.fromConfig({\n id: this.id,\n name: this.name,\n token: this._token,\n });\n return { success: true };\n } catch (err) {\n return { success: false, error: toErrorMessage(err) };\n }\n },\n [inviteToServer],\n );\n\n // Wire nextSibling chain for step promotion\n provideToken.nextSibling = inviteToServer;\n inviteToServer.nextSibling = verifyPermissions;\n\n this.steps = [provideToken, inviteToServer, verifyPermissions];\n }\n\n get bot(): Bot | undefined {\n return this._bot;\n }\n}\n","import { Bot } from \"./bot.js\";\nimport { BotOnboarding } from \"./onboarding.js\";\nimport type { MessageEvent, TypingEvent } from \"./types.js\";\n\ntype LifecycleEvent = \"botReady\" | \"botDisconnected\" | \"botError\";\n\nexport class ConnectorManager {\n private botMap = new Map<string, Bot>();\n private botUnsubscribers = new Map<string, (() => void)[]>();\n private messageHandlers: ((event: MessageEvent, bot: Bot) => void)[] = [];\n private typingHandlers: ((event: TypingEvent, bot: Bot) => void)[] = [];\n private lifecycleHandlers = new Map<LifecycleEvent, ((...args: unknown[]) => void)[]>();\n\n addBot(bot: Bot): void {\n if (this.botMap.has(bot.id)) {\n throw new Error(`Bot with id \"${bot.id}\" already exists in this manager.`);\n }\n this.botMap.set(bot.id, bot);\n\n const unsubs: (() => void)[] = [];\n unsubs.push(\n bot.onMessage((event) => {\n for (const handler of this.messageHandlers) {\n handler(event, bot);\n }\n }),\n );\n unsubs.push(\n bot.on(\"error\", (error) => {\n this.emitLifecycle(\"botError\", bot, error);\n }),\n );\n if (this.typingHandlers.length > 0) {\n this.subscribeTyping(bot, unsubs);\n }\n this.botUnsubscribers.set(bot.id, unsubs);\n }\n\n getBot(id: string): Bot | undefined {\n return this.botMap.get(id);\n }\n\n get bots(): Iterable<Bot> {\n return this.botMap.values();\n }\n\n removeBot(id: string): void {\n const unsubs = this.botUnsubscribers.get(id);\n if (unsubs) {\n for (const unsub of unsubs) unsub();\n this.botUnsubscribers.delete(id);\n }\n this.botMap.delete(id);\n }\n\n status(): { id: string; name: string; status: string }[] {\n return [...this.botMap.values()].map((bot) => ({\n id: bot.id,\n name: bot.name,\n status: bot.status,\n }));\n }\n\n onMessage(handler: (event: MessageEvent, bot: Bot) => void): void {\n this.messageHandlers.push(handler);\n }\n\n onTypingStart(handler: (event: TypingEvent, bot: Bot) => void): void {\n const isFirst = this.typingHandlers.length === 0;\n this.typingHandlers.push(handler);\n if (isFirst) {\n for (const [id, bot] of this.botMap) {\n const unsubs = this.botUnsubscribers.get(id) ?? [];\n this.subscribeTyping(bot, unsubs);\n }\n }\n }\n\n on(event: LifecycleEvent, callback: (...args: unknown[]) => void): void {\n const handlers = this.lifecycleHandlers.get(event) ?? [];\n handlers.push(callback);\n this.lifecycleHandlers.set(event, handlers);\n }\n\n private subscribeTyping(bot: Bot, unsubs: (() => void)[]): void {\n try {\n unsubs.push(\n bot.onTypingStart((event) => {\n for (const handler of this.typingHandlers) {\n handler(event, bot);\n }\n }),\n );\n } catch (error) {\n // Re-throw unexpected errors; only suppress the known intent-check error\n const isIntentError = error instanceof Error && error.message.startsWith(\"onTypingStart requires\");\n if (!isIntentError) throw error;\n }\n }\n\n private emitLifecycle(event: LifecycleEvent, ...args: unknown[]): void {\n const handlers = this.lifecycleHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n handler(...args);\n }\n }\n }\n\n onboardBot(id: string, name: string): BotOnboarding {\n return new BotOnboarding(id, name);\n }\n\n async disconnectAll(): Promise<void> {\n await Promise.all([...this.botMap.values()].map((bot) => bot.disconnect()));\n }\n\n async shutdown(): Promise<void> {\n await this.disconnectAll();\n }\n}\n"],"mappings":"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","names":["chunks","text","Client","Client"]}
|
package/package.json
CHANGED