@chirp-dev/chirp-sdk 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1402 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/api/types.ts
23
+ var types_exports = {};
24
+ __export(types_exports, {
25
+ Collection: () => Collection,
26
+ DEFAULT_INTENTS: () => DEFAULT_INTENTS
27
+ });
28
+ var DEFAULT_INTENTS, Collection;
29
+ var init_types = __esm({
30
+ "src/api/types.ts"() {
31
+ "use strict";
32
+ DEFAULT_INTENTS = [
33
+ "messages",
34
+ "messageUpdates",
35
+ "userStatus",
36
+ "typing",
37
+ "servers"
38
+ ];
39
+ Collection = class extends Map {
40
+ constructor(entries) {
41
+ super(entries);
42
+ }
43
+ get(key) {
44
+ return super.get(key);
45
+ }
46
+ set(key, value) {
47
+ return super.set(key, value);
48
+ }
49
+ find(predicate) {
50
+ for (const [key, value] of this) {
51
+ if (predicate(value, key)) {
52
+ return value;
53
+ }
54
+ }
55
+ return void 0;
56
+ }
57
+ filter(predicate) {
58
+ const result = [];
59
+ for (const [key, value] of this) {
60
+ if (predicate(value, key)) {
61
+ result.push(value);
62
+ }
63
+ }
64
+ return result;
65
+ }
66
+ first() {
67
+ return this.values().next().value;
68
+ }
69
+ last() {
70
+ let last;
71
+ for (const value of this.values()) {
72
+ last = value;
73
+ }
74
+ return last;
75
+ }
76
+ };
77
+ }
78
+ });
79
+
80
+ // src/client/ChirpClient.ts
81
+ import { io } from "socket.io-client";
82
+
83
+ // src/api/REST.ts
84
+ var APIError = class extends Error {
85
+ statusCode;
86
+ code;
87
+ constructor(message, statusCode, code) {
88
+ super(message);
89
+ this.name = "APIError";
90
+ this.statusCode = statusCode;
91
+ this.code = code;
92
+ }
93
+ };
94
+ var RESTClient = class {
95
+ baseUrl;
96
+ token;
97
+ constructor(baseUrl, token) {
98
+ this.baseUrl = baseUrl;
99
+ this.token = token;
100
+ }
101
+ /**
102
+ * Make an authenticated API request
103
+ */
104
+ async request(endpoint, options = {}) {
105
+ const url = `${this.baseUrl}${endpoint}`;
106
+ const headers = {
107
+ "Authorization": `Bot ${this.token}`,
108
+ "Content-Type": "application/json",
109
+ ...options.headers
110
+ };
111
+ const response = await fetch(url, {
112
+ ...options,
113
+ headers
114
+ });
115
+ const contentType = response.headers.get("content-type");
116
+ const isJson = contentType?.includes("application/json");
117
+ if (!response.ok) {
118
+ let errorMessage = `HTTP ${response.status}`;
119
+ if (isJson) {
120
+ try {
121
+ const errorData = await response.json();
122
+ errorMessage = errorData.message || errorData.error || errorMessage;
123
+ } catch {
124
+ }
125
+ }
126
+ throw new APIError(errorMessage, response.status);
127
+ }
128
+ if (isJson && response.status !== 204) {
129
+ return response.json();
130
+ }
131
+ return void 0;
132
+ }
133
+ /**
134
+ * GET request
135
+ */
136
+ async get(endpoint) {
137
+ return this.request(endpoint, { method: "GET" });
138
+ }
139
+ /**
140
+ * POST request
141
+ */
142
+ async post(endpoint, body) {
143
+ return this.request(endpoint, {
144
+ method: "POST",
145
+ body: body ? JSON.stringify(body) : void 0
146
+ });
147
+ }
148
+ /**
149
+ * PATCH request
150
+ */
151
+ async patch(endpoint, body) {
152
+ return this.request(endpoint, {
153
+ method: "PATCH",
154
+ body: body ? JSON.stringify(body) : void 0
155
+ });
156
+ }
157
+ /**
158
+ * PUT request
159
+ */
160
+ async put(endpoint, body) {
161
+ return this.request(endpoint, {
162
+ method: "PUT",
163
+ body: body ? JSON.stringify(body) : void 0
164
+ });
165
+ }
166
+ /**
167
+ * DELETE request
168
+ */
169
+ async delete(endpoint) {
170
+ return this.request(endpoint, { method: "DELETE" });
171
+ }
172
+ // ========================================================================
173
+ // Bot API Methods
174
+ // ========================================================================
175
+ /**
176
+ * Get the bot's user information
177
+ */
178
+ async getBotInfo() {
179
+ return this.get("/api/auth/me");
180
+ }
181
+ /**
182
+ * Get the bot's token information
183
+ */
184
+ async getTokenInfo() {
185
+ return this.get("/api/bot-tokens");
186
+ }
187
+ /**
188
+ * List all servers the bot has access to
189
+ */
190
+ async getServers() {
191
+ return this.get("/api/servers");
192
+ }
193
+ /**
194
+ * List the bot's server memberships with details
195
+ */
196
+ async getBotServers() {
197
+ return this.get("/api/bot-tokens/servers");
198
+ }
199
+ /**
200
+ * Get channels in a server
201
+ */
202
+ async getChannels(serverId) {
203
+ return this.get(`/api/servers/${serverId}/channels`);
204
+ }
205
+ /**
206
+ * Get messages in a channel
207
+ */
208
+ async getMessages(channelId, options = {}) {
209
+ const params = new URLSearchParams();
210
+ if (options.page) params.append("page", String(options.page));
211
+ if (options.limit) params.append("limit", String(options.limit));
212
+ const query = params.toString();
213
+ return this.get(
214
+ `/api/channels/${channelId}/messages${query ? `?${query}` : ""}`
215
+ );
216
+ }
217
+ /**
218
+ * Send a message to a channel
219
+ */
220
+ async sendMessage(channelId, content, options = {}) {
221
+ return this.post(`/api/channels/${channelId}/messages`, {
222
+ content,
223
+ replyTo: options.replyTo
224
+ });
225
+ }
226
+ /**
227
+ * Reply to a specific message
228
+ */
229
+ async reply(channelId, messageId, content, author, originalContent) {
230
+ return this.post(`/api/channels/${channelId}/messages`, {
231
+ content,
232
+ replyTo: messageId
233
+ });
234
+ }
235
+ /**
236
+ * Edit a message (only works for bot's own messages)
237
+ */
238
+ async editMessage(messageId, content) {
239
+ return this.patch(`/api/messages/${messageId}`, { content });
240
+ }
241
+ /**
242
+ * Delete a message (only works for bot's own messages)
243
+ */
244
+ async deleteMessage(messageId) {
245
+ return this.delete(`/api/messages/${messageId}`);
246
+ }
247
+ /**
248
+ * Join a server via invite code
249
+ */
250
+ async joinServer(inviteCode) {
251
+ return this.post("/api/bot-tokens/servers/join", { inviteCode });
252
+ }
253
+ /**
254
+ * Leave a server
255
+ */
256
+ async leaveServer(serverId) {
257
+ return this.delete(`/api/bot-tokens/servers/${serverId}/leave`);
258
+ }
259
+ };
260
+
261
+ // src/client/events/EventEmitter.ts
262
+ import EventEmitter from "eventemitter3";
263
+ var SafeEventEmitter = class {
264
+ emitter;
265
+ constructor() {
266
+ this.emitter = new EventEmitter();
267
+ }
268
+ /**
269
+ * Register an event listener
270
+ */
271
+ on(event, listener) {
272
+ this.emitter.on(event, listener);
273
+ return this;
274
+ }
275
+ /**
276
+ * Register a one-time event listener
277
+ */
278
+ once(event, listener) {
279
+ this.emitter.once(event, listener);
280
+ return this;
281
+ }
282
+ /**
283
+ * Remove an event listener
284
+ */
285
+ off(event, listener) {
286
+ this.emitter.off(event, listener);
287
+ return this;
288
+ }
289
+ /**
290
+ * Emit an event
291
+ */
292
+ emit(event, ...args) {
293
+ return this.emitter.emit(event, ...args);
294
+ }
295
+ /**
296
+ * Remove all listeners for an event or all events
297
+ */
298
+ removeAllListeners(event) {
299
+ this.emitter.removeAllListeners(event);
300
+ return this;
301
+ }
302
+ /**
303
+ * Get the count of listeners for an event
304
+ */
305
+ listenerCount(event) {
306
+ return this.emitter.listenerCount(event);
307
+ }
308
+ };
309
+
310
+ // src/commands/CommandContext.ts
311
+ var CommandContext = class {
312
+ command;
313
+ args;
314
+ message;
315
+ channel;
316
+ client;
317
+ api;
318
+ constructor(command, args, message, channel, client, api) {
319
+ this.command = command;
320
+ this.args = args;
321
+ this.message = message;
322
+ this.channel = channel;
323
+ this.client = client;
324
+ this.api = api;
325
+ }
326
+ /**
327
+ * Reply to the command message
328
+ */
329
+ async reply(content) {
330
+ return this.api.sendMessage(this.channel.id, content, {
331
+ replyTo: this.message.id
332
+ });
333
+ }
334
+ /**
335
+ * Send a message to the same channel
336
+ */
337
+ async send(content) {
338
+ return this.api.sendMessage(this.channel.id, content);
339
+ }
340
+ /**
341
+ * Edit the bot's response message
342
+ */
343
+ async edit(messageId, content) {
344
+ return this.api.editMessage(messageId, content);
345
+ }
346
+ /**
347
+ * Delete a message
348
+ */
349
+ async delete(messageId) {
350
+ return this.api.deleteMessage(messageId);
351
+ }
352
+ /**
353
+ * Get the raw command string
354
+ */
355
+ get rawCommand() {
356
+ return `${this.command}${this.args.length ? " " + this.args.join(" ") : ""}`;
357
+ }
358
+ };
359
+
360
+ // src/commands/CommandManager.ts
361
+ var CommandManager = class {
362
+ commands;
363
+ prefix;
364
+ client;
365
+ constructor(prefix = "/") {
366
+ this.commands = /* @__PURE__ */ new Map();
367
+ this.prefix = prefix;
368
+ }
369
+ /**
370
+ * Set the client reference (called by ChirpClient)
371
+ * @internal
372
+ */
373
+ _setClient(client) {
374
+ this.client = client;
375
+ }
376
+ register(commandOrBuilder) {
377
+ const command = "build" in commandOrBuilder ? commandOrBuilder.build() : commandOrBuilder;
378
+ this.commands.set(command.name, command);
379
+ return this;
380
+ }
381
+ /**
382
+ * Register multiple commands
383
+ */
384
+ registerMany(commands) {
385
+ for (const commandOrBuilder of commands) {
386
+ if ("build" in commandOrBuilder) {
387
+ this.register(commandOrBuilder);
388
+ } else {
389
+ this.register(commandOrBuilder);
390
+ }
391
+ }
392
+ return this;
393
+ }
394
+ /**
395
+ * Unregister a command
396
+ */
397
+ unregister(name) {
398
+ return this.commands.delete(name);
399
+ }
400
+ /**
401
+ * Get a command by name
402
+ */
403
+ get(name) {
404
+ return this.commands.get(name);
405
+ }
406
+ /**
407
+ * Check if a command exists
408
+ */
409
+ has(name) {
410
+ return this.commands.has(name);
411
+ }
412
+ /**
413
+ * Get all registered commands
414
+ */
415
+ getAll() {
416
+ return new Map(this.commands);
417
+ }
418
+ /**
419
+ * Clear all commands
420
+ */
421
+ clear() {
422
+ this.commands.clear();
423
+ }
424
+ /**
425
+ * Parse a message to check if it's a command
426
+ * Returns { command, args } if it's a command, null otherwise
427
+ */
428
+ parse(message) {
429
+ const content = message.content.trim();
430
+ if (!content.startsWith(this.prefix)) {
431
+ return null;
432
+ }
433
+ const parts = content.slice(this.prefix.length).trim().split(/\s+/);
434
+ const command = parts[0];
435
+ const args = parts.slice(1);
436
+ return { command, args };
437
+ }
438
+ /**
439
+ * Execute a command
440
+ * @internal
441
+ */
442
+ async execute(commandName, args, message, channel, client, api) {
443
+ const command = this.commands.get(commandName);
444
+ if (!command) {
445
+ return;
446
+ }
447
+ const context = new CommandContext(
448
+ commandName,
449
+ args,
450
+ message,
451
+ channel,
452
+ client,
453
+ api
454
+ );
455
+ try {
456
+ await command.handler(context);
457
+ } catch (error) {
458
+ if (this.client) {
459
+ this.client.emit("error", error);
460
+ }
461
+ throw error;
462
+ }
463
+ }
464
+ };
465
+
466
+ // src/client/managers/ChannelManager.ts
467
+ var ChannelManager = class {
468
+ api;
469
+ cache;
470
+ constructor(api) {
471
+ this.api = api;
472
+ this.cache = this.constructor.createCollection();
473
+ }
474
+ /**
475
+ * Create a Collection instance (avoiding circular dependency)
476
+ */
477
+ static createCollection() {
478
+ const { Collection: Collection2 } = (init_types(), __toCommonJS(types_exports));
479
+ return new Collection2();
480
+ }
481
+ /**
482
+ * Fetch channels for a server
483
+ */
484
+ async fetch(serverId) {
485
+ const channels = await this.api.getChannels(serverId);
486
+ for (const channel of channels) {
487
+ this.cache.set(channel.id, channel);
488
+ }
489
+ return channels;
490
+ }
491
+ /**
492
+ * Get a channel from cache by ID
493
+ */
494
+ get(channelId) {
495
+ return this.cache.get(channelId);
496
+ }
497
+ /**
498
+ * Find a channel by name in a server
499
+ */
500
+ findByName(serverId, name) {
501
+ return this.cache.find(
502
+ (channel) => channel.serverId === serverId && channel.name === name
503
+ );
504
+ }
505
+ /**
506
+ * Get all channels from cache
507
+ */
508
+ getAll() {
509
+ return this.cache;
510
+ }
511
+ /**
512
+ * Clear the channel cache
513
+ */
514
+ clear() {
515
+ this.cache.clear();
516
+ }
517
+ /**
518
+ * Add or update a channel in cache
519
+ */
520
+ set(channel) {
521
+ this.cache.set(channel.id, channel);
522
+ return this;
523
+ }
524
+ /**
525
+ * Remove a channel from cache
526
+ */
527
+ delete(channelId) {
528
+ return this.cache.delete(channelId);
529
+ }
530
+ };
531
+
532
+ // src/client/managers/MessageManager.ts
533
+ var MessageManager = class {
534
+ api;
535
+ cache;
536
+ constructor(api) {
537
+ this.api = api;
538
+ this.cache = this.constructor.createCollection();
539
+ }
540
+ /**
541
+ * Create a Collection instance (avoiding circular dependency)
542
+ */
543
+ static createCollection() {
544
+ const { Collection: Collection2 } = (init_types(), __toCommonJS(types_exports));
545
+ return new Collection2();
546
+ }
547
+ /**
548
+ * Fetch messages for a channel
549
+ */
550
+ async fetch(channelId, options = {}) {
551
+ const response = await this.api.getMessages(channelId, options);
552
+ for (const message of response.data) {
553
+ this.cache.set(message.id, message);
554
+ }
555
+ return response;
556
+ }
557
+ /**
558
+ * Send a message to a channel
559
+ */
560
+ async send(channelId, content, options) {
561
+ const message = await this.api.sendMessage(channelId, content, options);
562
+ this.cache.set(message.id, message);
563
+ return message;
564
+ }
565
+ /**
566
+ * Reply to a message
567
+ */
568
+ async reply(channelId, messageId, content, author, originalContent) {
569
+ const message = await this.api.reply(
570
+ channelId,
571
+ messageId,
572
+ content,
573
+ author,
574
+ originalContent
575
+ );
576
+ this.cache.set(message.id, message);
577
+ return message;
578
+ }
579
+ /**
580
+ * Edit a message
581
+ */
582
+ async edit(messageId, content) {
583
+ const message = await this.api.editMessage(messageId, content);
584
+ this.cache.set(messageId, message);
585
+ return message;
586
+ }
587
+ /**
588
+ * Delete a message
589
+ */
590
+ async delete(messageId) {
591
+ await this.api.deleteMessage(messageId);
592
+ this.cache.delete(messageId);
593
+ }
594
+ /**
595
+ * Get a message from cache by ID
596
+ */
597
+ get(messageId) {
598
+ return this.cache.get(messageId);
599
+ }
600
+ /**
601
+ * Get all messages from cache
602
+ */
603
+ getAll() {
604
+ return this.cache;
605
+ }
606
+ /**
607
+ * Clear the message cache
608
+ */
609
+ clear() {
610
+ this.cache.clear();
611
+ }
612
+ /**
613
+ * Add or update a message in cache
614
+ */
615
+ set(message) {
616
+ this.cache.set(message.id, message);
617
+ return this;
618
+ }
619
+ /**
620
+ * Remove a message from cache
621
+ */
622
+ deleteFromCache(messageId) {
623
+ return this.cache.delete(messageId);
624
+ }
625
+ };
626
+
627
+ // src/client/managers/ServerManager.ts
628
+ var ServerManager = class {
629
+ api;
630
+ cache;
631
+ constructor(api) {
632
+ this.api = api;
633
+ this.cache = this.constructor.createCollection();
634
+ }
635
+ /**
636
+ * Create a Collection instance (avoiding circular dependency)
637
+ */
638
+ static createCollection() {
639
+ const { Collection: Collection2 } = (init_types(), __toCommonJS(types_exports));
640
+ return new Collection2();
641
+ }
642
+ /**
643
+ * Fetch all servers the bot has access to
644
+ */
645
+ async fetch() {
646
+ const servers = await this.api.getServers();
647
+ for (const server of servers) {
648
+ this.cache.set(server.id, server);
649
+ }
650
+ return servers;
651
+ }
652
+ /**
653
+ * Fetch the bot's server memberships with details
654
+ */
655
+ async fetchBotServers() {
656
+ const botServers = await this.api.getBotServers();
657
+ for (const botServer of botServers) {
658
+ this.cache.set(botServer.serverId, {
659
+ id: botServer.serverId,
660
+ name: botServer.serverName,
661
+ iconUrl: botServer.serverIconUrl,
662
+ ownerId: "",
663
+ createdAt: botServer.joinedAt
664
+ });
665
+ }
666
+ return botServers.map((bs) => ({
667
+ id: bs.serverId,
668
+ name: bs.serverName,
669
+ iconUrl: bs.serverIconUrl,
670
+ ownerId: "",
671
+ createdAt: bs.joinedAt
672
+ }));
673
+ }
674
+ /**
675
+ * Join a server via invite code
676
+ */
677
+ async join(inviteCode) {
678
+ const server = await this.api.joinServer(inviteCode);
679
+ this.cache.set(server.id, server);
680
+ return server;
681
+ }
682
+ /**
683
+ * Leave a server
684
+ */
685
+ async leave(serverId) {
686
+ await this.api.leaveServer(serverId);
687
+ this.cache.delete(serverId);
688
+ }
689
+ /**
690
+ * Get a server from cache by ID
691
+ */
692
+ get(serverId) {
693
+ return this.cache.get(serverId);
694
+ }
695
+ /**
696
+ * Find a server by name
697
+ */
698
+ findByName(name) {
699
+ return this.cache.find((server) => server.name === name);
700
+ }
701
+ /**
702
+ * Get all servers from cache
703
+ */
704
+ getAll() {
705
+ return this.cache;
706
+ }
707
+ /**
708
+ * Clear the server cache
709
+ */
710
+ clear() {
711
+ this.cache.clear();
712
+ }
713
+ /**
714
+ * Add or update a server in cache
715
+ */
716
+ set(server) {
717
+ this.cache.set(server.id, server);
718
+ return this;
719
+ }
720
+ /**
721
+ * Remove a server from cache
722
+ */
723
+ delete(serverId) {
724
+ return this.cache.delete(serverId);
725
+ }
726
+ };
727
+
728
+ // src/util/helpers.ts
729
+ function wsToHttp(wsUrl) {
730
+ return wsUrl.replace(/^ws:\/\//i, "http://").replace(/^wss:\/\//i, "https://");
731
+ }
732
+ function httpToWs(httpUrl) {
733
+ return httpUrl.replace(/^http:\/\//i, "ws://").replace(/^https:\/\//i, "wss://");
734
+ }
735
+ function parseArgs(input) {
736
+ const args = [];
737
+ let current = "";
738
+ let inQuotes = false;
739
+ for (let i = 0; i < input.length; i++) {
740
+ const char = input[i];
741
+ if (char === '"' || char === "'") {
742
+ inQuotes = !inQuotes;
743
+ } else if (char === " " && !inQuotes) {
744
+ if (current) {
745
+ args.push(current);
746
+ current = "";
747
+ }
748
+ } else {
749
+ current += char;
750
+ }
751
+ }
752
+ if (current) {
753
+ args.push(current);
754
+ }
755
+ return args;
756
+ }
757
+ function formatMention(userId) {
758
+ return `<@${userId}>`;
759
+ }
760
+ function formatChannelMention(channelId) {
761
+ return `<#${channelId}>`;
762
+ }
763
+ function extractMentionIds(content) {
764
+ const mentionRegex = /<@([^>]+)>/g;
765
+ const ids = [];
766
+ let match;
767
+ while ((match = mentionRegex.exec(content)) !== null) {
768
+ ids.push(match[1]);
769
+ }
770
+ return ids;
771
+ }
772
+ function delay(ms) {
773
+ return new Promise((resolve) => setTimeout(resolve, ms));
774
+ }
775
+ async function retry(fn, options = {}) {
776
+ const {
777
+ maxAttempts = 3,
778
+ initialDelay = 1e3,
779
+ maxDelay = 1e4,
780
+ factor = 2
781
+ } = options;
782
+ let delayTime = initialDelay;
783
+ let lastError;
784
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
785
+ try {
786
+ return await fn();
787
+ } catch (error) {
788
+ lastError = error;
789
+ if (attempt === maxAttempts) {
790
+ throw lastError;
791
+ }
792
+ await delay(delayTime);
793
+ delayTime = Math.min(delayTime * factor, maxDelay);
794
+ }
795
+ }
796
+ throw lastError;
797
+ }
798
+
799
+ // src/util/logger.ts
800
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
801
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
802
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
803
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
804
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
805
+ return LogLevel2;
806
+ })(LogLevel || {});
807
+ var Logger = class _Logger {
808
+ level;
809
+ prefix;
810
+ constructor(prefix = "ChirpSDK", level = 1 /* INFO */) {
811
+ this.prefix = prefix;
812
+ this.level = level;
813
+ }
814
+ /**
815
+ * Set the log level
816
+ */
817
+ setLevel(level) {
818
+ this.level = level;
819
+ }
820
+ /**
821
+ * Format a log message
822
+ */
823
+ format(level, message, ...args) {
824
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
825
+ return `[${timestamp}] [${level}] [${this.prefix}] ${message}`;
826
+ }
827
+ /**
828
+ * Log a debug message
829
+ */
830
+ debug(message, ...args) {
831
+ if (this.level <= 0 /* DEBUG */) {
832
+ console.debug(this.format("DEBUG", message), ...args);
833
+ }
834
+ }
835
+ /**
836
+ * Log an info message
837
+ */
838
+ info(message, ...args) {
839
+ if (this.level <= 1 /* INFO */) {
840
+ console.info(this.format("INFO", message), ...args);
841
+ }
842
+ }
843
+ /**
844
+ * Log a warning message
845
+ */
846
+ warn(message, ...args) {
847
+ if (this.level <= 2 /* WARN */) {
848
+ console.warn(this.format("WARN", message), ...args);
849
+ }
850
+ }
851
+ /**
852
+ * Log an error message
853
+ */
854
+ error(message, ...args) {
855
+ if (this.level <= 3 /* ERROR */) {
856
+ console.error(this.format("ERROR", message), ...args);
857
+ }
858
+ }
859
+ /**
860
+ * Create a child logger with a different prefix
861
+ */
862
+ child(prefix) {
863
+ return new _Logger(`${this.prefix}:${prefix}`, this.level);
864
+ }
865
+ };
866
+ var logger = new Logger();
867
+
868
+ // src/client/ChirpClient.ts
869
+ var CollectionClass;
870
+ function getCollection() {
871
+ if (!CollectionClass) {
872
+ const module = (init_types(), __toCommonJS(types_exports));
873
+ CollectionClass = module.Collection;
874
+ }
875
+ return CollectionClass;
876
+ }
877
+ var ConnectionStatus = /* @__PURE__ */ ((ConnectionStatus2) => {
878
+ ConnectionStatus2["Disconnected"] = "disconnected";
879
+ ConnectionStatus2["Connecting"] = "connecting";
880
+ ConnectionStatus2["Connected"] = "connected";
881
+ ConnectionStatus2["Reconnecting"] = "reconnecting";
882
+ return ConnectionStatus2;
883
+ })(ConnectionStatus || {});
884
+ var ChirpClient = class extends SafeEventEmitter {
885
+ // ========================================================================
886
+ // Public Properties
887
+ // ========================================================================
888
+ options;
889
+ commands;
890
+ api;
891
+ user = null;
892
+ servers;
893
+ channels;
894
+ ws;
895
+ channelsManager;
896
+ messagesManager;
897
+ serversManager;
898
+ // ========================================================================
899
+ // Private Properties
900
+ // ========================================================================
901
+ token = null;
902
+ baseUrl;
903
+ wsUrl;
904
+ socket;
905
+ status = "disconnected" /* Disconnected */;
906
+ reconnectAttempts = 0;
907
+ reconnectTimer = null;
908
+ log;
909
+ // ========================================================================
910
+ // Constructor
911
+ // ========================================================================
912
+ constructor(options = {}) {
913
+ super();
914
+ this.options = {
915
+ token: options.token ?? "",
916
+ baseUrl: options.baseUrl ?? "",
917
+ wsUrl: options.wsUrl ?? "ws://localhost:3001",
918
+ commandPrefix: options.commandPrefix ?? "/",
919
+ intents: options.intents ?? ["messages", "messageUpdates", "userStatus", "typing", "servers"],
920
+ reconnectAttempts: options.reconnectAttempts ?? Infinity,
921
+ reconnectDelay: options.reconnectDelay ?? 1e3
922
+ };
923
+ if (!this.options.baseUrl) {
924
+ this.baseUrl = wsToHttp(this.options.wsUrl);
925
+ } else {
926
+ this.baseUrl = this.options.baseUrl;
927
+ }
928
+ this.wsUrl = this.options.wsUrl;
929
+ this.log = new Logger("ChirpClient");
930
+ this.api = new RESTClient(this.baseUrl, "");
931
+ this.commands = new CommandManager(this.options.commandPrefix);
932
+ const Collection2 = getCollection();
933
+ this.servers = new Collection2();
934
+ this.channels = new Collection2();
935
+ this.setupProcessHandlers();
936
+ }
937
+ // ========================================================================
938
+ // Public Methods
939
+ // ========================================================================
940
+ /**
941
+ * Connect and authenticate the bot
942
+ */
943
+ async login(token) {
944
+ if (this.status !== "disconnected" /* Disconnected */) {
945
+ throw new Error("Client is already connected or connecting");
946
+ }
947
+ this.token = token || this.options.token;
948
+ if (!this.token) {
949
+ throw new Error("No token provided. Set token in options or pass to login()");
950
+ }
951
+ this.status = "connecting" /* Connecting */;
952
+ this.log.info("Connecting to", this.wsUrl);
953
+ try {
954
+ this.api = new RESTClient(this.baseUrl, this.token);
955
+ this.user = await this.api.getBotInfo();
956
+ this.log.info(`Authenticated as ${this.user.username}`);
957
+ this.channelsManager = new ChannelManager(this.api);
958
+ this.messagesManager = new MessageManager(this.api);
959
+ this.serversManager = new ServerManager(this.api);
960
+ this.commands._setClient(this);
961
+ await this.fetchInitialData();
962
+ await this.connectWebSocket();
963
+ this.status = "connected" /* Connected */;
964
+ this.emit("ready");
965
+ this.log.info("Bot is ready");
966
+ } catch (error) {
967
+ this.status = "disconnected" /* Disconnected */;
968
+ this.log.error("Login failed:", error);
969
+ throw error;
970
+ }
971
+ }
972
+ /**
973
+ * Disconnect the bot
974
+ */
975
+ async logout() {
976
+ this.clearReconnectTimer();
977
+ this.socket?.disconnect();
978
+ this.status = "disconnected" /* Disconnected */;
979
+ this.user = null;
980
+ this.log.info("Logged out");
981
+ }
982
+ /**
983
+ * Get current connection status
984
+ */
985
+ getStatus() {
986
+ return this.status;
987
+ }
988
+ /**
989
+ * Check if the client is connected
990
+ */
991
+ isConnected() {
992
+ return this.status === "connected" /* Connected */ && this.socket?.connected === true;
993
+ }
994
+ // ========================================================================
995
+ // Private Methods - Initialization
996
+ // ========================================================================
997
+ async fetchInitialData() {
998
+ try {
999
+ const servers = await this.serversManager.fetch();
1000
+ this.log.debug(`Fetched ${servers.length} servers`);
1001
+ for (const [serverId, server] of this.serversManager.cache) {
1002
+ this.servers.set(serverId, server);
1003
+ }
1004
+ for (const server of servers) {
1005
+ try {
1006
+ const channels = await this.channelsManager.fetch(server.id);
1007
+ this.log.debug(`Fetched ${channels.length} channels for ${server.name}`);
1008
+ for (const [channelId, channel] of this.channelsManager.cache) {
1009
+ this.channels.set(channelId, channel);
1010
+ }
1011
+ } catch (error) {
1012
+ this.log.warn(`Failed to fetch channels for ${server.name}:`, error);
1013
+ }
1014
+ }
1015
+ } catch (error) {
1016
+ this.log.warn("Failed to fetch initial data:", error);
1017
+ }
1018
+ }
1019
+ setupProcessHandlers() {
1020
+ const shutdown = async () => {
1021
+ await this.logout();
1022
+ process.exit(0);
1023
+ };
1024
+ process.on("SIGINT", shutdown);
1025
+ process.on("SIGTERM", shutdown);
1026
+ }
1027
+ // ========================================================================
1028
+ // Private Methods - WebSocket Connection
1029
+ // ========================================================================
1030
+ async connectWebSocket() {
1031
+ return new Promise((resolve, reject) => {
1032
+ const socketOptions = {
1033
+ auth: { token: `Bot ${this.token}` },
1034
+ reconnection: false,
1035
+ // We handle reconnection ourselves
1036
+ transports: ["websocket"]
1037
+ };
1038
+ this.socket = io(this.wsUrl, socketOptions);
1039
+ this.ws = this.socket;
1040
+ this.socket.on("connect", () => {
1041
+ this.log.debug("WebSocket connected");
1042
+ this.reconnectAttempts = 0;
1043
+ resolve();
1044
+ });
1045
+ this.socket.on("connect_error", (error) => {
1046
+ this.log.error("WebSocket connection error:", error);
1047
+ this.status = "disconnected" /* Disconnected */;
1048
+ reject(error);
1049
+ });
1050
+ this.socket.on("disconnect", (reason) => {
1051
+ this.log.warn("WebSocket disconnected:", reason);
1052
+ this.status = "disconnected" /* Disconnected */;
1053
+ this.emit("disconnected", reason);
1054
+ if (reason !== "io client disconnect") {
1055
+ this.scheduleReconnect();
1056
+ }
1057
+ });
1058
+ this.setupEventHandlers();
1059
+ });
1060
+ }
1061
+ scheduleReconnect() {
1062
+ if (this.reconnectAttempts >= this.options.reconnectAttempts) {
1063
+ this.log.error("Max reconnect attempts reached");
1064
+ this.emit("error", new Error("Max reconnect attempts reached"));
1065
+ return;
1066
+ }
1067
+ this.status = "reconnecting" /* Reconnecting */;
1068
+ this.reconnectAttempts++;
1069
+ const delay3 = this.options.reconnectDelay * Math.min(this.reconnectAttempts, 10);
1070
+ this.log.info(`Reconnecting in ${delay3}ms (attempt ${this.reconnectAttempts})`);
1071
+ this.emit("reconnecting", this.reconnectAttempts);
1072
+ this.reconnectTimer = setTimeout(async () => {
1073
+ try {
1074
+ await this.connectWebSocket();
1075
+ this.status = "connected" /* Connected */;
1076
+ this.emit("reconnected", this.reconnectAttempts);
1077
+ this.log.info("Reconnected successfully");
1078
+ } catch (error) {
1079
+ this.log.error("Reconnect failed:", error);
1080
+ this.scheduleReconnect();
1081
+ }
1082
+ }, delay3);
1083
+ }
1084
+ clearReconnectTimer() {
1085
+ if (this.reconnectTimer) {
1086
+ clearTimeout(this.reconnectTimer);
1087
+ this.reconnectTimer = null;
1088
+ }
1089
+ }
1090
+ // ========================================================================
1091
+ // Private Methods - Event Handlers
1092
+ // ========================================================================
1093
+ setupEventHandlers() {
1094
+ this.socket.on("message:new", this.handleMessageNew.bind(this));
1095
+ this.socket.on("message:updated", this.handleMessageUpdated.bind(this));
1096
+ this.socket.on("message:deleted", this.handleMessageDeleted.bind(this));
1097
+ this.socket.on("user:status", this.handleUserStatus.bind(this));
1098
+ this.socket.on("typing:start", this.handleTypingStart.bind(this));
1099
+ if (this.hasIntent("polls")) {
1100
+ this.socket.on("poll:voted", this.handlePollVoted.bind(this));
1101
+ }
1102
+ if (this.hasIntent("threads")) {
1103
+ this.socket.on("thread:reopened", this.handleThreadReopened.bind(this));
1104
+ this.socket.on("user_joined_thread", this.handleUserJoinedThread.bind(this));
1105
+ }
1106
+ if (this.hasIntent("voice")) {
1107
+ this.socket.on("voice:joined", this.handleVoiceJoined.bind(this));
1108
+ this.socket.on("voice:left", this.handleVoiceLeft.bind(this));
1109
+ }
1110
+ this.socket.on("bot:server_joined", this.handleBotServerJoined.bind(this));
1111
+ this.socket.on("bot:server_left", this.handleBotServerLeft.bind(this));
1112
+ this.socket.on("subscription:added", this.handleSubscriptionAdded.bind(this));
1113
+ }
1114
+ hasIntent(intent) {
1115
+ return this.options.intents.includes(intent);
1116
+ }
1117
+ // ========================================================================
1118
+ // Message Event Handlers
1119
+ // ========================================================================
1120
+ async handleMessageNew(data) {
1121
+ const message = data;
1122
+ const channelId = message.channel_id;
1123
+ if (message.bot_id) {
1124
+ return;
1125
+ }
1126
+ if (!message.content || !message.content.trim()) {
1127
+ return;
1128
+ }
1129
+ this.messagesManager.set(message);
1130
+ const channel = this.channels.get(channelId);
1131
+ if (!channel) return;
1132
+ const parsed = this.commands.parse(message);
1133
+ if (parsed) {
1134
+ try {
1135
+ await this.commands.execute(
1136
+ parsed.command,
1137
+ parsed.args,
1138
+ message,
1139
+ channel,
1140
+ this,
1141
+ this.api
1142
+ );
1143
+ } catch (error) {
1144
+ this.log.error(`Command execution failed for /${parsed.command}:`, error);
1145
+ }
1146
+ return;
1147
+ }
1148
+ this.emit("message:new", message, channelId);
1149
+ }
1150
+ handleMessageUpdated(data) {
1151
+ const { channelId, messageId, content, edited, edited_at } = data;
1152
+ const cachedMessage = this.messagesManager.get(messageId);
1153
+ if (cachedMessage) {
1154
+ const updatedMessage = {
1155
+ ...cachedMessage,
1156
+ content,
1157
+ edited,
1158
+ edited_at,
1159
+ updated_at: edited_at
1160
+ };
1161
+ this.messagesManager.cache.set(messageId, updatedMessage);
1162
+ }
1163
+ this.emit("message:updated", messageId, channelId, data);
1164
+ }
1165
+ handleMessageDeleted(data) {
1166
+ const { messageId, channelId } = data;
1167
+ this.messagesManager.deleteFromCache(messageId);
1168
+ this.emit("message:deleted", messageId, channelId);
1169
+ }
1170
+ // ========================================================================
1171
+ // User Event Handlers
1172
+ // ========================================================================
1173
+ handleUserStatus(data) {
1174
+ const { userId, status, user } = data;
1175
+ this.emit("user:status", userId, status, user);
1176
+ }
1177
+ handleTypingStart(data) {
1178
+ const { userId, channelId } = data;
1179
+ this.emit("typing:start", userId, channelId);
1180
+ }
1181
+ // ========================================================================
1182
+ // Poll Event Handlers
1183
+ // ========================================================================
1184
+ handlePollVoted(data) {
1185
+ const { pollId, userId, optionId } = data;
1186
+ this.emit("poll:voted", pollId, userId, optionId);
1187
+ }
1188
+ // ========================================================================
1189
+ // Thread Event Handlers
1190
+ // ========================================================================
1191
+ handleThreadReopened(data) {
1192
+ const { threadId, channelId } = data;
1193
+ this.emit("thread:reopened", threadId, channelId);
1194
+ }
1195
+ handleUserJoinedThread(data) {
1196
+ const { threadId, userId } = data;
1197
+ this.emit("user_joined_thread", threadId, userId);
1198
+ }
1199
+ // ========================================================================
1200
+ // Voice Event Handlers
1201
+ // ========================================================================
1202
+ handleVoiceJoined(data) {
1203
+ const { userId, channelId, serverId } = data;
1204
+ this.emit("voice:joined", userId, channelId, serverId);
1205
+ }
1206
+ handleVoiceLeft(data) {
1207
+ const { userId, channelId, serverId } = data;
1208
+ this.emit("voice:left", userId, channelId, serverId);
1209
+ }
1210
+ // ========================================================================
1211
+ // Bot Event Handlers
1212
+ // ========================================================================
1213
+ handleBotServerJoined(data) {
1214
+ const { server } = data;
1215
+ const serverId = server.id;
1216
+ const serverName = server.name;
1217
+ this.log.info(`Joined server: ${serverName}`);
1218
+ this.emit("bot:server_joined", serverId, serverName);
1219
+ const serverInfo = {
1220
+ id: serverId,
1221
+ name: serverName,
1222
+ iconUrl: server.icon_url,
1223
+ ownerId: "",
1224
+ // Unknown at this point
1225
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1226
+ // Approximate
1227
+ };
1228
+ this.servers.set(serverId, serverInfo);
1229
+ this.channelsManager.fetch(serverId).then((channels) => {
1230
+ for (const [channelId, channel] of this.channelsManager.cache) {
1231
+ this.channels.set(channelId, channel);
1232
+ }
1233
+ }).catch((error) => {
1234
+ this.log.warn(`Failed to fetch channels for ${serverName}:`, error);
1235
+ });
1236
+ }
1237
+ handleBotServerLeft(data) {
1238
+ const { server } = data;
1239
+ const serverId = server.id;
1240
+ const serverName = server.name;
1241
+ this.log.info(`Left server: ${serverName}`);
1242
+ this.emit("bot:server_left", serverId, serverName);
1243
+ this.servers.delete(serverId);
1244
+ for (const [channelId, channel] of this.channels) {
1245
+ if (channel.serverId === serverId) {
1246
+ this.channels.delete(channelId);
1247
+ }
1248
+ }
1249
+ }
1250
+ // ========================================================================
1251
+ // Subscription Event Handlers
1252
+ // ========================================================================
1253
+ handleSubscriptionAdded(data) {
1254
+ this.emit("subscription:added", data);
1255
+ }
1256
+ };
1257
+
1258
+ // src/commands/CommandBuilder.ts
1259
+ var CommandBuilder = class {
1260
+ name = "";
1261
+ description = "";
1262
+ usage;
1263
+ handler;
1264
+ /**
1265
+ * Set the command name
1266
+ */
1267
+ setName(name) {
1268
+ this.name = name;
1269
+ return this;
1270
+ }
1271
+ /**
1272
+ * Set the command description
1273
+ */
1274
+ setDescription(description) {
1275
+ this.description = description;
1276
+ return this;
1277
+ }
1278
+ /**
1279
+ * Set the command usage example
1280
+ */
1281
+ setUsage(usage) {
1282
+ this.usage = usage;
1283
+ return this;
1284
+ }
1285
+ /**
1286
+ * Set the command handler
1287
+ */
1288
+ setHandler(handler) {
1289
+ this.handler = handler;
1290
+ return this;
1291
+ }
1292
+ /**
1293
+ * Build the command object
1294
+ */
1295
+ build() {
1296
+ if (!this.name) {
1297
+ throw new Error("Command name is required");
1298
+ }
1299
+ if (!this.description) {
1300
+ throw new Error("Command description is required");
1301
+ }
1302
+ if (!this.handler) {
1303
+ throw new Error("Command handler is required");
1304
+ }
1305
+ return {
1306
+ name: this.name,
1307
+ description: this.description,
1308
+ usage: this.usage,
1309
+ handler: this.handler
1310
+ // Cast to satisfy Command type
1311
+ };
1312
+ }
1313
+ };
1314
+
1315
+ // src/errors/index.ts
1316
+ var ChirpError = class extends Error {
1317
+ constructor(message) {
1318
+ super(message);
1319
+ this.name = "ChirpError";
1320
+ Error.captureStackTrace?.(this, this.constructor);
1321
+ }
1322
+ };
1323
+ var AuthenticationError = class extends ChirpError {
1324
+ constructor(message = "Authentication failed") {
1325
+ super(message);
1326
+ this.name = "AuthenticationError";
1327
+ }
1328
+ };
1329
+ var RateLimitError = class extends ChirpError {
1330
+ retryAfter;
1331
+ constructor(message, retryAfter = 0) {
1332
+ super(message);
1333
+ this.name = "RateLimitError";
1334
+ this.retryAfter = retryAfter;
1335
+ }
1336
+ };
1337
+ var NotFoundError = class extends ChirpError {
1338
+ constructor(message = "Resource not found") {
1339
+ super(message);
1340
+ this.name = "NotFoundError";
1341
+ }
1342
+ };
1343
+ var PermissionError = class extends ChirpError {
1344
+ constructor(message = "Insufficient permissions") {
1345
+ super(message);
1346
+ this.name = "PermissionError";
1347
+ }
1348
+ };
1349
+ var ValidationError = class extends ChirpError {
1350
+ constructor(message = "Validation failed") {
1351
+ super(message);
1352
+ this.name = "ValidationError";
1353
+ }
1354
+ };
1355
+ var ConnectionError = class extends ChirpError {
1356
+ constructor(message = "Connection failed") {
1357
+ super(message);
1358
+ this.name = "ConnectionError";
1359
+ }
1360
+ };
1361
+ var CommandError = class extends ChirpError {
1362
+ commandName;
1363
+ constructor(message, commandName) {
1364
+ super(message);
1365
+ this.name = "CommandError";
1366
+ this.commandName = commandName;
1367
+ }
1368
+ };
1369
+ export {
1370
+ APIError,
1371
+ AuthenticationError,
1372
+ ChannelManager,
1373
+ ChirpClient,
1374
+ ChirpError,
1375
+ CommandBuilder,
1376
+ CommandContext,
1377
+ CommandError,
1378
+ CommandManager,
1379
+ ConnectionError,
1380
+ ConnectionStatus,
1381
+ LogLevel,
1382
+ Logger,
1383
+ MessageManager,
1384
+ NotFoundError,
1385
+ PermissionError,
1386
+ RESTClient,
1387
+ RateLimitError,
1388
+ SafeEventEmitter,
1389
+ ServerManager,
1390
+ ValidationError,
1391
+ ChirpClient as default,
1392
+ delay,
1393
+ extractMentionIds,
1394
+ formatChannelMention,
1395
+ formatMention,
1396
+ httpToWs,
1397
+ logger,
1398
+ parseArgs,
1399
+ retry,
1400
+ wsToHttp
1401
+ };
1402
+ //# sourceMappingURL=index.js.map