@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/README.md +57 -0
- package/dist/index.d.ts +891 -0
- package/dist/index.js +1402 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
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
|