@stoatx/client 0.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/LICENSE +674 -0
- package/README.md +131 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.cjs +2800 -0
- package/dist/index.d.cts +1338 -0
- package/dist/index.d.ts +1338 -0
- package/dist/index.js +2734 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2734 @@
|
|
|
1
|
+
// src/client/Client.ts
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
|
|
4
|
+
// src/gateway/GatewayManager.ts
|
|
5
|
+
import WebSocket from "ws";
|
|
6
|
+
|
|
7
|
+
// src/structures/Base.ts
|
|
8
|
+
var Base = class _Base {
|
|
9
|
+
id;
|
|
10
|
+
cachedAt = Date.now();
|
|
11
|
+
client;
|
|
12
|
+
constructor(client, data) {
|
|
13
|
+
this.id = data._id;
|
|
14
|
+
this.client = client;
|
|
15
|
+
Object.defineProperty(this, "client", {
|
|
16
|
+
value: client,
|
|
17
|
+
enumerable: false,
|
|
18
|
+
writable: false
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compares this object with another to see if they represent the same entity.
|
|
23
|
+
*/
|
|
24
|
+
equals(other) {
|
|
25
|
+
if (typeof other === "string") return this.id === other;
|
|
26
|
+
return other instanceof _Base && this.id === other.id;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns the UUID string when the object is cast to a string.
|
|
30
|
+
*/
|
|
31
|
+
toString() {
|
|
32
|
+
return this.id;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Helper to quickly clone a structure
|
|
36
|
+
*/
|
|
37
|
+
_clone() {
|
|
38
|
+
return Object.assign(Object.create(this), this);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/structures/Message.ts
|
|
43
|
+
import * as util from "util";
|
|
44
|
+
|
|
45
|
+
// src/structures/Attachment.ts
|
|
46
|
+
var Attachment = class extends Base {
|
|
47
|
+
id;
|
|
48
|
+
tag;
|
|
49
|
+
filename;
|
|
50
|
+
metadata;
|
|
51
|
+
contentType;
|
|
52
|
+
size;
|
|
53
|
+
deleted;
|
|
54
|
+
reported;
|
|
55
|
+
messageId;
|
|
56
|
+
userId;
|
|
57
|
+
serverId;
|
|
58
|
+
objectId;
|
|
59
|
+
constructor(client, data) {
|
|
60
|
+
super(client, { ...data, id: data._id });
|
|
61
|
+
this.id = data._id;
|
|
62
|
+
this.tag = data.tag;
|
|
63
|
+
this.filename = data.filename;
|
|
64
|
+
this.metadata = data.metadata ?? { type: "File" };
|
|
65
|
+
this.contentType = data.content_type;
|
|
66
|
+
this.size = data.size;
|
|
67
|
+
this.deleted = data.deleted ?? false;
|
|
68
|
+
this.reported = data.reported ?? false;
|
|
69
|
+
this.messageId = data.message_id;
|
|
70
|
+
this.userId = data.user_id;
|
|
71
|
+
this.serverId = data.server_id;
|
|
72
|
+
this.objectId = data.object_id;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Automatically constructs the direct CDN URL for this file
|
|
76
|
+
*/
|
|
77
|
+
get url() {
|
|
78
|
+
return `https://cdn.stoatusercontent.com/${this.tag}/${this.id}/${this.filename}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Helper boolean to quickly check if it's an image
|
|
82
|
+
*/
|
|
83
|
+
get isImage() {
|
|
84
|
+
return this.metadata.type === "Image";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/structures/Message.ts
|
|
89
|
+
import { decodeTime } from "ulid";
|
|
90
|
+
var Message = class extends Base {
|
|
91
|
+
content = null;
|
|
92
|
+
authorId;
|
|
93
|
+
channelId;
|
|
94
|
+
embeds = [];
|
|
95
|
+
attachments = [];
|
|
96
|
+
editedAt = null;
|
|
97
|
+
createdAt = null;
|
|
98
|
+
flags = 0;
|
|
99
|
+
interactions = null;
|
|
100
|
+
masquerade = null;
|
|
101
|
+
mentions = [];
|
|
102
|
+
pinned = false;
|
|
103
|
+
reactions = [];
|
|
104
|
+
replies = [];
|
|
105
|
+
role_mentions = [];
|
|
106
|
+
constructor(client, data) {
|
|
107
|
+
super(client, data);
|
|
108
|
+
this.authorId = data.author;
|
|
109
|
+
this.channelId = data.channel;
|
|
110
|
+
const timestamp = decodeTime(this.id);
|
|
111
|
+
if (timestamp) {
|
|
112
|
+
console.log(timestamp);
|
|
113
|
+
this.createdAt = new Date(timestamp);
|
|
114
|
+
}
|
|
115
|
+
if (data.attachments) {
|
|
116
|
+
this.attachments = data.attachments.map((fileData) => new Attachment(this.client, fileData));
|
|
117
|
+
}
|
|
118
|
+
if (data.masquerade !== void 0) this.masquerade = data.masquerade;
|
|
119
|
+
if (data.mentions !== void 0) this.mentions = data.mentions;
|
|
120
|
+
if (data.replies !== void 0) this.replies = data.replies;
|
|
121
|
+
if (data.role_mentions !== void 0) this.role_mentions = data.role_mentions;
|
|
122
|
+
this._patch(data);
|
|
123
|
+
}
|
|
124
|
+
async reply(contentOrOptions) {
|
|
125
|
+
let channel = this.channel;
|
|
126
|
+
if (!channel) channel = await this.client.channels.fetch(this.channelId);
|
|
127
|
+
const options = typeof contentOrOptions === "string" ? { content: contentOrOptions } : { ...contentOrOptions };
|
|
128
|
+
const repliesArray = options.replies ? [...options.replies] : [];
|
|
129
|
+
const alreadyReplying = repliesArray.some((reply) => reply.id === this.id);
|
|
130
|
+
if (!alreadyReplying) {
|
|
131
|
+
repliesArray.push({
|
|
132
|
+
id: this.id,
|
|
133
|
+
mention: true
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
options.replies = repliesArray;
|
|
137
|
+
return channel.messages.send(options);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Edits this message.
|
|
141
|
+
* @param contentOrOptions The new content or options for the message.
|
|
142
|
+
*/
|
|
143
|
+
async edit(contentOrOptions) {
|
|
144
|
+
let channel = this.channel;
|
|
145
|
+
if (!channel) channel = await this.client.channels.fetch(this.channelId);
|
|
146
|
+
return await channel.messages.edit(this.id, contentOrOptions);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Deletes this message.
|
|
150
|
+
*/
|
|
151
|
+
async delete() {
|
|
152
|
+
let channel = this.channel;
|
|
153
|
+
if (!channel) channel = await this.client.channels.fetch(this.channelId);
|
|
154
|
+
await channel.messages.delete(this.id);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Pins this message.
|
|
158
|
+
*/
|
|
159
|
+
async pin() {
|
|
160
|
+
let channel = this.channel;
|
|
161
|
+
if (!channel) channel = await this.client.channels.fetch(this.channelId);
|
|
162
|
+
await channel.messages.pin(this.id);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Unpins this message.
|
|
166
|
+
*/
|
|
167
|
+
async unpin() {
|
|
168
|
+
let channel = this.channel;
|
|
169
|
+
if (!channel) channel = await this.client.channels.fetch(this.channelId);
|
|
170
|
+
await channel.messages.unpin(this.id);
|
|
171
|
+
}
|
|
172
|
+
/** Gets the Channel object from cache */
|
|
173
|
+
get channel() {
|
|
174
|
+
return this.client.channels.cache.get(this.channelId);
|
|
175
|
+
}
|
|
176
|
+
/** Gets the Global User object from cache */
|
|
177
|
+
get author() {
|
|
178
|
+
return this.client.users.cache.get(this.authorId);
|
|
179
|
+
}
|
|
180
|
+
/** Gets the Server Member object (if sent in a server) */
|
|
181
|
+
get member() {
|
|
182
|
+
const channel = this.channel;
|
|
183
|
+
if (channel && "serverId" in channel) {
|
|
184
|
+
const server = this.client.servers.cache.get(channel.serverId);
|
|
185
|
+
return server?.members.cache.get(this.authorId);
|
|
186
|
+
}
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
/** Gets the Server ID if this message was sent in a server channel */
|
|
190
|
+
get serverId() {
|
|
191
|
+
const channel = this.channel;
|
|
192
|
+
if (channel && "serverId" in channel) {
|
|
193
|
+
return channel.serverId;
|
|
194
|
+
}
|
|
195
|
+
return void 0;
|
|
196
|
+
}
|
|
197
|
+
/** Gets the Server object from cache */
|
|
198
|
+
get server() {
|
|
199
|
+
const serverId = this.serverId;
|
|
200
|
+
if (!serverId) return void 0;
|
|
201
|
+
return this.client.servers.cache.get(serverId);
|
|
202
|
+
}
|
|
203
|
+
_patch(data) {
|
|
204
|
+
if (data.content !== void 0) this.content = data.content;
|
|
205
|
+
if (data.edited !== void 0) this.editedAt = new Date(data.edited);
|
|
206
|
+
if (data.pinned !== void 0) this.pinned = data.pinned;
|
|
207
|
+
if (data.embeds !== void 0) this.embeds = data.embeds;
|
|
208
|
+
if (data.flags !== void 0) this.flags = data.flags;
|
|
209
|
+
if (data.reactions !== void 0) this.reactions = data.reactions;
|
|
210
|
+
if (data.interactions !== void 0) {
|
|
211
|
+
if (data.interactions) {
|
|
212
|
+
this.interactions = {
|
|
213
|
+
reactions: data.interactions.reactions ?? [],
|
|
214
|
+
restrictReactions: data.interactions.restrict_reactions ?? false
|
|
215
|
+
};
|
|
216
|
+
} else {
|
|
217
|
+
this.interactions = null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// This tells Node.js exactly how to print this object in console.log()
|
|
222
|
+
[util.inspect.custom](depth, options, inspect10) {
|
|
223
|
+
const { client, authorId, channelId, ...props } = this;
|
|
224
|
+
const data = {
|
|
225
|
+
...props,
|
|
226
|
+
author: this.author,
|
|
227
|
+
channel: this.channel,
|
|
228
|
+
server: this.server,
|
|
229
|
+
member: this.member
|
|
230
|
+
};
|
|
231
|
+
return `${this.constructor.name} ${inspect10(data, { ...options, depth: depth ?? options.depth })}`;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/structures/User.ts
|
|
236
|
+
var UserRelationship = /* @__PURE__ */ ((UserRelationship2) => {
|
|
237
|
+
UserRelationship2["None"] = "None";
|
|
238
|
+
UserRelationship2["User"] = "User";
|
|
239
|
+
UserRelationship2["Friend"] = "Friend";
|
|
240
|
+
UserRelationship2["Outgoing"] = "Outgoing";
|
|
241
|
+
UserRelationship2["Incoming"] = "Incoming";
|
|
242
|
+
UserRelationship2["Blocked"] = "Blocked";
|
|
243
|
+
UserRelationship2["BlockedOther"] = "BlockedOther";
|
|
244
|
+
return UserRelationship2;
|
|
245
|
+
})(UserRelationship || {});
|
|
246
|
+
var UserPresence = /* @__PURE__ */ ((UserPresence2) => {
|
|
247
|
+
UserPresence2["Online"] = "Online";
|
|
248
|
+
UserPresence2["Idle"] = "Idle";
|
|
249
|
+
UserPresence2["Focus"] = "Focus";
|
|
250
|
+
UserPresence2["Busy"] = "Busy";
|
|
251
|
+
UserPresence2["Invisible"] = "Invisible";
|
|
252
|
+
return UserPresence2;
|
|
253
|
+
})(UserPresence || {});
|
|
254
|
+
var User = class extends Base {
|
|
255
|
+
discriminator;
|
|
256
|
+
online;
|
|
257
|
+
relationship;
|
|
258
|
+
username;
|
|
259
|
+
avatar;
|
|
260
|
+
badges;
|
|
261
|
+
bot;
|
|
262
|
+
displayName;
|
|
263
|
+
flags;
|
|
264
|
+
privileged;
|
|
265
|
+
status;
|
|
266
|
+
constructor(client, data) {
|
|
267
|
+
super(client, data);
|
|
268
|
+
this.bot = false;
|
|
269
|
+
this.privileged = false;
|
|
270
|
+
this.flags = 0;
|
|
271
|
+
this._patch(data);
|
|
272
|
+
}
|
|
273
|
+
_patch(data, clear) {
|
|
274
|
+
if (data.username !== void 0) this.username = data.username;
|
|
275
|
+
if (data.discriminator !== void 0) this.discriminator = data.discriminator;
|
|
276
|
+
if (data.online !== void 0) this.online = data.online;
|
|
277
|
+
if (data.relationship !== void 0) this.relationship = data.relationship;
|
|
278
|
+
if (data.display_name !== void 0) this.displayName = data.display_name;
|
|
279
|
+
if (data.badges !== void 0) this.badges = data.badges;
|
|
280
|
+
if (data.flags !== void 0) this.flags = data.flags;
|
|
281
|
+
if (data.privileged !== void 0) this.privileged = data.privileged ?? false;
|
|
282
|
+
if (data.status !== void 0) this.status = data.status;
|
|
283
|
+
if (data.bot !== void 0) {
|
|
284
|
+
this.bot = data.bot ? { owner: data.bot.owner } : false;
|
|
285
|
+
}
|
|
286
|
+
if (data.avatar !== void 0) {
|
|
287
|
+
this.avatar = new Attachment(this.client, data.avatar);
|
|
288
|
+
}
|
|
289
|
+
if (clear && Array.isArray(clear)) {
|
|
290
|
+
for (const field of clear) {
|
|
291
|
+
if (field === "Avatar") this.avatar = null;
|
|
292
|
+
if (field === "StatusText" && this.status) this.status.text = null;
|
|
293
|
+
if (field === "DisplayName") this.displayName = null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Convenience getter to return the user's tag (username#discriminator)
|
|
299
|
+
*/
|
|
300
|
+
get tag() {
|
|
301
|
+
return `${this.username}#${this.discriminator}`;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Fetch a User to update their information
|
|
305
|
+
* @param force Skip the cache check and force an API request
|
|
306
|
+
* @returns The fetched User object
|
|
307
|
+
* @throws Error if the user cannot be found or fetched
|
|
308
|
+
* @example
|
|
309
|
+
* // Fetch a user
|
|
310
|
+
* await user.fetch();
|
|
311
|
+
*/
|
|
312
|
+
async fetch(force = false) {
|
|
313
|
+
return await this.client.users.fetch(this.id, force);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/structures/ClientUser.ts
|
|
318
|
+
var ClientUser = class extends User {
|
|
319
|
+
constructor(client, data) {
|
|
320
|
+
super(client, data);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/gateway/GatewayManager.ts
|
|
325
|
+
var GatewayManager = class {
|
|
326
|
+
constructor(client) {
|
|
327
|
+
this.client = client;
|
|
328
|
+
}
|
|
329
|
+
ws = null;
|
|
330
|
+
pingInterval = null;
|
|
331
|
+
token = null;
|
|
332
|
+
reconnectAttempts = 0;
|
|
333
|
+
maxReconnectWait = 6e4;
|
|
334
|
+
// Cap at 60s
|
|
335
|
+
isIntentionalClose = false;
|
|
336
|
+
async connect(token) {
|
|
337
|
+
this.token = token;
|
|
338
|
+
this.isIntentionalClose = false;
|
|
339
|
+
this.client.emit("debug", "Connecting to Stoat Gateway...");
|
|
340
|
+
if (this.ws) {
|
|
341
|
+
this.ws.removeAllListeners();
|
|
342
|
+
this.ws = null;
|
|
343
|
+
}
|
|
344
|
+
const baseUrl = "wss://stoat.chat/events";
|
|
345
|
+
const url = `${baseUrl}?version=1&format=json&token=${this.token}`;
|
|
346
|
+
this.ws = new WebSocket(url);
|
|
347
|
+
this.ws.on("open", () => {
|
|
348
|
+
this.client.emit("debug", "WebSocket Opened. Starting ping loop...");
|
|
349
|
+
this.reconnectAttempts = 0;
|
|
350
|
+
this.startPingLoop();
|
|
351
|
+
});
|
|
352
|
+
this.ws.on("message", (data) => this.handleMessage(data));
|
|
353
|
+
this.ws.on("close", (code, reason) => {
|
|
354
|
+
this.client.emit("debug", `WebSocket closed: ${code} - ${reason.toString()}`);
|
|
355
|
+
if (this.pingInterval) clearInterval(this.pingInterval);
|
|
356
|
+
if (!this.isIntentionalClose) {
|
|
357
|
+
this.reconnect();
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
this.ws.on("error", (error) => {
|
|
361
|
+
this.client.emit("error", error);
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
handleMessage(rawData) {
|
|
365
|
+
const payload = JSON.parse(rawData.toString());
|
|
366
|
+
const eventType = payload.type;
|
|
367
|
+
switch (eventType) {
|
|
368
|
+
case "Error":
|
|
369
|
+
this.client.emit("error", new Error(`Gateway Error: ${payload.error || JSON.stringify(payload)}`));
|
|
370
|
+
break;
|
|
371
|
+
case "Authenticated":
|
|
372
|
+
this.client.emit("debug", "Successfully authenticated with Stoat.");
|
|
373
|
+
break;
|
|
374
|
+
case "Ready": {
|
|
375
|
+
if (payload.channels) {
|
|
376
|
+
for (const rawChannel of payload.channels) {
|
|
377
|
+
this.client.channels._add(rawChannel);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (payload.servers) {
|
|
381
|
+
for (const rawServer of payload.servers) {
|
|
382
|
+
this.client.servers._add(rawServer);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (payload.users) {
|
|
386
|
+
for (const rawUser of payload.users) {
|
|
387
|
+
this.client.users._add(rawUser);
|
|
388
|
+
if (rawUser.relation === "User" && !this.client.user) {
|
|
389
|
+
this.client.user = new ClientUser(this.client, rawUser);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
this.client.emit("ready", payload);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case "Message": {
|
|
397
|
+
if (payload.user) this.client.users._add(payload.user);
|
|
398
|
+
const channel = this.client.channels.cache.get(payload.channel);
|
|
399
|
+
let message;
|
|
400
|
+
if (channel) {
|
|
401
|
+
message = channel.messages._add(payload);
|
|
402
|
+
if ("serverId" in channel) {
|
|
403
|
+
const serverId = channel.serverId;
|
|
404
|
+
const server = this.client.servers.cache.get(serverId);
|
|
405
|
+
if (server && payload.member && payload.user) {
|
|
406
|
+
server.members._add({ ...payload.member, user: payload.user });
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
message = new Message(this.client, payload);
|
|
411
|
+
}
|
|
412
|
+
this.client.emit("messageCreate", message);
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
case "MessageUpdate": {
|
|
416
|
+
const channel = this.client.channels.cache.get(payload.channel);
|
|
417
|
+
const existing = channel?.messages.cache.get(payload.id);
|
|
418
|
+
if (existing) {
|
|
419
|
+
const oldMessage = existing._clone();
|
|
420
|
+
existing._patch(payload.data);
|
|
421
|
+
this.client.emit("messageUpdate", oldMessage, existing);
|
|
422
|
+
} else {
|
|
423
|
+
const newMessage = new Message(this.client, { id: payload.id, channelId: payload.channel, ...payload.data });
|
|
424
|
+
this.client.emit("messageUpdate", null, newMessage);
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case "MessageAppend": {
|
|
429
|
+
const channel = this.client.channels.cache.get(payload.channel);
|
|
430
|
+
const message = channel?.messages.cache.get(payload.id);
|
|
431
|
+
if (message && payload.append.embeds) {
|
|
432
|
+
const oldMessage = message._clone();
|
|
433
|
+
message.embeds.push(...payload.append.embeds);
|
|
434
|
+
this.client.emit("messageUpdate", oldMessage, message);
|
|
435
|
+
}
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case "MessageDelete": {
|
|
439
|
+
const channel = this.client.channels.cache.get(payload.channel);
|
|
440
|
+
let message;
|
|
441
|
+
if (channel) {
|
|
442
|
+
message = channel.messages.cache.get(payload.id);
|
|
443
|
+
channel.messages.cache.delete(payload.id);
|
|
444
|
+
}
|
|
445
|
+
if (message) {
|
|
446
|
+
this.client.emit("messageDelete", message);
|
|
447
|
+
} else {
|
|
448
|
+
this.client.emit("messageDelete", { id: payload.id, channelId: payload.channel });
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case "Pong":
|
|
453
|
+
this.client.emit("debug", "Received Pong from server.");
|
|
454
|
+
break;
|
|
455
|
+
case "ChannelCreate": {
|
|
456
|
+
const channel = this.client.channels._add(payload);
|
|
457
|
+
this.client.emit("channelCreate", channel);
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case "ChannelUpdate": {
|
|
461
|
+
const existing = this.client.channels.cache.get(payload.id);
|
|
462
|
+
if (existing) {
|
|
463
|
+
const oldChannel = existing._clone();
|
|
464
|
+
if ("_patch" in existing) {
|
|
465
|
+
existing._patch(payload.data, payload.clear);
|
|
466
|
+
}
|
|
467
|
+
this.client.emit("channelUpdate", oldChannel, existing);
|
|
468
|
+
} else {
|
|
469
|
+
const newChannel = this.client.channels._add({ id: payload.id, ...payload.data });
|
|
470
|
+
this.client.emit("channelUpdate", null, newChannel);
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
case "ChannelDelete": {
|
|
475
|
+
const channel = this.client.channels.cache.get(payload.id);
|
|
476
|
+
if (channel) {
|
|
477
|
+
this.client.channels.cache.delete(channel.id);
|
|
478
|
+
this.client.emit("channelDelete", channel);
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case "ServerCreate": {
|
|
483
|
+
const server = this.client.servers._add(payload);
|
|
484
|
+
this.client.emit("serverCreate", server);
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
case "ServerUpdate": {
|
|
488
|
+
const existing = this.client.servers.cache.get(payload.id);
|
|
489
|
+
if (existing) {
|
|
490
|
+
const oldServer = existing._clone();
|
|
491
|
+
existing._patch(payload.data, payload.clear);
|
|
492
|
+
this.client.emit("serverUpdate", oldServer, existing);
|
|
493
|
+
} else {
|
|
494
|
+
const newServer = this.client.servers._add({ id: payload.id, ...payload.data });
|
|
495
|
+
this.client.emit("serverUpdate", null, newServer);
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
case "ServerDelete": {
|
|
500
|
+
const server = this.client.servers.cache.get(payload.id);
|
|
501
|
+
if (server) {
|
|
502
|
+
this.client.servers.cache.delete(payload.id);
|
|
503
|
+
this.client.emit("serverDelete", server);
|
|
504
|
+
} else {
|
|
505
|
+
this.client.emit("serverDelete", payload.id);
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
case "ServerMemberJoin": {
|
|
510
|
+
const server = this.client.servers.cache.get(payload.id);
|
|
511
|
+
if (server) {
|
|
512
|
+
const member = server.members._add({ user: payload.user });
|
|
513
|
+
this.client.emit("serverMemberJoin", member);
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
case "ServerMemberLeave": {
|
|
518
|
+
const server = this.client.servers.cache.get(payload.id);
|
|
519
|
+
if (server) {
|
|
520
|
+
const member = server.members.cache.get(payload.user);
|
|
521
|
+
if (member) {
|
|
522
|
+
server.members.cache.delete(payload.user);
|
|
523
|
+
this.client.emit("serverMemberLeave", member);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case "UserUpdate": {
|
|
529
|
+
const existing = this.client.users.cache.get(payload.id);
|
|
530
|
+
if (existing) {
|
|
531
|
+
const oldUser = existing._clone();
|
|
532
|
+
existing._patch(payload.data, payload.clear);
|
|
533
|
+
this.client.emit("userUpdate", oldUser, existing);
|
|
534
|
+
}
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
default:
|
|
538
|
+
this.client.emit("raw", payload);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
startPingLoop() {
|
|
542
|
+
if (this.pingInterval) clearInterval(this.pingInterval);
|
|
543
|
+
this.pingInterval = setInterval(() => {
|
|
544
|
+
this.client.emit("debug", "Sending Ping...");
|
|
545
|
+
this.send({ type: "Ping", data: Date.now() });
|
|
546
|
+
}, 2e4);
|
|
547
|
+
}
|
|
548
|
+
send(payload) {
|
|
549
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
550
|
+
this.ws.send(JSON.stringify(payload));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
reconnect() {
|
|
554
|
+
if (!this.token) {
|
|
555
|
+
return this.client.emit("error", new Error("RECONNECT_FAILED: No token available."));
|
|
556
|
+
}
|
|
557
|
+
let waitTime = Math.pow(2, this.reconnectAttempts) * 1e3;
|
|
558
|
+
const jitter = waitTime * 0.2 * Math.random();
|
|
559
|
+
waitTime = Math.min(waitTime + jitter, this.maxReconnectWait);
|
|
560
|
+
this.reconnectAttempts++;
|
|
561
|
+
this.client.emit(
|
|
562
|
+
"debug",
|
|
563
|
+
`Attempting to reconnect in ${Math.round(waitTime / 1e3)}s... (Attempt ${this.reconnectAttempts})`
|
|
564
|
+
);
|
|
565
|
+
setTimeout(() => {
|
|
566
|
+
void this.connect(this.token);
|
|
567
|
+
}, waitTime);
|
|
568
|
+
}
|
|
569
|
+
disconnect() {
|
|
570
|
+
this.isIntentionalClose = true;
|
|
571
|
+
if (this.pingInterval) clearInterval(this.pingInterval);
|
|
572
|
+
if (this.ws) {
|
|
573
|
+
this.ws.close(1e3, "Client disconnected gracefully");
|
|
574
|
+
this.ws.removeAllListeners();
|
|
575
|
+
this.ws = null;
|
|
576
|
+
}
|
|
577
|
+
this.client.emit("debug", "Gateway disconnected intentionally.");
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// src/rest/RESTManager.ts
|
|
582
|
+
import { request } from "undici";
|
|
583
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
584
|
+
var AsyncBucket = class {
|
|
585
|
+
remaining = 1;
|
|
586
|
+
resetAt = 0;
|
|
587
|
+
queue = Promise.resolve();
|
|
588
|
+
};
|
|
589
|
+
var RESTManager = class {
|
|
590
|
+
constructor(client) {
|
|
591
|
+
this.client = client;
|
|
592
|
+
}
|
|
593
|
+
baseURL = "https://stoat.chat/api";
|
|
594
|
+
token = null;
|
|
595
|
+
buckets = /* @__PURE__ */ new Map();
|
|
596
|
+
setToken(token) {
|
|
597
|
+
this.token = token;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Generates a local identifier for the bucket based on method and path.
|
|
601
|
+
*/
|
|
602
|
+
getRouteKey(method, endpoint) {
|
|
603
|
+
return `${method}:${endpoint}`;
|
|
604
|
+
}
|
|
605
|
+
makeRequest(method, endpoint, body) {
|
|
606
|
+
const routeKey = this.getRouteKey(method, endpoint);
|
|
607
|
+
let bucket = this.buckets.get(routeKey);
|
|
608
|
+
if (!bucket) {
|
|
609
|
+
bucket = new AsyncBucket();
|
|
610
|
+
this.buckets.set(routeKey, bucket);
|
|
611
|
+
}
|
|
612
|
+
return new Promise((resolve, reject) => {
|
|
613
|
+
bucket.queue = bucket.queue.then(async () => {
|
|
614
|
+
try {
|
|
615
|
+
const result = await this.execute(method, endpoint, body, bucket);
|
|
616
|
+
resolve(result);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
reject(error);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
async execute(method, endpoint, body, bucket) {
|
|
624
|
+
if (!this.token) {
|
|
625
|
+
throw new Error("REST_NOT_READY: You must call client.login() before making API requests.");
|
|
626
|
+
}
|
|
627
|
+
const url = `${this.baseURL}${endpoint}`;
|
|
628
|
+
if (bucket.remaining <= 0 && Date.now() < bucket.resetAt) {
|
|
629
|
+
const waitTime = bucket.resetAt - Date.now();
|
|
630
|
+
this.client.emit("debug", `Bucket [${method}:${endpoint}] exhausted. Waiting ${waitTime}ms proactively...`);
|
|
631
|
+
await sleep(waitTime);
|
|
632
|
+
}
|
|
633
|
+
const options = {
|
|
634
|
+
method,
|
|
635
|
+
headers: {
|
|
636
|
+
"X-Bot-Token": this.token,
|
|
637
|
+
"Content-Type": "application/json"
|
|
638
|
+
},
|
|
639
|
+
...body ? { body: JSON.stringify(body) } : {}
|
|
640
|
+
};
|
|
641
|
+
const response = await request(url, options);
|
|
642
|
+
const remainingHeader = response.headers["x-ratelimit-remaining"];
|
|
643
|
+
const resetAfterHeader = response.headers["x-ratelimit-reset-after"];
|
|
644
|
+
if (remainingHeader !== void 0 && resetAfterHeader !== void 0) {
|
|
645
|
+
bucket.remaining = Number(remainingHeader);
|
|
646
|
+
bucket.resetAt = Date.now() + Number(resetAfterHeader);
|
|
647
|
+
}
|
|
648
|
+
if (response.statusCode === 429) {
|
|
649
|
+
const data2 = await response.body.json();
|
|
650
|
+
const retryMs = data2.retry_after || Number(resetAfterHeader) || 5e3;
|
|
651
|
+
this.client.emit("debug", `Hit 429 on [${method}:${endpoint}]. Retrying in ${retryMs}ms.`);
|
|
652
|
+
bucket.remaining = 0;
|
|
653
|
+
bucket.resetAt = Date.now() + retryMs;
|
|
654
|
+
await sleep(retryMs);
|
|
655
|
+
return this.execute(method, endpoint, body, bucket);
|
|
656
|
+
}
|
|
657
|
+
const data = await response.body.json();
|
|
658
|
+
if (response.statusCode >= 400) {
|
|
659
|
+
let errorMessage = "Unknown Error";
|
|
660
|
+
if (typeof data === "object" && data !== null && "message" in data) {
|
|
661
|
+
errorMessage = String(data.message);
|
|
662
|
+
}
|
|
663
|
+
throw new Error(`[Stoat API Error ${response.statusCode}]: ${errorMessage}`);
|
|
664
|
+
}
|
|
665
|
+
return data;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Uploads a file to Stoat's CDN and returns the File ID
|
|
669
|
+
*/
|
|
670
|
+
async uploadFile(filename, fileBuffer) {
|
|
671
|
+
if (!this.token) throw new Error("REST_NOT_READY: No token available.");
|
|
672
|
+
const url = "https://cdn.stoatusercontent.com/attachments";
|
|
673
|
+
const formData = new FormData();
|
|
674
|
+
formData.append("file", new Blob([fileBuffer]), filename);
|
|
675
|
+
const response = await fetch(url, {
|
|
676
|
+
method: "POST",
|
|
677
|
+
headers: {
|
|
678
|
+
"X-Bot-Token": this.token
|
|
679
|
+
},
|
|
680
|
+
body: formData
|
|
681
|
+
});
|
|
682
|
+
if (!response.ok) {
|
|
683
|
+
throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
|
|
684
|
+
}
|
|
685
|
+
const data = await response.json();
|
|
686
|
+
return data.id;
|
|
687
|
+
}
|
|
688
|
+
get(endpoint) {
|
|
689
|
+
return this.makeRequest("GET", endpoint);
|
|
690
|
+
}
|
|
691
|
+
post(endpoint, body) {
|
|
692
|
+
return this.makeRequest("POST", endpoint, body);
|
|
693
|
+
}
|
|
694
|
+
patch(endpoint, body) {
|
|
695
|
+
return this.makeRequest("PATCH", endpoint, body);
|
|
696
|
+
}
|
|
697
|
+
delete(endpoint) {
|
|
698
|
+
return this.makeRequest("DELETE", endpoint);
|
|
699
|
+
}
|
|
700
|
+
async put(endpoint, body) {
|
|
701
|
+
return this.makeRequest("PUT", endpoint, body);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
// src/utils/Collection.ts
|
|
706
|
+
var Collection = class _Collection extends Map {
|
|
707
|
+
limit;
|
|
708
|
+
constructor(limit = Infinity) {
|
|
709
|
+
super();
|
|
710
|
+
this.limit = limit;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Overrides the default set method to enforce the maximum cache size.
|
|
714
|
+
*/
|
|
715
|
+
set(key, value) {
|
|
716
|
+
if (this.limit === 0) return this;
|
|
717
|
+
if (this.size >= this.limit && !this.has(key)) {
|
|
718
|
+
const oldestKey = this.keys().next().value;
|
|
719
|
+
if (oldestKey !== void 0) {
|
|
720
|
+
this.delete(oldestKey);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return super.set(key, value);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Finds the first item where the given function returns true.
|
|
727
|
+
*/
|
|
728
|
+
find(fn) {
|
|
729
|
+
for (const [key, val] of this) {
|
|
730
|
+
if (fn(val, key, this)) return val;
|
|
731
|
+
}
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Returns a new Collection containing only the items where the function returns true.
|
|
736
|
+
*/
|
|
737
|
+
filter(fn) {
|
|
738
|
+
const results = new _Collection();
|
|
739
|
+
for (const [key, val] of this) {
|
|
740
|
+
if (fn(val, key, this)) results.set(key, val);
|
|
741
|
+
}
|
|
742
|
+
return results;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Maps each item to a new array of values.
|
|
746
|
+
*/
|
|
747
|
+
map(fn) {
|
|
748
|
+
const results = [];
|
|
749
|
+
for (const [key, val] of this) {
|
|
750
|
+
results.push(fn(val, key, this));
|
|
751
|
+
}
|
|
752
|
+
return results;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Gets the very first value in the Collection (based on insertion order).
|
|
756
|
+
*/
|
|
757
|
+
first() {
|
|
758
|
+
return this.values().next().value;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Gets the very last value in the Collection.
|
|
762
|
+
*/
|
|
763
|
+
last() {
|
|
764
|
+
const arr = Array.from(this.values());
|
|
765
|
+
return arr[arr.length - 1];
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Checks if at least one item matches the condition.
|
|
769
|
+
*/
|
|
770
|
+
some(fn) {
|
|
771
|
+
for (const [key, val] of this) {
|
|
772
|
+
if (fn(val, key, this)) return true;
|
|
773
|
+
}
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
// src/managers/MessageManager.ts
|
|
779
|
+
import * as util2 from "util";
|
|
780
|
+
|
|
781
|
+
// src/managers/BaseManager.ts
|
|
782
|
+
var BaseManager = class {
|
|
783
|
+
constructor(client, limit = Infinity) {
|
|
784
|
+
this.client = client;
|
|
785
|
+
this.cache = new Collection(limit);
|
|
786
|
+
}
|
|
787
|
+
cache;
|
|
788
|
+
/**
|
|
789
|
+
* Transforms raw data into a Structure, patches if existing, and saves to cache.
|
|
790
|
+
* @internal
|
|
791
|
+
*/
|
|
792
|
+
_add(data) {
|
|
793
|
+
const id = this.extractId(data);
|
|
794
|
+
const existing = this.cache.get(id);
|
|
795
|
+
if (existing && typeof existing._patch === "function") {
|
|
796
|
+
existing._patch(data);
|
|
797
|
+
return existing;
|
|
798
|
+
}
|
|
799
|
+
const structure = this.construct(data);
|
|
800
|
+
this.cache.set(id, structure);
|
|
801
|
+
return structure;
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
// src/managers/MessageManager.ts
|
|
806
|
+
var MessageManager = class extends BaseManager {
|
|
807
|
+
constructor(client, channel, limit = Infinity) {
|
|
808
|
+
super(client, limit);
|
|
809
|
+
this.channel = channel;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Tell BaseManager how to find the ID for Messages
|
|
813
|
+
*/
|
|
814
|
+
extractId(data) {
|
|
815
|
+
return data._id ?? data.id;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Tell BaseManager how to build a Message
|
|
819
|
+
*/
|
|
820
|
+
construct(data) {
|
|
821
|
+
return new Message(this.client, data);
|
|
822
|
+
}
|
|
823
|
+
async fetch(id) {
|
|
824
|
+
const data = await this.client.rest.get(`/channels/${this.channel.id}/messages/${id}`);
|
|
825
|
+
return this._add(data);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Fetches multiple messages from the channel using specific filter parameters.
|
|
829
|
+
* @param options The query parameters to filter the fetched messages.
|
|
830
|
+
* @returns A promise that resolves to a Collection of fetched Messages.
|
|
831
|
+
* @throws {Error} If the API request fails.
|
|
832
|
+
* @example
|
|
833
|
+
* // Fetch the last 50 messages in the channel
|
|
834
|
+
* const messages = await channel.messages.fetchMany({ limit: 50, sort: "Latest" });
|
|
835
|
+
*
|
|
836
|
+
* // Fetch 20 messages before a specific message ID
|
|
837
|
+
* const history = await channel.messages.fetchMany({ limit: 20, before: "01H..." });
|
|
838
|
+
*/
|
|
839
|
+
async fetchMany(options = {}) {
|
|
840
|
+
const params = new URLSearchParams();
|
|
841
|
+
if (options.limit !== void 0) params.append("limit", options.limit.toString());
|
|
842
|
+
if (options.before !== void 0) params.append("before", options.before);
|
|
843
|
+
if (options.after !== void 0) params.append("after", options.after);
|
|
844
|
+
if (options.sort !== void 0) params.append("sort", options.sort);
|
|
845
|
+
if (options.nearby !== void 0) params.append("nearby", options.nearby);
|
|
846
|
+
if (options.includeUsers !== void 0) params.append("include_users", options.includeUsers.toString());
|
|
847
|
+
const queryString = params.toString();
|
|
848
|
+
const endpoint = `/channels/${this.channel.id}/messages${queryString ? `?${queryString}` : ""}`;
|
|
849
|
+
const data = await this.client.rest.get(endpoint);
|
|
850
|
+
const rawMessages = Array.isArray(data) ? data : data.messages || [];
|
|
851
|
+
if (!Array.isArray(data) && data.users) {
|
|
852
|
+
for (const userData of data.users) {
|
|
853
|
+
this.client.users._add(userData);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
const fetched = new Collection();
|
|
857
|
+
for (const rawMsg of rawMessages) {
|
|
858
|
+
const msg = this._add(rawMsg);
|
|
859
|
+
fetched.set(msg.id, msg);
|
|
860
|
+
}
|
|
861
|
+
return fetched;
|
|
862
|
+
}
|
|
863
|
+
resolveId(message) {
|
|
864
|
+
if (typeof message === "string") return message;
|
|
865
|
+
if (message instanceof Message) return message.id;
|
|
866
|
+
throw new Error("Invalid MessageResolvable: must be a Message object or a string ID.");
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Sends a new message to this channel.
|
|
870
|
+
* @param contentOrOptions The string content or message options payload.
|
|
871
|
+
* @returns A promise that resolves to the sent Message.
|
|
872
|
+
*/
|
|
873
|
+
async send(contentOrOptions) {
|
|
874
|
+
const payload = typeof contentOrOptions === "string" ? { content: contentOrOptions } : { ...contentOrOptions };
|
|
875
|
+
if (payload.embeds) {
|
|
876
|
+
payload.embeds = payload.embeds.map(
|
|
877
|
+
(embed) => typeof embed.toJSON === "function" ? embed.toJSON() : embed
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
const data = await this.client.rest.post(`/channels/${this.channel.id}/messages`, payload);
|
|
881
|
+
return new Message(this.client, data);
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Edits an existing message.
|
|
885
|
+
* @param message The MessageResolvable (object or ID) to edit.
|
|
886
|
+
* @param contentOrOptions The new content or options.
|
|
887
|
+
* @returns A promise that resolves to the updated Message.
|
|
888
|
+
*/
|
|
889
|
+
async edit(message, contentOrOptions) {
|
|
890
|
+
const id = this.resolveId(message);
|
|
891
|
+
const payload = typeof contentOrOptions === "string" ? { content: contentOrOptions } : { ...contentOrOptions };
|
|
892
|
+
if (payload.embeds) {
|
|
893
|
+
payload.embeds = payload.embeds.map(
|
|
894
|
+
(embed) => typeof embed.toJSON === "function" ? embed.toJSON() : embed
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
const data = await this.client.rest.patch(`/channels/${this.channel.id}/messages/${id}`, payload);
|
|
898
|
+
const existing = this.cache.get(id);
|
|
899
|
+
if (existing) {
|
|
900
|
+
existing._patch(data);
|
|
901
|
+
return existing;
|
|
902
|
+
}
|
|
903
|
+
return new Message(this.client, data);
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Deletes a message from the channel.
|
|
907
|
+
* @param message The MessageResolvable to delete.
|
|
908
|
+
*/
|
|
909
|
+
async delete(message) {
|
|
910
|
+
const id = this.resolveId(message);
|
|
911
|
+
await this.client.rest.delete(`/channels/${this.channel.id}/messages/${id}`);
|
|
912
|
+
this.cache.delete(id);
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Pins a message in the channel.
|
|
916
|
+
* @param message The MessageResolvable to pin.
|
|
917
|
+
*/
|
|
918
|
+
async pin(message) {
|
|
919
|
+
const id = this.resolveId(message);
|
|
920
|
+
await this.client.rest.post(`/channels/${this.channel.id}/messages/${id}/pin`, {});
|
|
921
|
+
const existing = this.cache.get(id);
|
|
922
|
+
if (existing) existing.pinned = true;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Unpins a message in the channel.
|
|
926
|
+
* @param message The MessageResolvable to unpin.
|
|
927
|
+
*/
|
|
928
|
+
async unpin(message) {
|
|
929
|
+
const id = this.resolveId(message);
|
|
930
|
+
await this.client.rest.delete(`/channels/${this.channel.id}/messages/${id}/pin`);
|
|
931
|
+
const existing = this.cache.get(id);
|
|
932
|
+
if (existing) existing.pinned = false;
|
|
933
|
+
}
|
|
934
|
+
[util2.inspect.custom]() {
|
|
935
|
+
return this.cache;
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
// src/structures/BaseChannel.ts
|
|
940
|
+
var ChannelType = /* @__PURE__ */ ((ChannelType2) => {
|
|
941
|
+
ChannelType2["TEXT"] = "TextChannel";
|
|
942
|
+
ChannelType2["DM"] = "DirectMessage";
|
|
943
|
+
ChannelType2["GROUP"] = "Group";
|
|
944
|
+
return ChannelType2;
|
|
945
|
+
})(ChannelType || {});
|
|
946
|
+
var BaseChannel = class extends Base {
|
|
947
|
+
type;
|
|
948
|
+
messages;
|
|
949
|
+
constructor(client, data) {
|
|
950
|
+
super(client, data);
|
|
951
|
+
this.type = data.channel_type;
|
|
952
|
+
this.messages = new MessageManager(this.client, this);
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Sends a message to this channel.
|
|
956
|
+
* @param contentOrOptions The string content or message options payload.
|
|
957
|
+
* @returns A promise that resolves to the sent Message.
|
|
958
|
+
* @example
|
|
959
|
+
* await channel.send("Hello world!");
|
|
960
|
+
* await channel.send({ content: "Here is an embed", embeds: [myEmbed] });
|
|
961
|
+
*/
|
|
962
|
+
async send(contentOrOptions) {
|
|
963
|
+
return this.messages.send(contentOrOptions);
|
|
964
|
+
}
|
|
965
|
+
async fetch(force = true) {
|
|
966
|
+
return await this.client.channels.fetch(this.id, force);
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Fetches multiple messages from this channel.
|
|
970
|
+
* @param options The query parameters to filter the messages.
|
|
971
|
+
*/
|
|
972
|
+
async fetchMessages(options) {
|
|
973
|
+
return this.messages.fetchMany(options);
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Edits this channel.
|
|
977
|
+
* @param options The fields to update
|
|
978
|
+
*/
|
|
979
|
+
async edit(options) {
|
|
980
|
+
return await this.client.channels.edit(this.id, options);
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Deletes this channel.
|
|
984
|
+
*/
|
|
985
|
+
async delete() {
|
|
986
|
+
await this.client.channels.delete(this.id);
|
|
987
|
+
}
|
|
988
|
+
isText() {
|
|
989
|
+
return this.type === "TextChannel" /* TEXT */;
|
|
990
|
+
}
|
|
991
|
+
isDM() {
|
|
992
|
+
return this.type === "DirectMessage" /* DM */;
|
|
993
|
+
}
|
|
994
|
+
isGroup() {
|
|
995
|
+
return this.type === "Group" /* GROUP */;
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
// src/structures/TextChannel.ts
|
|
1000
|
+
var TextChannel = class extends BaseChannel {
|
|
1001
|
+
name;
|
|
1002
|
+
serverId;
|
|
1003
|
+
defaultPermissions;
|
|
1004
|
+
description;
|
|
1005
|
+
icon;
|
|
1006
|
+
lastMessageId;
|
|
1007
|
+
nsfw;
|
|
1008
|
+
slowmode;
|
|
1009
|
+
voice;
|
|
1010
|
+
constructor(client, data) {
|
|
1011
|
+
super(client, data);
|
|
1012
|
+
this.serverId = data.server;
|
|
1013
|
+
this.defaultPermissions = data.default_permissions;
|
|
1014
|
+
this.description = data.description;
|
|
1015
|
+
this.icon = data.icon;
|
|
1016
|
+
this.lastMessageId = data.last_message_id;
|
|
1017
|
+
this.nsfw = data.nsfw ?? false;
|
|
1018
|
+
this.slowmode = data.slowmode ?? 0;
|
|
1019
|
+
this.voice = data.voice;
|
|
1020
|
+
this._patch(data);
|
|
1021
|
+
}
|
|
1022
|
+
_patch(data, clear) {
|
|
1023
|
+
if (data.name !== void 0) this.name = data.name;
|
|
1024
|
+
if (data.description !== void 0) this.description = data.description;
|
|
1025
|
+
if (clear && Array.isArray(clear)) {
|
|
1026
|
+
for (const field of clear) {
|
|
1027
|
+
switch (field) {
|
|
1028
|
+
case "Description":
|
|
1029
|
+
this.description = null;
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Updates the permission overrides for the channel.
|
|
1037
|
+
* @param roleId The raw string ID of the role to update.
|
|
1038
|
+
* @param options The allow and deny permissions to set.
|
|
1039
|
+
* @returns A promise that resolves to the updated BaseChannel.
|
|
1040
|
+
* @throws {TypeError} If the channel is not a Server Channel, or options are invalid.
|
|
1041
|
+
* @throws {Error} If the API request fails.
|
|
1042
|
+
* @example
|
|
1043
|
+
* // Deny a role the ability to send messages in this channel
|
|
1044
|
+
* await channel.setRolePermissions("ROLE_ID", { deny: ["SendMessage"] });
|
|
1045
|
+
*/
|
|
1046
|
+
async setRolePermissions(roleId, options) {
|
|
1047
|
+
return await this.client.channels.setRolePermissions(this.id, roleId, options);
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Updates the default (everyone) permissions for this channel.
|
|
1051
|
+
* @param permissions The default permissions to grant globally in this channel.
|
|
1052
|
+
* @returns A promise that resolves to the updated BaseChannel.
|
|
1053
|
+
* @throws {TypeError} If invalid permissions are provided.
|
|
1054
|
+
* @throws {Error} If the API request fails.
|
|
1055
|
+
* @example
|
|
1056
|
+
* // Set the default permission to allow everyone to view the channel
|
|
1057
|
+
* await channel.setDefaultPermissions(["ViewChannel", "ReadMessageHistory"]);
|
|
1058
|
+
*/
|
|
1059
|
+
async setDefaultPermissions(permissions) {
|
|
1060
|
+
return await this.client.channels.setDefaultPermissions(this.id, permissions);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
// src/structures/UnknownChannel.ts
|
|
1065
|
+
var UnknownChannel = class extends BaseChannel {
|
|
1066
|
+
constructor(client, data) {
|
|
1067
|
+
super(client, data);
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
// src/structures/DMChannel.ts
|
|
1072
|
+
var DMChannel = class extends BaseChannel {
|
|
1073
|
+
active = false;
|
|
1074
|
+
recipients = [];
|
|
1075
|
+
lastMessageId = null;
|
|
1076
|
+
constructor(client, data) {
|
|
1077
|
+
super(client, data);
|
|
1078
|
+
}
|
|
1079
|
+
_patch(data) {
|
|
1080
|
+
if (data.active !== void 0) this.active = data.active;
|
|
1081
|
+
if (data.recipients !== void 0) this.recipients = data.recipients;
|
|
1082
|
+
if (data.last_message_id !== void 0) this.lastMessageId = data.last_message_id;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// src/structures/GroupChannel.ts
|
|
1087
|
+
import * as util3 from "util";
|
|
1088
|
+
var GroupChannel = class extends BaseChannel {
|
|
1089
|
+
name;
|
|
1090
|
+
ownerId;
|
|
1091
|
+
recipients = [];
|
|
1092
|
+
description = null;
|
|
1093
|
+
icon = null;
|
|
1094
|
+
lastMessageId = null;
|
|
1095
|
+
nsfw = false;
|
|
1096
|
+
constructor(client, data) {
|
|
1097
|
+
super(client, data);
|
|
1098
|
+
this._patch(data);
|
|
1099
|
+
}
|
|
1100
|
+
_patch(data, clear) {
|
|
1101
|
+
if (data.name !== void 0) this.name = data.name;
|
|
1102
|
+
if (data.owner !== void 0) this.ownerId = data.owner;
|
|
1103
|
+
if (data.recipients !== void 0) this.recipients = data.recipients;
|
|
1104
|
+
if (data.description !== void 0) this.description = data.description;
|
|
1105
|
+
if (data.last_message_id !== void 0) this.lastMessageId = data.last_message_id;
|
|
1106
|
+
if (data.nsfw !== void 0) this.nsfw = data.nsfw;
|
|
1107
|
+
if (data.icon !== void 0) {
|
|
1108
|
+
this.icon = data.icon ? new Attachment(this.client, data.icon) : null;
|
|
1109
|
+
}
|
|
1110
|
+
if (clear && Array.isArray(clear)) {
|
|
1111
|
+
for (const field of clear) {
|
|
1112
|
+
switch (field) {
|
|
1113
|
+
case "Name":
|
|
1114
|
+
this.name = "";
|
|
1115
|
+
break;
|
|
1116
|
+
case "Description":
|
|
1117
|
+
this.description = null;
|
|
1118
|
+
break;
|
|
1119
|
+
case "Icon":
|
|
1120
|
+
this.icon = null;
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
/** Gets the User object of the person who owns this group */
|
|
1127
|
+
get owner() {
|
|
1128
|
+
return this.client.users.cache.get(this.ownerId);
|
|
1129
|
+
}
|
|
1130
|
+
/** Resolves the recipient IDs into an array of cached User objects */
|
|
1131
|
+
get recipientUsers() {
|
|
1132
|
+
return this.recipients.map((id) => this.client.users.cache.get(id)).filter((user) => user !== void 0);
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Updates the default (everyone) permissions for this channel.
|
|
1136
|
+
* @param permissions The default permissions to grant globally in this channel.
|
|
1137
|
+
* @returns A promise that resolves to the updated BaseChannel.
|
|
1138
|
+
* @throws {TypeError} If invalid permissions are provided.
|
|
1139
|
+
* @throws {Error} If the API request fails.
|
|
1140
|
+
* @example
|
|
1141
|
+
* // Set the default permission to allow everyone to view the channel
|
|
1142
|
+
* await channel.setDefaultPermissions(["ViewChannel", "ReadMessageHistory"]);
|
|
1143
|
+
*/
|
|
1144
|
+
async setDefaultPermissions(permissions) {
|
|
1145
|
+
return await this.client.channels.setDefaultPermissions(this.id, permissions);
|
|
1146
|
+
}
|
|
1147
|
+
[util3.inspect.custom](_depth, options, inspect10) {
|
|
1148
|
+
const { client, ...props } = this;
|
|
1149
|
+
return `${this.constructor.name} ${inspect10(
|
|
1150
|
+
{
|
|
1151
|
+
...props,
|
|
1152
|
+
owner: this.owner,
|
|
1153
|
+
recipientUsers: this.recipientUsers
|
|
1154
|
+
},
|
|
1155
|
+
options
|
|
1156
|
+
)}`;
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// src/utils/ChannelFactory.ts
|
|
1161
|
+
function createChannel(client, data) {
|
|
1162
|
+
switch (data.channel_type) {
|
|
1163
|
+
case "TextChannel" /* TEXT */:
|
|
1164
|
+
return new TextChannel(client, data);
|
|
1165
|
+
case "DirectMessage" /* DM */:
|
|
1166
|
+
return new DMChannel(client, data);
|
|
1167
|
+
case "Group" /* GROUP */:
|
|
1168
|
+
return new GroupChannel(client, data);
|
|
1169
|
+
default:
|
|
1170
|
+
client.emit("debug", `Received unknown channel type: ${data.type}`);
|
|
1171
|
+
return new UnknownChannel(client, data);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// src/utils/permissions.ts
|
|
1176
|
+
var PermissionFlags = {
|
|
1177
|
+
// Server permissions
|
|
1178
|
+
ManageChannel: 1n << 0n,
|
|
1179
|
+
ManageServer: 1n << 1n,
|
|
1180
|
+
ManagePermissions: 1n << 2n,
|
|
1181
|
+
ManageRole: 1n << 3n,
|
|
1182
|
+
ManageCustomisation: 1n << 4n,
|
|
1183
|
+
// Member permissions
|
|
1184
|
+
KickMembers: 1n << 6n,
|
|
1185
|
+
BanMembers: 1n << 7n,
|
|
1186
|
+
TimeoutMembers: 1n << 8n,
|
|
1187
|
+
AssignRoles: 1n << 9n,
|
|
1188
|
+
ChangeNickname: 1n << 10n,
|
|
1189
|
+
ManageNicknames: 1n << 11n,
|
|
1190
|
+
ChangeAvatar: 1n << 12n,
|
|
1191
|
+
RemoveAvatars: 1n << 13n,
|
|
1192
|
+
// Channel permissions
|
|
1193
|
+
ViewChannel: 1n << 20n,
|
|
1194
|
+
ReadMessageHistory: 1n << 21n,
|
|
1195
|
+
SendMessage: 1n << 22n,
|
|
1196
|
+
ManageMessages: 1n << 23n,
|
|
1197
|
+
ManageWebhooks: 1n << 24n,
|
|
1198
|
+
InviteOthers: 1n << 25n,
|
|
1199
|
+
SendEmbeds: 1n << 26n,
|
|
1200
|
+
UploadFiles: 1n << 27n,
|
|
1201
|
+
Masquerade: 1n << 28n,
|
|
1202
|
+
React: 1n << 29n,
|
|
1203
|
+
BypassSlowmode: 1n << 39n,
|
|
1204
|
+
// Voice permissions
|
|
1205
|
+
Connect: 1n << 30n,
|
|
1206
|
+
Speak: 1n << 31n,
|
|
1207
|
+
Video: 1n << 32n,
|
|
1208
|
+
MuteMembers: 1n << 33n,
|
|
1209
|
+
DeafenMembers: 1n << 34n,
|
|
1210
|
+
MoveMembers: 1n << 35n,
|
|
1211
|
+
Listen: 1n << 36n,
|
|
1212
|
+
// Channel permissions (Part 2)
|
|
1213
|
+
MentionEveryone: 1n << 37n,
|
|
1214
|
+
MentionRoles: 1n << 38n,
|
|
1215
|
+
// Grant all
|
|
1216
|
+
GrantAllSafe: 0x000fffffffffffffn
|
|
1217
|
+
};
|
|
1218
|
+
var Permissions = class {
|
|
1219
|
+
/**
|
|
1220
|
+
* Converts a string, BigInt, or array of strings into a single BigInt
|
|
1221
|
+
*/
|
|
1222
|
+
static resolve(permission) {
|
|
1223
|
+
if (typeof permission === "bigint") return permission;
|
|
1224
|
+
if (typeof permission === "string") {
|
|
1225
|
+
return PermissionFlags[permission] ?? 0n;
|
|
1226
|
+
}
|
|
1227
|
+
if (Array.isArray(permission)) {
|
|
1228
|
+
return permission.reduce((acc, p) => acc | this.resolve(p), 0n);
|
|
1229
|
+
}
|
|
1230
|
+
return 0n;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Checks if a specific permission bit exists
|
|
1234
|
+
*/
|
|
1235
|
+
static has(totalPermissions, permissionToCheck) {
|
|
1236
|
+
const resolvedCheck = this.resolve(permissionToCheck);
|
|
1237
|
+
return (totalPermissions & resolvedCheck) === resolvedCheck;
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
// src/managers/ChannelManager.ts
|
|
1242
|
+
var ChannelManager = class extends BaseManager {
|
|
1243
|
+
/**
|
|
1244
|
+
* Manages API methods and caching for all channels globally.
|
|
1245
|
+
* @param client The active Client instance.
|
|
1246
|
+
* @param limit The maximum number of channels to hold in the cache.
|
|
1247
|
+
*/
|
|
1248
|
+
constructor(client, limit = Infinity) {
|
|
1249
|
+
super(client, limit);
|
|
1250
|
+
this.client = client;
|
|
1251
|
+
}
|
|
1252
|
+
extractId(data) {
|
|
1253
|
+
return data._id ?? data.id;
|
|
1254
|
+
}
|
|
1255
|
+
construct(data) {
|
|
1256
|
+
return createChannel(this.client, data);
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Resolves a ChannelResolvable to a cached BaseChannel object.
|
|
1260
|
+
* @param channel The ChannelResolvable to resolve.
|
|
1261
|
+
* @returns The resolved BaseChannel, or undefined if not cached.
|
|
1262
|
+
*/
|
|
1263
|
+
resolve(channel) {
|
|
1264
|
+
if (channel instanceof BaseChannel) return channel;
|
|
1265
|
+
if (typeof channel === "string") {
|
|
1266
|
+
const id = channel.replace(/[<#>]/g, "");
|
|
1267
|
+
return this.cache.get(id);
|
|
1268
|
+
}
|
|
1269
|
+
return void 0;
|
|
1270
|
+
}
|
|
1271
|
+
resolveText(channel) {
|
|
1272
|
+
const resolved = this.resolve(channel);
|
|
1273
|
+
return resolved?.isText() ? resolved : void 0;
|
|
1274
|
+
}
|
|
1275
|
+
resolveDM(channel) {
|
|
1276
|
+
const resolved = this.resolve(channel);
|
|
1277
|
+
return resolved?.isDM() ? resolved : void 0;
|
|
1278
|
+
}
|
|
1279
|
+
resolveGroup(channel) {
|
|
1280
|
+
const resolved = this.resolve(channel);
|
|
1281
|
+
return resolved?.isGroup() ? resolved : void 0;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Extracts ID from a ChannelResolvable.
|
|
1285
|
+
* @param channel The ChannelResolvable to extract the ID from.
|
|
1286
|
+
* @returns The extracted channel ID.
|
|
1287
|
+
* @throws {TypeError} If an invalid type is provided.
|
|
1288
|
+
*/
|
|
1289
|
+
resolveId(channel) {
|
|
1290
|
+
if (channel instanceof BaseChannel) {
|
|
1291
|
+
return channel.id;
|
|
1292
|
+
}
|
|
1293
|
+
if (typeof channel === "string") {
|
|
1294
|
+
return channel.replace(/[<#>]/g, "");
|
|
1295
|
+
}
|
|
1296
|
+
throw new TypeError("Invalid ChannelResolvable provided. Expected a BaseChannel object or a string ID/Mention.");
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Fetches a Channel from the API or resolves it from the local cache.
|
|
1300
|
+
* @param channel The ID, mention, or Channel object to fetch.
|
|
1301
|
+
* @param force Whether to skip the cache check and force a direct API request. Defaults to true.
|
|
1302
|
+
* @returns A promise that resolves to the fetched BaseChannel object.
|
|
1303
|
+
* @throws {TypeError} If an invalid ChannelResolvable is provided.
|
|
1304
|
+
* @throws {Error} If the API request fails.
|
|
1305
|
+
* @example
|
|
1306
|
+
* // Fetch a channel, bypassing cache
|
|
1307
|
+
* const channel = await client.channels.fetch("01H...");
|
|
1308
|
+
*/
|
|
1309
|
+
async fetch(channel, force = true) {
|
|
1310
|
+
if (!force) {
|
|
1311
|
+
const cached = this.resolve(channel);
|
|
1312
|
+
if (cached) return cached;
|
|
1313
|
+
}
|
|
1314
|
+
const id = this.resolveId(channel);
|
|
1315
|
+
const data = await this.client.rest.get(`/channels/${id}`);
|
|
1316
|
+
return this._add(data);
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Edits a channel in the server.
|
|
1320
|
+
* @param channel The ChannelResolvable to edit.
|
|
1321
|
+
* @param options The fields to update.
|
|
1322
|
+
* @returns A promise that resolves to the updated BaseChannel.
|
|
1323
|
+
* @throws {TypeError} If invalid options or ChannelResolvable are provided.
|
|
1324
|
+
* @throws {Error} If the API request fails.
|
|
1325
|
+
* @example
|
|
1326
|
+
* // Update channel name and remove its description
|
|
1327
|
+
* await client.channels.edit("01H...", { name: "general", description: null });
|
|
1328
|
+
*/
|
|
1329
|
+
async edit(channel, options) {
|
|
1330
|
+
if (!options || typeof options !== "object") {
|
|
1331
|
+
throw new TypeError("ChannelEditOptions must be a valid object.");
|
|
1332
|
+
}
|
|
1333
|
+
const payload = {};
|
|
1334
|
+
const remove = [];
|
|
1335
|
+
if (options.name !== void 0) payload.name = options.name;
|
|
1336
|
+
if (options.owner !== void 0) payload.owner = options.owner;
|
|
1337
|
+
if (options.nsfw !== void 0) payload.nsfw = options.nsfw;
|
|
1338
|
+
if (options.archived !== void 0) payload.archived = options.archived;
|
|
1339
|
+
if (options.voice !== void 0) payload.voice = options.voice;
|
|
1340
|
+
if (options.slowmode !== void 0) payload.slowmode = options.slowmode;
|
|
1341
|
+
if (options.description !== void 0) {
|
|
1342
|
+
if (options.description === null) remove.push("Description");
|
|
1343
|
+
else payload.description = options.description;
|
|
1344
|
+
}
|
|
1345
|
+
if (options.icon !== void 0) {
|
|
1346
|
+
if (options.icon === null) remove.push("Icon");
|
|
1347
|
+
else payload.icon = options.icon;
|
|
1348
|
+
}
|
|
1349
|
+
if (remove.length > 0) payload.remove = remove;
|
|
1350
|
+
if (Object.keys(payload).length === 0) return this.fetch(channel);
|
|
1351
|
+
const data = await this.client.rest.patch(`/channels/${this.resolveId(channel)}`, payload);
|
|
1352
|
+
return this._add(data);
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Updates the permission overrides for a specific role in a channel.
|
|
1356
|
+
* @param channel The ChannelResolvable to update permissions for.
|
|
1357
|
+
* @param roleId The raw string ID of the role to update.
|
|
1358
|
+
* @param options The allow and deny permissions to set.
|
|
1359
|
+
* @returns A promise that resolves to the updated BaseChannel.
|
|
1360
|
+
* @throws {TypeError} If the channel is not a Server Channel, or options are invalid.
|
|
1361
|
+
* @throws {Error} If the API request fails.
|
|
1362
|
+
* @example
|
|
1363
|
+
* // Deny a role the ability to send messages in this channel
|
|
1364
|
+
* await client.channels.setRolePermissions(channel, "ROLE_ID", { deny: ["SendMessage"] });
|
|
1365
|
+
*/
|
|
1366
|
+
async setRolePermissions(channel, roleId, options) {
|
|
1367
|
+
const resolved = this.resolve(channel);
|
|
1368
|
+
if (resolved && !resolved.isText()) {
|
|
1369
|
+
throw new TypeError("Role permissions can only be set on Server Channels.");
|
|
1370
|
+
}
|
|
1371
|
+
if (!options || typeof options !== "object") {
|
|
1372
|
+
throw new TypeError("ChannelRolePermissionOptions must be a valid object.");
|
|
1373
|
+
}
|
|
1374
|
+
const id = this.resolveId(channel);
|
|
1375
|
+
const allowBigInt = options.allow !== void 0 ? Permissions.resolve(options.allow) : 0n;
|
|
1376
|
+
const denyBigInt = options.deny !== void 0 ? Permissions.resolve(options.deny) : 0n;
|
|
1377
|
+
const payload = {
|
|
1378
|
+
permissions: {
|
|
1379
|
+
allow: Number(allowBigInt),
|
|
1380
|
+
deny: Number(denyBigInt)
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
const data = await this.client.rest.put(`/channels/${id}/permissions/${roleId}`, payload);
|
|
1384
|
+
return this._add(data);
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Updates the default (everyone) permissions for a channel.
|
|
1388
|
+
* @param channel The ChannelResolvable to update permissions for.
|
|
1389
|
+
* @param permissions The default permissions to grant globally in this channel.
|
|
1390
|
+
* @returns A promise that resolves to the updated BaseChannel.
|
|
1391
|
+
* @throws {TypeError} If invalid permissions are provided.
|
|
1392
|
+
* @throws {Error} If the API request fails.
|
|
1393
|
+
* @example
|
|
1394
|
+
* // Set the default permission to allow everyone to view the channel
|
|
1395
|
+
* await client.channels.setDefaultPermissions(channel, ["ViewChannel", "ReadMessageHistory"]);
|
|
1396
|
+
*/
|
|
1397
|
+
async setDefaultPermissions(channel, permissions) {
|
|
1398
|
+
const resolved = this.resolve(channel);
|
|
1399
|
+
if (resolved && resolved.isDM()) {
|
|
1400
|
+
throw new TypeError("Default permissions cannot be set on Direct Message channels.");
|
|
1401
|
+
}
|
|
1402
|
+
const id = this.resolveId(channel);
|
|
1403
|
+
const permBigInt = Permissions.resolve(permissions);
|
|
1404
|
+
const payload = {
|
|
1405
|
+
permissions: Number(permBigInt)
|
|
1406
|
+
};
|
|
1407
|
+
const data = await this.client.rest.put(`/channels/${id}/permissions/default`, payload);
|
|
1408
|
+
return this._add(data);
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Deletes a server channel, leaves a group, or closes a DM.
|
|
1412
|
+
* @param channel The channel object, raw ID, or mention string to delete.
|
|
1413
|
+
* @returns A promise that resolves when the action is successful.
|
|
1414
|
+
* @throws {Error} If the API request fails.
|
|
1415
|
+
* @example
|
|
1416
|
+
* await client.channels.delete("01H...");
|
|
1417
|
+
*/
|
|
1418
|
+
async delete(channel) {
|
|
1419
|
+
const id = this.resolveId(channel);
|
|
1420
|
+
await this.client.rest.delete(`/channels/${id}`);
|
|
1421
|
+
this.cache.delete(id);
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
|
|
1425
|
+
// src/structures/Member.ts
|
|
1426
|
+
import * as util4 from "util";
|
|
1427
|
+
|
|
1428
|
+
// src/managers/MemberRoleManager.ts
|
|
1429
|
+
var MemberRoleManager = class {
|
|
1430
|
+
constructor(member) {
|
|
1431
|
+
this.member = member;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Gets a Collection of the actual Role objects this member has.
|
|
1435
|
+
* This dynamically pulls from the Server's role cache so references are always up to date
|
|
1436
|
+
*/
|
|
1437
|
+
get cache() {
|
|
1438
|
+
const fetched = new Collection();
|
|
1439
|
+
const server = this.member.server;
|
|
1440
|
+
if (!server) return fetched;
|
|
1441
|
+
for (const roleId of this.member.roleIds) {
|
|
1442
|
+
const role = server.roles.cache.get(roleId);
|
|
1443
|
+
if (role) fetched.set(role.id, role);
|
|
1444
|
+
}
|
|
1445
|
+
return fetched;
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Checks if the member has a specific role.
|
|
1449
|
+
* @param role The RoleResolvable to check for.
|
|
1450
|
+
*/
|
|
1451
|
+
has(role) {
|
|
1452
|
+
const server = this.member.server;
|
|
1453
|
+
if (!server) return false;
|
|
1454
|
+
const id = server.roles.resolveId(role);
|
|
1455
|
+
return this.member.roleIds.includes(id);
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Adds a role to the member.
|
|
1459
|
+
* @param role The RoleResolvable to add.
|
|
1460
|
+
* @returns A promise that resolves to the updated Member.
|
|
1461
|
+
*/
|
|
1462
|
+
async add(role) {
|
|
1463
|
+
const server = this.member.server;
|
|
1464
|
+
if (!server) throw new Error("Server not cached, cannot add role.");
|
|
1465
|
+
const id = server.roles.resolveId(role);
|
|
1466
|
+
if (this.member.roleIds.includes(id)) return this.member;
|
|
1467
|
+
const newRoles = [...this.member.roleIds, id];
|
|
1468
|
+
return this.member.edit({ roles: newRoles });
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Removes a role from the member.
|
|
1472
|
+
* @param role The RoleResolvable to remove.
|
|
1473
|
+
* @returns A promise that resolves to the updated Member.
|
|
1474
|
+
*/
|
|
1475
|
+
async remove(role) {
|
|
1476
|
+
const server = this.member.server;
|
|
1477
|
+
if (!server) throw new Error("Server not cached, cannot remove role.");
|
|
1478
|
+
const id = server.roles.resolveId(role);
|
|
1479
|
+
if (!this.member.roleIds.includes(id)) return this.member;
|
|
1480
|
+
const newRoles = this.member.roleIds.filter((rId) => rId !== id);
|
|
1481
|
+
return this.member.edit({ roles: newRoles });
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Overwrites all roles on the member with a completely new array.
|
|
1485
|
+
* @param roles An array of RoleResolvables.
|
|
1486
|
+
* @returns A promise that resolves to the updated Member.
|
|
1487
|
+
*/
|
|
1488
|
+
async set(roles) {
|
|
1489
|
+
const server = this.member.server;
|
|
1490
|
+
if (!server) throw new Error("Server not cached, cannot set roles.");
|
|
1491
|
+
const newRoles = roles.map((r) => server.roles.resolveId(r));
|
|
1492
|
+
return this.member.edit({ roles: newRoles });
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
// src/structures/Member.ts
|
|
1497
|
+
var Member = class extends Base {
|
|
1498
|
+
serverId;
|
|
1499
|
+
nickname = null;
|
|
1500
|
+
avatar = null;
|
|
1501
|
+
roleIds = [];
|
|
1502
|
+
joinedAt;
|
|
1503
|
+
timeout = null;
|
|
1504
|
+
canPublish = false;
|
|
1505
|
+
canRecieve = false;
|
|
1506
|
+
roles;
|
|
1507
|
+
constructor(client, data) {
|
|
1508
|
+
super(client, { _id: data.user._id });
|
|
1509
|
+
this.serverId = data.serverId || data.server_id;
|
|
1510
|
+
this.joinedAt = new Date(data.joinedAt || data.joined_at);
|
|
1511
|
+
this.roles = new MemberRoleManager(this);
|
|
1512
|
+
this._patch(data);
|
|
1513
|
+
}
|
|
1514
|
+
_patch(data) {
|
|
1515
|
+
if (data.nickname !== void 0) this.nickname = data.nickname;
|
|
1516
|
+
if (data.avatar !== void 0) this.avatar = data.avatar;
|
|
1517
|
+
if (data.roles !== void 0) this.roleIds = data.roles;
|
|
1518
|
+
if (data.timeout !== void 0) this.timeout = data.timeout ? new Date(data.timeout) : null;
|
|
1519
|
+
if (data.can_publish !== void 0) this.canPublish = data.canPublish;
|
|
1520
|
+
if (data.can_recieve !== void 0) this.canRecieve = data.canRecieve;
|
|
1521
|
+
}
|
|
1522
|
+
/** Gets the global User object for this member */
|
|
1523
|
+
get user() {
|
|
1524
|
+
return this.client.users.cache.get(this.id);
|
|
1525
|
+
}
|
|
1526
|
+
/** Gets the Server object this member belongs to */
|
|
1527
|
+
get server() {
|
|
1528
|
+
return this.client.servers.cache.get(this.serverId);
|
|
1529
|
+
}
|
|
1530
|
+
/** Resolves the array of role strings into actual Role objects */
|
|
1531
|
+
get roleObjects() {
|
|
1532
|
+
const server = this.server;
|
|
1533
|
+
if (!server) return [];
|
|
1534
|
+
return this.roleIds.map((id) => server.roles.cache.get(id)).filter((role) => role !== void 0);
|
|
1535
|
+
}
|
|
1536
|
+
/** Calculates the member's total permissions using BigInt */
|
|
1537
|
+
get permissions() {
|
|
1538
|
+
const server = this.server;
|
|
1539
|
+
if (!server) return 0n;
|
|
1540
|
+
let totalPerms = server.defaultPermissions ?? 0n;
|
|
1541
|
+
for (const role of this.roleObjects) {
|
|
1542
|
+
totalPerms |= BigInt(role.permissions);
|
|
1543
|
+
}
|
|
1544
|
+
if (server.ownerId === this.id) {
|
|
1545
|
+
return PermissionFlags.GrantAllSafe;
|
|
1546
|
+
}
|
|
1547
|
+
return totalPerms;
|
|
1548
|
+
}
|
|
1549
|
+
/** Checks if the member has a specific permission */
|
|
1550
|
+
hasPermission(permission) {
|
|
1551
|
+
return Permissions.has(this.permissions, permission);
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Edits this member's nickname, avatar, roles, or timeout.
|
|
1555
|
+
*/
|
|
1556
|
+
async edit(options) {
|
|
1557
|
+
let server = this.server;
|
|
1558
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1559
|
+
return await server.members.edit(this.id, options);
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Kicks this member from the server.
|
|
1563
|
+
*/
|
|
1564
|
+
async kick() {
|
|
1565
|
+
let server = this.server;
|
|
1566
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1567
|
+
await server.members.kick(this.id);
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Bans this member from the server.
|
|
1571
|
+
* @param options The options for this ban
|
|
1572
|
+
*/
|
|
1573
|
+
async ban(options) {
|
|
1574
|
+
let server = this.server;
|
|
1575
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1576
|
+
await server.members.ban(this.id, options);
|
|
1577
|
+
}
|
|
1578
|
+
async unban() {
|
|
1579
|
+
let server = this.client.servers.cache.get(this.serverId);
|
|
1580
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1581
|
+
await server.members.unban(this.id);
|
|
1582
|
+
}
|
|
1583
|
+
[util4.inspect.custom](depth, options, inspect10) {
|
|
1584
|
+
const { client, serverId, ...props } = this;
|
|
1585
|
+
return `${this.constructor.name} ${inspect10(
|
|
1586
|
+
{
|
|
1587
|
+
...props,
|
|
1588
|
+
user: this.user,
|
|
1589
|
+
permissions: this.permissions
|
|
1590
|
+
},
|
|
1591
|
+
{ ...options, depth: depth ?? options.depth }
|
|
1592
|
+
)}`;
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
// src/managers/MemberManager.ts
|
|
1597
|
+
import * as util5 from "util";
|
|
1598
|
+
var MemberManager = class extends BaseManager {
|
|
1599
|
+
constructor(client, server, limit = Infinity) {
|
|
1600
|
+
super(client, limit);
|
|
1601
|
+
this.server = server;
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Tell BaseManager how to handle Revolt's Member IDs
|
|
1605
|
+
* @internal
|
|
1606
|
+
*/
|
|
1607
|
+
extractId(data) {
|
|
1608
|
+
return data.user_id ?? data.id ?? (typeof data._id === "string" ? data._id : data._id?.user);
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Tell BaseManager how to build a Member
|
|
1612
|
+
* @internal
|
|
1613
|
+
*/
|
|
1614
|
+
construct(data) {
|
|
1615
|
+
if (!data.server_id && !data.serverId) {
|
|
1616
|
+
data.serverId = this.server.id;
|
|
1617
|
+
}
|
|
1618
|
+
return new Member(this.client, data);
|
|
1619
|
+
}
|
|
1620
|
+
resolve(member) {
|
|
1621
|
+
if (member instanceof Member) return member;
|
|
1622
|
+
if (member instanceof User) return this.cache.get(member.id);
|
|
1623
|
+
if (typeof member === "string") return this.cache.get(member.replace(/[<@>]/g, ""));
|
|
1624
|
+
return void 0;
|
|
1625
|
+
}
|
|
1626
|
+
resolveId(member) {
|
|
1627
|
+
if (typeof member === "string") return member.replace(/[<@>]/g, "");
|
|
1628
|
+
if ("id" in member) return member.id;
|
|
1629
|
+
throw new TypeError("Invalid MemberResolvable provided.");
|
|
1630
|
+
}
|
|
1631
|
+
async fetch(member, force = true) {
|
|
1632
|
+
if (!force) {
|
|
1633
|
+
const cached = this.resolve(member);
|
|
1634
|
+
if (cached) return cached;
|
|
1635
|
+
}
|
|
1636
|
+
const id = this.resolveId(member);
|
|
1637
|
+
const data = await this.client.rest.get(`/channels/${id}`);
|
|
1638
|
+
return this._add(data);
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Fetches multiple members from the server.
|
|
1642
|
+
* @param options Filter options for the fetch request.
|
|
1643
|
+
* @returns A promise that resolves to a Collection of fetched Members.
|
|
1644
|
+
* @throws {Error} If the API request fails.
|
|
1645
|
+
* @example
|
|
1646
|
+
* // Fetch all members in the server
|
|
1647
|
+
* const allMembers = await server.members.fetchMany();
|
|
1648
|
+
*
|
|
1649
|
+
* // Fetch only online members to save bandwidth
|
|
1650
|
+
* const onlineMembers = await server.members.fetchMany({ exclude_offline: true });
|
|
1651
|
+
*/
|
|
1652
|
+
async fetchMany(options = {}) {
|
|
1653
|
+
const params = new URLSearchParams();
|
|
1654
|
+
if (options.exclude_offline !== void 0) {
|
|
1655
|
+
params.append("exclude_offline", options.exclude_offline.toString());
|
|
1656
|
+
}
|
|
1657
|
+
const queryString = params.toString();
|
|
1658
|
+
const endpoint = `/servers/${this.server.id}/members${queryString ? `?${queryString}` : ""}`;
|
|
1659
|
+
const data = await this.client.rest.get(endpoint);
|
|
1660
|
+
if (data.users && Array.isArray(data.users)) {
|
|
1661
|
+
for (const userData of data.users) {
|
|
1662
|
+
this.client.users._add(userData);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
const fetched = new Collection();
|
|
1666
|
+
const rawMembers = data.members || (Array.isArray(data) ? data : []);
|
|
1667
|
+
for (const rawMember of rawMembers) {
|
|
1668
|
+
const member = this._add(rawMember);
|
|
1669
|
+
fetched.set(member.id, member);
|
|
1670
|
+
}
|
|
1671
|
+
return fetched;
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Edits a member in the server.
|
|
1675
|
+
* @param member The MemberResolvable to edit.
|
|
1676
|
+
* @param options The fields to update (nickname, roles, timeout, etc.).
|
|
1677
|
+
*/
|
|
1678
|
+
async edit(member, options) {
|
|
1679
|
+
const id = this.resolveId(member);
|
|
1680
|
+
const payload = {};
|
|
1681
|
+
const remove = [];
|
|
1682
|
+
if (options.nickname !== void 0) {
|
|
1683
|
+
if (options.nickname === null) remove.push("Nickname");
|
|
1684
|
+
else payload.nickname = options.nickname;
|
|
1685
|
+
}
|
|
1686
|
+
if (options.avatar !== void 0) {
|
|
1687
|
+
if (options.avatar === null) remove.push("Avatar");
|
|
1688
|
+
else payload.avatar = options.avatar;
|
|
1689
|
+
}
|
|
1690
|
+
if (options.roles !== void 0) payload.roles = options.roles;
|
|
1691
|
+
if (options.timeout !== void 0) {
|
|
1692
|
+
if (options.timeout === null) remove.push("Timeout");
|
|
1693
|
+
else payload.timeout = options.timeout;
|
|
1694
|
+
}
|
|
1695
|
+
if (remove.length > 0) payload.remove = remove;
|
|
1696
|
+
if (Object.keys(payload).length === 0) return this.fetch(id);
|
|
1697
|
+
const data = await this.client.rest.patch(`/servers/${this.server.id}/members/${id}`, payload);
|
|
1698
|
+
return this._add(data);
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Kicks a member from the server.
|
|
1702
|
+
* @param member The MemberResolvable to kick.
|
|
1703
|
+
*/
|
|
1704
|
+
async kick(member) {
|
|
1705
|
+
const id = this.resolveId(member);
|
|
1706
|
+
await this.client.rest.delete(`/servers/${this.server.id}/members/${id}`);
|
|
1707
|
+
this.cache.delete(id);
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Bans a member from the server.
|
|
1711
|
+
* @param member The MemberResolvable to ban.
|
|
1712
|
+
* @param options The ban options
|
|
1713
|
+
*/
|
|
1714
|
+
async ban(member, options) {
|
|
1715
|
+
const id = this.resolveId(member);
|
|
1716
|
+
const payload = {};
|
|
1717
|
+
if (options?.reason !== void 0) payload.reason = options.reason;
|
|
1718
|
+
if (options?.deleteMessageSeconds !== void 0) payload.delete_message_seconds = options.deleteMessageSeconds;
|
|
1719
|
+
await this.client.rest.put(`/servers/${this.server.id}/bans/${id}`, payload);
|
|
1720
|
+
this.cache.delete(id);
|
|
1721
|
+
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Unbans a user from the server
|
|
1724
|
+
* @param member The MemberResolvable to unban
|
|
1725
|
+
*/
|
|
1726
|
+
async unban(member) {
|
|
1727
|
+
const id = this.resolveId(member);
|
|
1728
|
+
await this.client.rest.delete(`/servers/${this.server.id}/bans/${id}`);
|
|
1729
|
+
}
|
|
1730
|
+
[util5.inspect.custom]() {
|
|
1731
|
+
return this.cache;
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
// src/managers/ServerChannelManager.ts
|
|
1736
|
+
import * as util6 from "util";
|
|
1737
|
+
var ServerChannelManager = class {
|
|
1738
|
+
constructor(client, server) {
|
|
1739
|
+
this.client = client;
|
|
1740
|
+
this.server = server;
|
|
1741
|
+
}
|
|
1742
|
+
get cache() {
|
|
1743
|
+
return this.client.channels.cache.filter((channel) => channel.serverId === this.server.id);
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Creates a new channel within this server.
|
|
1747
|
+
* @param options The configuration for the new channel.
|
|
1748
|
+
* @returns The newly created Channel object.
|
|
1749
|
+
*/
|
|
1750
|
+
async create(options) {
|
|
1751
|
+
if (!options.name) throw new Error("A channel name must be provided.");
|
|
1752
|
+
const payload = {
|
|
1753
|
+
name: options.name,
|
|
1754
|
+
type: options.type ?? "Text",
|
|
1755
|
+
description: options.description,
|
|
1756
|
+
nsfw: options.nsfw ?? false
|
|
1757
|
+
};
|
|
1758
|
+
if (options.type === "Voice" && options.voice?.max_users) {
|
|
1759
|
+
payload.voice = { max_users: options.voice.max_users };
|
|
1760
|
+
}
|
|
1761
|
+
const data = await this.client.rest.post(`/servers/${this.server.id}/channels`, payload);
|
|
1762
|
+
return this.client.channels._add(data);
|
|
1763
|
+
}
|
|
1764
|
+
[util6.inspect.custom]() {
|
|
1765
|
+
return this.cache;
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
// src/structures/Role.ts
|
|
1770
|
+
import * as util7 from "util";
|
|
1771
|
+
var Role = class extends Base {
|
|
1772
|
+
serverId;
|
|
1773
|
+
name;
|
|
1774
|
+
color = null;
|
|
1775
|
+
hoist = false;
|
|
1776
|
+
rank = 0;
|
|
1777
|
+
permissions = 0n;
|
|
1778
|
+
constructor(client, data, serverId) {
|
|
1779
|
+
super(client, data);
|
|
1780
|
+
this.serverId = serverId;
|
|
1781
|
+
this._patch(data);
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Updates the role instance with new data without losing the object reference.
|
|
1785
|
+
* @internal
|
|
1786
|
+
*/
|
|
1787
|
+
_patch(data) {
|
|
1788
|
+
if (data.name !== void 0) this.name = data.name;
|
|
1789
|
+
if (data.color !== void 0) this.color = data.color;
|
|
1790
|
+
if (data.hoist !== void 0) this.hoist = data.hoist;
|
|
1791
|
+
if (data.rank !== void 0) this.rank = data.rank;
|
|
1792
|
+
if (data.permissions !== void 0) {
|
|
1793
|
+
try {
|
|
1794
|
+
if (typeof data.permissions === "object" && data.permissions !== null) {
|
|
1795
|
+
const allowPerms = data.permissions.a ?? data.permissions[0] ?? 0;
|
|
1796
|
+
this.permissions = BigInt(allowPerms);
|
|
1797
|
+
} else {
|
|
1798
|
+
this.permissions = BigInt(data.permissions);
|
|
1799
|
+
}
|
|
1800
|
+
} catch {
|
|
1801
|
+
this.permissions = 0n;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* The server this role belongs to.
|
|
1807
|
+
* Pulls dynamically from the cache to prevent massive memory duplication.
|
|
1808
|
+
*/
|
|
1809
|
+
get server() {
|
|
1810
|
+
return this.client.servers.cache.get(this.serverId);
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Checks whether this role has a specific permission.
|
|
1814
|
+
* @param permission The permission to check for.
|
|
1815
|
+
* @returns True if the role has the permission.
|
|
1816
|
+
*/
|
|
1817
|
+
hasPermission(permission) {
|
|
1818
|
+
return Permissions.has(this.permissions, permission);
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Fetches this role directly from the API to ensure data is up to date.
|
|
1822
|
+
* @param force Whether to skip the cache check and force a direct API request. Defaults to false.
|
|
1823
|
+
* @returns A promise that resolves to the fetched Role object.
|
|
1824
|
+
* @throws {TypeError} If an invalid RoleResolvable is provided.
|
|
1825
|
+
* @throws {Error} If the API request fails (e.g., the role does not exist).
|
|
1826
|
+
* @example
|
|
1827
|
+
* // Refresh the role's data from the API
|
|
1828
|
+
* await role.fetch();
|
|
1829
|
+
* console.log(`Role updated, current name: ${role.name}`);
|
|
1830
|
+
*/
|
|
1831
|
+
async fetch(force = true) {
|
|
1832
|
+
let server = this.server;
|
|
1833
|
+
if (!server) {
|
|
1834
|
+
server = await this.client.servers.fetch(this.serverId);
|
|
1835
|
+
}
|
|
1836
|
+
return await server.roles.fetch(this.id, force);
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Edits the role with the given options. Only the fields provided in the options will be updated; all other fields will remain unchanged.
|
|
1840
|
+
* @param options The fields to update.
|
|
1841
|
+
* @returns A promise that resolves to the updated Role.
|
|
1842
|
+
* @throws {TypeError} If invalid options or RoleResolvable are provided.
|
|
1843
|
+
* @throws {Error} If the API request fails (e.g., lack of permissions).
|
|
1844
|
+
* @example
|
|
1845
|
+
* // Change the role's name and color
|
|
1846
|
+
* await role.edit({ name: "Senior Admin", colour: "#FFD700" });
|
|
1847
|
+
*
|
|
1848
|
+
* // Remove the custom color from the role
|
|
1849
|
+
* await role.edit({ colour: null });
|
|
1850
|
+
*/
|
|
1851
|
+
async edit(options) {
|
|
1852
|
+
let server = this.server;
|
|
1853
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1854
|
+
return await server.roles.edit(this.id, options);
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* Deletes this Role from the server.
|
|
1858
|
+
* @throws {Error} If the role cannot be deleted (e.g., lack of permissions).
|
|
1859
|
+
* @example
|
|
1860
|
+
* // Delete a role by its ID
|
|
1861
|
+
* await role.delete();
|
|
1862
|
+
*/
|
|
1863
|
+
async delete() {
|
|
1864
|
+
let server = this.server;
|
|
1865
|
+
if (!server) {
|
|
1866
|
+
server = await this.client.servers.fetch(this.serverId);
|
|
1867
|
+
}
|
|
1868
|
+
await server.roles.delete(this.id);
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Updates the permissions for this role.
|
|
1872
|
+
* @param options The allow and deny permissions to set.
|
|
1873
|
+
* @returns A promise that resolves to the updated Role object.
|
|
1874
|
+
* @throws {TypeError} If invalid options are provided.
|
|
1875
|
+
* @throws {Error} If the API request fails.
|
|
1876
|
+
* @example
|
|
1877
|
+
* // Grant the role permission to manage channels and send messages
|
|
1878
|
+
* await role.setPermissions({
|
|
1879
|
+
* allow: ["ManageChannel", "SendMessage"]
|
|
1880
|
+
* });
|
|
1881
|
+
*/
|
|
1882
|
+
async setPermissions(options) {
|
|
1883
|
+
let server = this.server;
|
|
1884
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1885
|
+
return await server.roles.setPermissions(this.id, options);
|
|
1886
|
+
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Updates the hierarchical position of this role.
|
|
1889
|
+
* Automatically reconstructs the role array and performs a bulk update.
|
|
1890
|
+
* @param newPosition The new rank/position for this role (0-indexed).
|
|
1891
|
+
* @returns A promise that resolves to this updated Role object.
|
|
1892
|
+
* @throws {Error} If API request fails.
|
|
1893
|
+
* @example
|
|
1894
|
+
* // Move this role to position 2 in the hierarchy
|
|
1895
|
+
* await role.setPosition(2);
|
|
1896
|
+
* console.log(`Role moved to rank: ${role.rank}`);
|
|
1897
|
+
*/
|
|
1898
|
+
async setPosition(newPosition) {
|
|
1899
|
+
let server = this.server;
|
|
1900
|
+
if (!server) server = await this.client.servers.fetch(this.serverId);
|
|
1901
|
+
const sortedRoles = Array.from(server.roles.cache.values()).sort((a, b) => a.rank - b.rank);
|
|
1902
|
+
const filteredRoles = sortedRoles.filter((r) => r.id !== this.id);
|
|
1903
|
+
const clampedPosition = Math.max(0, Math.min(newPosition, filteredRoles.length));
|
|
1904
|
+
filteredRoles.splice(clampedPosition, 0, this);
|
|
1905
|
+
await server.roles.setRanks(filteredRoles);
|
|
1906
|
+
return this;
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Customizer for Node.js `console.log` and `util.inspect`.
|
|
1910
|
+
* Hides the cyclic client reference and raw serverId for a cleaner output.
|
|
1911
|
+
* @internal
|
|
1912
|
+
*/
|
|
1913
|
+
[util7.inspect.custom]() {
|
|
1914
|
+
const { client, serverId, ...props } = this;
|
|
1915
|
+
return `${this.constructor.name} ${util7.inspect(props)}`;
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
|
|
1919
|
+
// src/managers/RoleManager.ts
|
|
1920
|
+
import * as util8 from "util";
|
|
1921
|
+
var RoleManager = class extends BaseManager {
|
|
1922
|
+
/**
|
|
1923
|
+
* Manages API methods and caching for server roles.
|
|
1924
|
+
*/
|
|
1925
|
+
constructor(client, server, limit = Infinity) {
|
|
1926
|
+
super(client, limit);
|
|
1927
|
+
this.server = server;
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Tell BaseManager how to find the ID for Roles
|
|
1931
|
+
*/
|
|
1932
|
+
extractId(data) {
|
|
1933
|
+
return data._id ?? data.id;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Tell BaseManager how to build a Role
|
|
1937
|
+
*/
|
|
1938
|
+
construct(data) {
|
|
1939
|
+
return new Role(this.client, data, this.server.id);
|
|
1940
|
+
}
|
|
1941
|
+
/**
|
|
1942
|
+
* Adds or updates a role in the local cache.
|
|
1943
|
+
* @internal
|
|
1944
|
+
* @param data The raw role data from the API.
|
|
1945
|
+
* @param idParam An optional ID parameter if the payload wraps the role object.
|
|
1946
|
+
* @returns The newly created or updated Role object.
|
|
1947
|
+
*/
|
|
1948
|
+
_add(data, idParam) {
|
|
1949
|
+
const id = idParam ?? data._id ?? data.id;
|
|
1950
|
+
const existing = this.cache.get(id);
|
|
1951
|
+
if (existing) {
|
|
1952
|
+
existing._patch(data);
|
|
1953
|
+
return existing;
|
|
1954
|
+
}
|
|
1955
|
+
const role = new Role(this.client, data, this.server.id);
|
|
1956
|
+
this.cache.set(role.id, role);
|
|
1957
|
+
return role;
|
|
1958
|
+
}
|
|
1959
|
+
[util8.inspect.custom]() {
|
|
1960
|
+
return this.cache;
|
|
1961
|
+
}
|
|
1962
|
+
/**
|
|
1963
|
+
* Resolves a RoleResolvable to a Role object from the cache.
|
|
1964
|
+
* @param role The RoleResolvable to resolve.
|
|
1965
|
+
* @returns The resolved Role object, or undefined if not found.
|
|
1966
|
+
*/
|
|
1967
|
+
resolve(role) {
|
|
1968
|
+
if (role instanceof Role) return role;
|
|
1969
|
+
if (typeof role === "string") {
|
|
1970
|
+
const id = role.replace(/[<%>]/g, "");
|
|
1971
|
+
return this.cache.get(id);
|
|
1972
|
+
}
|
|
1973
|
+
return void 0;
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Extracts ID from a RoleResolvable.
|
|
1977
|
+
* @param role The RoleResolvable to extract the ID from.
|
|
1978
|
+
* @returns The extracted role ID.
|
|
1979
|
+
* @throws TypeError if an invalid type is provided.
|
|
1980
|
+
*/
|
|
1981
|
+
resolveId(role) {
|
|
1982
|
+
if (role instanceof Role) {
|
|
1983
|
+
return role.id;
|
|
1984
|
+
}
|
|
1985
|
+
if (typeof role === "string") {
|
|
1986
|
+
return role.replace(/[<%>]/g, "");
|
|
1987
|
+
}
|
|
1988
|
+
throw new TypeError("Invalid RoleResolvable provided. Expected a Role object or a string ID/Mention.");
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Creates a new role in this server.
|
|
1992
|
+
* @param options The name and optional rank of the new role
|
|
1993
|
+
* @returns Role The role that was created
|
|
1994
|
+
* @throws {Error} If the role cannot be created (e.g., lack of permissions, invalid options).
|
|
1995
|
+
* @throws {TypeError} If invalid options are provided.
|
|
1996
|
+
* @example
|
|
1997
|
+
* // Create a new role named "Moderator" with rank 1
|
|
1998
|
+
* const moderatorRole = await server.roles.create({ name: "Moderator", rank: 1 });
|
|
1999
|
+
* console.log(`Created role: ${moderatorRole.name} with ID: ${moderatorRole.id}`);
|
|
2000
|
+
*/
|
|
2001
|
+
async create(options) {
|
|
2002
|
+
if (!options || typeof options !== "object") {
|
|
2003
|
+
throw new TypeError("RoleCreateOptions must be a valid object.");
|
|
2004
|
+
}
|
|
2005
|
+
if (typeof options.name !== "string" || options.name.trim().length === 0) {
|
|
2006
|
+
throw new TypeError("A valid role 'name' (string) must be provided.");
|
|
2007
|
+
}
|
|
2008
|
+
const payload = {
|
|
2009
|
+
name: options.name
|
|
2010
|
+
};
|
|
2011
|
+
const data = await this.client.rest.post(`/servers/${this.server.id}/roles`, payload);
|
|
2012
|
+
return this._add(data);
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Fetches a Role from the API or resolves it from the local cache.
|
|
2016
|
+
* @param role The ID, mention, or Role object to fetch.
|
|
2017
|
+
* @param force Whether to skip the cache check and force a direct API request. Defaults to false.
|
|
2018
|
+
* @returns A promise that resolves to the fetched Role object.
|
|
2019
|
+
* @throws {TypeError} If an invalid RoleResolvable is provided.
|
|
2020
|
+
* @throws {Error} If the API request fails (e.g., the role does not exist).
|
|
2021
|
+
* @example
|
|
2022
|
+
* // Fetch a role from the API
|
|
2023
|
+
* const role = await server.roles.fetch("01JE2MM759J5D7CHJF084R7MJ2");
|
|
2024
|
+
* console.log(`Fetched role: ${role.name} with ID: ${role.id}`);
|
|
2025
|
+
*
|
|
2026
|
+
* // Fetch a role by mention
|
|
2027
|
+
* const role = await.server.roles.fetch("<%01JE2MM759J5D7CHJF084R7MJ2>");
|
|
2028
|
+
* console.log(`Fetched role: ${role.name} with ID: ${role.id}`);
|
|
2029
|
+
*
|
|
2030
|
+
* // Force fetch a role, bypassing the cache
|
|
2031
|
+
* const role = await server.roles.fetch("01JE2MM759J5D7CHJF084R7MJ2", true);
|
|
2032
|
+
* console.log(`Fetched role: ${role.name} with ID: ${role.id}`);
|
|
2033
|
+
*/
|
|
2034
|
+
async fetch(role, force = false) {
|
|
2035
|
+
if (!force) {
|
|
2036
|
+
const cached = this.resolve(role);
|
|
2037
|
+
if (cached) return cached;
|
|
2038
|
+
}
|
|
2039
|
+
const id = this.resolveId(role);
|
|
2040
|
+
const data = await this.client.rest.get(`/servers/${this.server.id}/roles/${id}`);
|
|
2041
|
+
return this._add(data);
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Edits an existing role in the server.
|
|
2045
|
+
* @param role The RoleResolvable to edit.
|
|
2046
|
+
* @param options The fields to update.
|
|
2047
|
+
* @returns A promise that resolves to the updated Role.
|
|
2048
|
+
* @throws {TypeError} If invalid options or RoleResolvable are provided.
|
|
2049
|
+
* @throws {Error} If the API request fails (e.g., lack of permissions).
|
|
2050
|
+
* @example
|
|
2051
|
+
* // Rename a role and give it a red color
|
|
2052
|
+
* await server.roles.edit("01H...", { name: "Super Admin", colour: "#FF0000" });
|
|
2053
|
+
*
|
|
2054
|
+
* // Remove the custom color from a role
|
|
2055
|
+
* await server.roles.edit(role, { colour: null });
|
|
2056
|
+
*/
|
|
2057
|
+
async edit(role, options) {
|
|
2058
|
+
if (!options || typeof options !== "object") {
|
|
2059
|
+
throw new TypeError("RoleEditOptions must be a valid object.");
|
|
2060
|
+
}
|
|
2061
|
+
const id = this.resolveId(role);
|
|
2062
|
+
const payload = {};
|
|
2063
|
+
const remove = [];
|
|
2064
|
+
if (options.name !== void 0) payload.name = options.name;
|
|
2065
|
+
if (options.hoist !== void 0) payload.hoist = options.hoist;
|
|
2066
|
+
if (options.colour !== void 0) {
|
|
2067
|
+
if (options.colour === null) {
|
|
2068
|
+
remove.push("Colour");
|
|
2069
|
+
} else {
|
|
2070
|
+
payload.colour = options.colour;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
if (remove.length > 0) {
|
|
2074
|
+
payload.remove = remove;
|
|
2075
|
+
}
|
|
2076
|
+
if (Object.keys(payload).length === 0) {
|
|
2077
|
+
return this.fetch(id);
|
|
2078
|
+
}
|
|
2079
|
+
const data = await this.client.rest.patch(`/servers/${this.server.id}/roles/${id}`, payload);
|
|
2080
|
+
return this._add(data, id);
|
|
2081
|
+
}
|
|
2082
|
+
/**
|
|
2083
|
+
* Deletes a Role from the server.
|
|
2084
|
+
* @param role The RoleResolvable to delete
|
|
2085
|
+
* @returns A promise that resolves when the role is successfully deleted.
|
|
2086
|
+
* @throws {TypeError} If an invalid RoleResolvable is provided.
|
|
2087
|
+
* @throws {Error} If the role cannot be deleted (e.g., lack of permissions).
|
|
2088
|
+
* @example
|
|
2089
|
+
* // Delete a role by its ID
|
|
2090
|
+
* await server.roles.delete("01JE2MM759J5D7CHJF084R7MJ2");
|
|
2091
|
+
* console.log("Role deleted successfully.");
|
|
2092
|
+
*
|
|
2093
|
+
* // Delete a role using a Role object
|
|
2094
|
+
* const role = await server.roles.fetch("01JE2MM759J5D7CHJF084R7MJ2");
|
|
2095
|
+
* await server.roles.delete(role);
|
|
2096
|
+
* console.log("Role deleted successfully.");
|
|
2097
|
+
*
|
|
2098
|
+
* // Delete a role by mention
|
|
2099
|
+
* await server.roles.delete("<%01JE2MM759J5D7CHJF084R7MJ2>");
|
|
2100
|
+
* console.log("Role deleted successfully.");
|
|
2101
|
+
*/
|
|
2102
|
+
async delete(role) {
|
|
2103
|
+
const id = this.resolveId(role);
|
|
2104
|
+
await this.client.rest.delete(`/servers/${this.server.id}/roles/${id}`);
|
|
2105
|
+
this.cache.delete(id);
|
|
2106
|
+
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Updates the permissions for a role in the server.
|
|
2109
|
+
* @param role The RoleResolvable to update permissions for.
|
|
2110
|
+
* @param options The allow and deny permissions to set.
|
|
2111
|
+
* @returns A promise that resolves to the updated Role.
|
|
2112
|
+
* @throws {TypeError} If an invalid RoleResolvable or options are provided.
|
|
2113
|
+
* @throws {Error} If the API request fails.
|
|
2114
|
+
* @example
|
|
2115
|
+
* // Set permissions using an array of strings.
|
|
2116
|
+
* await server.roles.setPermissions(role, {
|
|
2117
|
+
* allow: ["ManageChannel", "ViewChannel", "SendMessage"]
|
|
2118
|
+
* });
|
|
2119
|
+
*/
|
|
2120
|
+
async setPermissions(role, options) {
|
|
2121
|
+
if (!options || typeof options !== "object") {
|
|
2122
|
+
throw new TypeError("RolePermissionOptions must be a valid object.");
|
|
2123
|
+
}
|
|
2124
|
+
const id = this.resolveId(role);
|
|
2125
|
+
const allowBigInt = options.allow !== void 0 ? Permissions.resolve(options.allow) : 0n;
|
|
2126
|
+
const denyBigInt = options.deny !== void 0 ? Permissions.resolve(options.deny) : 0n;
|
|
2127
|
+
const payload = {
|
|
2128
|
+
permissions: {
|
|
2129
|
+
allow: Number(allowBigInt),
|
|
2130
|
+
deny: Number(denyBigInt)
|
|
2131
|
+
}
|
|
2132
|
+
};
|
|
2133
|
+
const data = await this.client.rest.put(`/servers/${this.server.id}/permissions/${id}`, payload);
|
|
2134
|
+
this.server._patch(data);
|
|
2135
|
+
return this.cache.get(id);
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* Updates the hierarchical positions of roles in the server.
|
|
2139
|
+
* @param ranks An array of RoleResolvables representing the new order of roles.
|
|
2140
|
+
* @returns A promise that resolves when the ranks are successfully updated.
|
|
2141
|
+
* @throws {TypeError} If the ranks parameter is not an array or contains invalid resolvables.
|
|
2142
|
+
* @throws {Error} If the API request fails.
|
|
2143
|
+
* @example
|
|
2144
|
+
* // Reorder roles by passing an array of Role objects or IDs
|
|
2145
|
+
* await server.roles.setRanks(["RoleID_1", adminRoleObject, "RoleID_3"]);
|
|
2146
|
+
*/
|
|
2147
|
+
async setRanks(ranks) {
|
|
2148
|
+
if (!Array.isArray(ranks)) {
|
|
2149
|
+
throw new TypeError("The 'ranks' parameter must be an array of RoleResolvables.");
|
|
2150
|
+
}
|
|
2151
|
+
const mappedIds = ranks.map((role) => this.resolveId(role));
|
|
2152
|
+
const payload = {
|
|
2153
|
+
ranks: mappedIds
|
|
2154
|
+
};
|
|
2155
|
+
const data = await this.client.rest.put(`/servers/${this.server.id}/roles`, payload);
|
|
2156
|
+
this.server._patch(data);
|
|
2157
|
+
return this.server;
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
|
|
2161
|
+
// src/structures/ServerInvite.ts
|
|
2162
|
+
var ServerInvite = class {
|
|
2163
|
+
code;
|
|
2164
|
+
creatorId;
|
|
2165
|
+
channelId;
|
|
2166
|
+
constructor(data) {
|
|
2167
|
+
this.code = data._id ?? data.code;
|
|
2168
|
+
this.creatorId = data.creator;
|
|
2169
|
+
this.channelId = data.channel;
|
|
2170
|
+
}
|
|
2171
|
+
_patch(data) {
|
|
2172
|
+
this.creatorId = data.creator ?? this.creatorId;
|
|
2173
|
+
this.channelId = data.channel ?? this.channelId;
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
|
|
2177
|
+
// src/managers/ServerInviteManager.ts
|
|
2178
|
+
var ServerInviteManager = class extends BaseManager {
|
|
2179
|
+
constructor(client, server, limit = Infinity) {
|
|
2180
|
+
super(client, limit);
|
|
2181
|
+
this.server = server;
|
|
2182
|
+
}
|
|
2183
|
+
extractId(data) {
|
|
2184
|
+
return data._id ?? data.code;
|
|
2185
|
+
}
|
|
2186
|
+
construct(data) {
|
|
2187
|
+
return new ServerInvite(data);
|
|
2188
|
+
}
|
|
2189
|
+
/**
|
|
2190
|
+
* Fetches all active invites for this server.
|
|
2191
|
+
*/
|
|
2192
|
+
async fetch() {
|
|
2193
|
+
const data = await this.client.rest.get(`/servers/${this.server.id}/invites`);
|
|
2194
|
+
const rawInvites = Array.isArray(data) ? data : [];
|
|
2195
|
+
const fetched = new Collection();
|
|
2196
|
+
for (const raw of rawInvites) {
|
|
2197
|
+
const invite = this._add(raw);
|
|
2198
|
+
fetched.set(invite.code, invite);
|
|
2199
|
+
}
|
|
2200
|
+
return fetched;
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
|
|
2204
|
+
// src/structures/ServerBan.ts
|
|
2205
|
+
var ServerBan = class {
|
|
2206
|
+
userId;
|
|
2207
|
+
reason;
|
|
2208
|
+
constructor(data) {
|
|
2209
|
+
this.userId = data._id?.user ?? data.id;
|
|
2210
|
+
this.reason = data.reason ?? null;
|
|
2211
|
+
}
|
|
2212
|
+
_patch(data) {
|
|
2213
|
+
if (data.reason !== void 0) this.reason = data.reason;
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
|
|
2217
|
+
// src/managers/ServerBanManager.ts
|
|
2218
|
+
var ServerBanManager = class extends BaseManager {
|
|
2219
|
+
constructor(client, server, limit = Infinity) {
|
|
2220
|
+
super(client, limit);
|
|
2221
|
+
this.server = server;
|
|
2222
|
+
}
|
|
2223
|
+
extractId(data) {
|
|
2224
|
+
return data._id?.user ?? data.id;
|
|
2225
|
+
}
|
|
2226
|
+
construct(data) {
|
|
2227
|
+
return new ServerBan(data);
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Fetches all bans in this server.
|
|
2231
|
+
* Automatically caches the associated User objects globally!
|
|
2232
|
+
* @returns A promise that resolves to a Collection of Bans.
|
|
2233
|
+
*/
|
|
2234
|
+
async fetch() {
|
|
2235
|
+
const data = await this.client.rest.get(`/servers/${this.server.id}/bans`);
|
|
2236
|
+
if (data.users && Array.isArray(data.users)) {
|
|
2237
|
+
for (const userData of data.users) {
|
|
2238
|
+
this.client.users._add(userData);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
const rawBans = data.bans || (Array.isArray(data) ? data : []);
|
|
2242
|
+
const fetched = new Collection();
|
|
2243
|
+
for (const rawBan of rawBans) {
|
|
2244
|
+
const ban = this._add(rawBan);
|
|
2245
|
+
fetched.set(ban.userId, ban);
|
|
2246
|
+
}
|
|
2247
|
+
return fetched;
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Unbans a user from the server.
|
|
2251
|
+
* @param userId The ID of the user to unban.
|
|
2252
|
+
*/
|
|
2253
|
+
async remove(userId) {
|
|
2254
|
+
const id = userId.replace(/[<@!>]/g, "");
|
|
2255
|
+
await this.client.rest.delete(`/servers/${this.server.id}/bans/${id}`);
|
|
2256
|
+
this.cache.delete(id);
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
|
|
2260
|
+
// src/structures/Server.ts
|
|
2261
|
+
var Server = class extends Base {
|
|
2262
|
+
channelIds = [];
|
|
2263
|
+
defaultPermissions;
|
|
2264
|
+
name;
|
|
2265
|
+
ownerId;
|
|
2266
|
+
analytics = false;
|
|
2267
|
+
banner = null;
|
|
2268
|
+
categories = [];
|
|
2269
|
+
description = null;
|
|
2270
|
+
discoverable = false;
|
|
2271
|
+
flags = 0;
|
|
2272
|
+
icon = null;
|
|
2273
|
+
nsfw = false;
|
|
2274
|
+
members;
|
|
2275
|
+
channels;
|
|
2276
|
+
roles;
|
|
2277
|
+
bans;
|
|
2278
|
+
invites;
|
|
2279
|
+
constructor(client, data) {
|
|
2280
|
+
super(client, data);
|
|
2281
|
+
this.channels = new ServerChannelManager(client, this);
|
|
2282
|
+
this.members = new MemberManager(client, this);
|
|
2283
|
+
this.roles = new RoleManager(client, this);
|
|
2284
|
+
this.bans = new ServerBanManager(this.client, this);
|
|
2285
|
+
this.invites = new ServerInviteManager(this.client, this);
|
|
2286
|
+
this._patch(data);
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Updates the server instance with new data without losing the object reference.
|
|
2290
|
+
*/
|
|
2291
|
+
_patch(data, clear) {
|
|
2292
|
+
if (data.channels !== void 0) this.channelIds = data.channels;
|
|
2293
|
+
if (data.default_permissions !== void 0) {
|
|
2294
|
+
try {
|
|
2295
|
+
this.defaultPermissions = BigInt(data.default_permissions);
|
|
2296
|
+
} catch {
|
|
2297
|
+
this.defaultPermissions = 0n;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
if (data.name !== void 0) this.name = data.name;
|
|
2301
|
+
if (data.owner !== void 0) this.ownerId = data.owner;
|
|
2302
|
+
if (data.analytics !== void 0) this.analytics = data.analytics;
|
|
2303
|
+
if (data.categories !== void 0) this.categories = data.categories;
|
|
2304
|
+
if (data.description !== void 0) this.description = data.description;
|
|
2305
|
+
if (data.discoverable !== void 0) this.discoverable = data.discoverable;
|
|
2306
|
+
if (data.flags !== void 0) this.flags = data.flags;
|
|
2307
|
+
if (data.nsfw !== void 0) this.nsfw = data.nsfw;
|
|
2308
|
+
if (data.icon !== void 0) {
|
|
2309
|
+
this.icon = data.icon ? new Attachment(this.client, data.icon) : null;
|
|
2310
|
+
}
|
|
2311
|
+
if (data.banner !== void 0) {
|
|
2312
|
+
this.banner = data.banner ? new Attachment(this.client, data.banner) : null;
|
|
2313
|
+
}
|
|
2314
|
+
if (data.roles !== void 0) {
|
|
2315
|
+
for (const [id, roleData] of Object.entries(data.roles)) {
|
|
2316
|
+
this.roles._add({ id, ...roleData });
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
if (clear && Array.isArray(clear)) {
|
|
2320
|
+
for (const field of clear) {
|
|
2321
|
+
switch (field) {
|
|
2322
|
+
case "Description":
|
|
2323
|
+
this.description = null;
|
|
2324
|
+
break;
|
|
2325
|
+
case "Icon":
|
|
2326
|
+
this.icon = null;
|
|
2327
|
+
break;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Edits this server.
|
|
2334
|
+
* @param options The fields to update.
|
|
2335
|
+
*/
|
|
2336
|
+
async edit(options) {
|
|
2337
|
+
await this.client.servers.edit(this.id, options);
|
|
2338
|
+
return this;
|
|
2339
|
+
}
|
|
2340
|
+
/**
|
|
2341
|
+
* Leaves the server
|
|
2342
|
+
*/
|
|
2343
|
+
async leave() {
|
|
2344
|
+
return this.client.rest.delete(`/servers/${this.id}/leave`);
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Fetches multiple members from this server.
|
|
2348
|
+
* @param options Filter options for the fetch request.
|
|
2349
|
+
* @returns A Collection of the fetched members.
|
|
2350
|
+
*/
|
|
2351
|
+
async fetchMembers(options) {
|
|
2352
|
+
return this.members.fetchMany(options);
|
|
2353
|
+
}
|
|
2354
|
+
};
|
|
2355
|
+
|
|
2356
|
+
// src/managers/ServerManager.ts
|
|
2357
|
+
import * as util9 from "util";
|
|
2358
|
+
var ServerManager = class extends BaseManager {
|
|
2359
|
+
constructor(client, limit = Infinity) {
|
|
2360
|
+
super(client, limit);
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Tell BaseManager how to find the ID for Servers
|
|
2364
|
+
*/
|
|
2365
|
+
extractId(data) {
|
|
2366
|
+
return data._id ?? data.id;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Tell BaseManager how to build a Server
|
|
2370
|
+
*/
|
|
2371
|
+
construct(data) {
|
|
2372
|
+
return new Server(this.client, data);
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Fetches a server from the API.
|
|
2376
|
+
* @param id The server ID.
|
|
2377
|
+
* @param force Whether to skip the cache and fetch from the API.
|
|
2378
|
+
*/
|
|
2379
|
+
async fetch(id, force = false) {
|
|
2380
|
+
if (!force) {
|
|
2381
|
+
const cached = this.cache.get(id);
|
|
2382
|
+
if (cached) return cached;
|
|
2383
|
+
}
|
|
2384
|
+
const data = await this.client.rest.get(`/servers/${id}`);
|
|
2385
|
+
return this._add(data);
|
|
2386
|
+
}
|
|
2387
|
+
async edit(serverId, options) {
|
|
2388
|
+
const payload = {};
|
|
2389
|
+
const remove = [];
|
|
2390
|
+
if (options.name !== void 0) payload.name = options.name;
|
|
2391
|
+
if (options.description !== void 0) {
|
|
2392
|
+
if (options.description === null) remove.push("Description");
|
|
2393
|
+
else payload.description = options.description;
|
|
2394
|
+
}
|
|
2395
|
+
if (options.icon !== void 0) {
|
|
2396
|
+
if (options.icon === null) remove.push("Icon");
|
|
2397
|
+
else payload.icon = options.icon;
|
|
2398
|
+
}
|
|
2399
|
+
if (options.banner !== void 0) {
|
|
2400
|
+
if (options.banner === null) remove.push("Banner");
|
|
2401
|
+
else payload.banner = options.banner;
|
|
2402
|
+
}
|
|
2403
|
+
if (options.systemMessages) {
|
|
2404
|
+
payload.system_messages = {};
|
|
2405
|
+
const sm = options.systemMessages;
|
|
2406
|
+
if (sm.userJoined !== void 0) {
|
|
2407
|
+
if (sm.userJoined === null) remove.push("SystemMessageUserJoined");
|
|
2408
|
+
else payload.system_messages.user_joined = sm.userJoined;
|
|
2409
|
+
}
|
|
2410
|
+
if (sm.userLeft !== void 0) {
|
|
2411
|
+
if (sm.userLeft === null) remove.push("SystemMessageUserLeft");
|
|
2412
|
+
else payload.system_messages.user_left = sm.userLeft;
|
|
2413
|
+
}
|
|
2414
|
+
if (sm.userKicked !== void 0) {
|
|
2415
|
+
if (sm.userKicked === null) remove.push("SystemMessageUserKicked");
|
|
2416
|
+
else payload.system_messages.user_kicked = sm.userKicked;
|
|
2417
|
+
}
|
|
2418
|
+
if (sm.userBanned !== void 0) {
|
|
2419
|
+
if (sm.userBanned === null) remove.push("SystemMessageUserBanned");
|
|
2420
|
+
else payload.system_messages.user_banned = sm.userBanned;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
if (options.categories !== void 0) payload.categories = options.categories;
|
|
2424
|
+
if (options.analytics !== void 0) payload.analytics = options.analytics;
|
|
2425
|
+
if (options.owner !== void 0) payload.owner = options.owner;
|
|
2426
|
+
if (remove.length > 0) payload.remove = remove;
|
|
2427
|
+
const data = await this.client.rest.patch(`/servers/${serverId}`, payload);
|
|
2428
|
+
return this._add(data);
|
|
2429
|
+
}
|
|
2430
|
+
[util9.inspect.custom]() {
|
|
2431
|
+
return this.cache;
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
|
|
2435
|
+
// src/managers/UserManager.ts
|
|
2436
|
+
var UserManager = class extends BaseManager {
|
|
2437
|
+
constructor(client, limit = Infinity) {
|
|
2438
|
+
super(client, limit);
|
|
2439
|
+
}
|
|
2440
|
+
/**
|
|
2441
|
+
* Tell BaseManager how to find the ID for Users
|
|
2442
|
+
*/
|
|
2443
|
+
extractId(data) {
|
|
2444
|
+
return data._id ?? data.id;
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Tell BaseManager how to build a User
|
|
2448
|
+
*/
|
|
2449
|
+
construct(data) {
|
|
2450
|
+
return new User(this.client, data);
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* Resolves a UserResolvable to a User object from the cache.
|
|
2454
|
+
*/
|
|
2455
|
+
resolve(user) {
|
|
2456
|
+
if (user instanceof User) return user;
|
|
2457
|
+
if (typeof user === "string") {
|
|
2458
|
+
const id = user.replace(/[<@>]/g, "");
|
|
2459
|
+
return this.cache.get(id);
|
|
2460
|
+
}
|
|
2461
|
+
return void 0;
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Extracts ID from a UserResolvable.
|
|
2465
|
+
* @param user The UserResolvable to extract the ID from.
|
|
2466
|
+
* @returns The extracted user ID.
|
|
2467
|
+
* @throws TypeError if an invalid type is provided.
|
|
2468
|
+
*/
|
|
2469
|
+
resolveId(user) {
|
|
2470
|
+
if (user instanceof User) {
|
|
2471
|
+
return user.id;
|
|
2472
|
+
}
|
|
2473
|
+
if (typeof user === "string") {
|
|
2474
|
+
return user.replace(/[<@>]/g, "");
|
|
2475
|
+
}
|
|
2476
|
+
throw new TypeError("Invalid UserResolvable provided. Expected a User object or a string ID/Mention.");
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Fetches a User.
|
|
2480
|
+
* @param user The ID or mention to fetch
|
|
2481
|
+
* @param force Skip the cache check and force an API request
|
|
2482
|
+
* @returns The fetched User object
|
|
2483
|
+
* @throws Error if the user cannot be found or fetched
|
|
2484
|
+
* @throws TypeError if invalid UserResolvable is provided
|
|
2485
|
+
* @example
|
|
2486
|
+
* // Fetch a user by ID
|
|
2487
|
+
* const user = await client.users.fetch("01JE2MM759J5D7CHJF084R7MJ2");
|
|
2488
|
+
* console.log(user.username);
|
|
2489
|
+
*
|
|
2490
|
+
* // Fetch a user by mention
|
|
2491
|
+
* const user = await client.users.fetch("<@01JE2MM759J5D7CHJF084R7MJ2>");
|
|
2492
|
+
* console.log(user.username);
|
|
2493
|
+
*
|
|
2494
|
+
* // Force fetch a user, bypassing the cache
|
|
2495
|
+
* const user = await client.users.fetch("01JE2MM759J5D7CHJF084R7MJ2", true);
|
|
2496
|
+
* console.log(user.username);
|
|
2497
|
+
*/
|
|
2498
|
+
async fetch(user, force = false) {
|
|
2499
|
+
if (!force) {
|
|
2500
|
+
const cached = this.resolve(user);
|
|
2501
|
+
if (cached) return cached;
|
|
2502
|
+
}
|
|
2503
|
+
const id = this.resolveId(user);
|
|
2504
|
+
const data = await this.client.rest.get(`/users/${id}`);
|
|
2505
|
+
return this._add(data);
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Fetch the current user (the bot itself).
|
|
2509
|
+
* @returns The fetched User object representing the current user.
|
|
2510
|
+
* @throws Error if the user cannot be fetched.
|
|
2511
|
+
* @example
|
|
2512
|
+
* // Fetch the current user (the bot itself)
|
|
2513
|
+
* const me = await client.users.fetchMe();
|
|
2514
|
+
* console.log(`Logged in as ${me.tag}`);
|
|
2515
|
+
*/
|
|
2516
|
+
async fetchMe() {
|
|
2517
|
+
const data = await this.client.rest.get(`/users/@me`);
|
|
2518
|
+
return this._add(data);
|
|
2519
|
+
}
|
|
2520
|
+
/**
|
|
2521
|
+
* Edits the currently authenticated user (the bot itself).
|
|
2522
|
+
* @param options The fields to update (avatar, status, profile, etc.).
|
|
2523
|
+
* @returns A promise that resolves to the updated User object.
|
|
2524
|
+
* @throws {TypeError} If invalid options are provided.
|
|
2525
|
+
* @throws {Error} If the API request fails.
|
|
2526
|
+
* @example
|
|
2527
|
+
* // Update the bot's status and presence
|
|
2528
|
+
* await client.users.editMe({
|
|
2529
|
+
* status: { text: "Watching the server", presence: "Online" }
|
|
2530
|
+
* });
|
|
2531
|
+
*
|
|
2532
|
+
* // Clear the bot's avatar and display name
|
|
2533
|
+
* await client.users.editMe({ avatar: null, displayName: null });
|
|
2534
|
+
*/
|
|
2535
|
+
async editMe(options) {
|
|
2536
|
+
if (!options || typeof options !== "object") {
|
|
2537
|
+
throw new TypeError("UserEditOptions must be a valid object.");
|
|
2538
|
+
}
|
|
2539
|
+
const payload = {};
|
|
2540
|
+
const remove = [];
|
|
2541
|
+
if (options.displayName !== void 0) {
|
|
2542
|
+
if (options.displayName === null) remove.push("DisplayName");
|
|
2543
|
+
else payload.display_name = options.displayName;
|
|
2544
|
+
}
|
|
2545
|
+
if (options.avatar !== void 0) {
|
|
2546
|
+
if (options.avatar === null) remove.push("Avatar");
|
|
2547
|
+
else payload.avatar = options.avatar;
|
|
2548
|
+
}
|
|
2549
|
+
if (options.profile !== void 0) {
|
|
2550
|
+
payload.profile = {};
|
|
2551
|
+
if (options.profile.content !== void 0) {
|
|
2552
|
+
if (options.profile.content === null) remove.push("ProfileContent");
|
|
2553
|
+
else payload.profile.content = options.profile.content;
|
|
2554
|
+
}
|
|
2555
|
+
if (options.profile.background !== void 0) {
|
|
2556
|
+
if (options.profile.background === null) remove.push("ProfileBackground");
|
|
2557
|
+
else payload.profile.background = options.profile.background;
|
|
2558
|
+
}
|
|
2559
|
+
if (Object.keys(payload.profile).length === 0) delete payload.profile;
|
|
2560
|
+
}
|
|
2561
|
+
if (options.status !== void 0) {
|
|
2562
|
+
payload.status = {};
|
|
2563
|
+
if (options.status.text !== void 0) {
|
|
2564
|
+
if (options.status.text === null) remove.push("StatusText");
|
|
2565
|
+
else payload.status.text = options.status.text;
|
|
2566
|
+
}
|
|
2567
|
+
if (options.status.presence !== void 0) {
|
|
2568
|
+
if (options.status.presence === null) remove.push("StatusPresence");
|
|
2569
|
+
else payload.status.presence = options.status.presence;
|
|
2570
|
+
}
|
|
2571
|
+
if (Object.keys(payload.status).length === 0) delete payload.status;
|
|
2572
|
+
}
|
|
2573
|
+
if (remove.length > 0) {
|
|
2574
|
+
payload.remove = remove;
|
|
2575
|
+
}
|
|
2576
|
+
if (Object.keys(payload).length === 0) {
|
|
2577
|
+
return this.fetch("@me");
|
|
2578
|
+
}
|
|
2579
|
+
const data = await this.client.rest.patch(`/users/@me`, payload);
|
|
2580
|
+
return this._add(data);
|
|
2581
|
+
}
|
|
2582
|
+
};
|
|
2583
|
+
|
|
2584
|
+
// src/managers/SweepManager.ts
|
|
2585
|
+
var SweeperManager = class {
|
|
2586
|
+
constructor(client, options) {
|
|
2587
|
+
this.client = client;
|
|
2588
|
+
this.options = options;
|
|
2589
|
+
}
|
|
2590
|
+
messageInterval = null;
|
|
2591
|
+
start() {
|
|
2592
|
+
if (this.options.messages) {
|
|
2593
|
+
this.client.emit("debug", "Starting Message Cache Sweeper...");
|
|
2594
|
+
this.messageInterval = setInterval(() => {
|
|
2595
|
+
this.sweepMessages();
|
|
2596
|
+
}, this.options.messages.interval);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
sweepMessages() {
|
|
2600
|
+
if (!this.options.messages) return;
|
|
2601
|
+
const now = Date.now();
|
|
2602
|
+
const lifetime = this.options.messages.lifetime;
|
|
2603
|
+
let sweptCount = 0;
|
|
2604
|
+
for (const channel of this.client.channels.cache.values()) {
|
|
2605
|
+
if (!("messages" in channel)) continue;
|
|
2606
|
+
const textChannel = channel;
|
|
2607
|
+
for (const [id, message] of textChannel.messages.cache.entries()) {
|
|
2608
|
+
if (now - message.cachedAt > lifetime) {
|
|
2609
|
+
textChannel.messages.cache.delete(id);
|
|
2610
|
+
sweptCount++;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
if (sweptCount > 0) {
|
|
2615
|
+
this.client.emit("debug", `\u{1F9F9} Sweeper cleared ${sweptCount} old messages from cache.`);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
stop() {
|
|
2619
|
+
if (this.messageInterval) clearInterval(this.messageInterval);
|
|
2620
|
+
this.client.emit("debug", "Sweepers stopped.");
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
// src/client/Client.ts
|
|
2625
|
+
var Client = class extends EventEmitter {
|
|
2626
|
+
constructor(options = {}) {
|
|
2627
|
+
super({ captureRejections: true });
|
|
2628
|
+
this.options = options;
|
|
2629
|
+
this.rest = new RESTManager(this);
|
|
2630
|
+
this.gateway = new GatewayManager(this);
|
|
2631
|
+
this.channels = new ChannelManager(this, options.cacheLimits?.channels);
|
|
2632
|
+
this.servers = new ServerManager(this, options.cacheLimits?.servers);
|
|
2633
|
+
this.users = new UserManager(this, options.cacheLimits?.users);
|
|
2634
|
+
this.sweepers = new SweeperManager(this, options.sweepers ?? {});
|
|
2635
|
+
}
|
|
2636
|
+
rest;
|
|
2637
|
+
gateway;
|
|
2638
|
+
channels;
|
|
2639
|
+
servers;
|
|
2640
|
+
users;
|
|
2641
|
+
sweepers;
|
|
2642
|
+
user = null;
|
|
2643
|
+
/**
|
|
2644
|
+
* Connects the bot to the Stoat Gateway
|
|
2645
|
+
*/
|
|
2646
|
+
async login(token) {
|
|
2647
|
+
if (!token) throw new Error("A valid token must be provided.");
|
|
2648
|
+
this.sweepers.start();
|
|
2649
|
+
this.rest.setToken(token);
|
|
2650
|
+
return this.gateway.connect(token);
|
|
2651
|
+
}
|
|
2652
|
+
[/* @__PURE__ */ Symbol.for("nodejs.rejection")](error) {
|
|
2653
|
+
this.emit("error", error);
|
|
2654
|
+
}
|
|
2655
|
+
on(event, listener) {
|
|
2656
|
+
return super.on(event, listener);
|
|
2657
|
+
}
|
|
2658
|
+
once(event, listener) {
|
|
2659
|
+
return super.once(event, listener);
|
|
2660
|
+
}
|
|
2661
|
+
emit(event, ...args) {
|
|
2662
|
+
return super.emit(event, ...args);
|
|
2663
|
+
}
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2666
|
+
// src/builders/EmbedBuilder.ts
|
|
2667
|
+
var EmbedBuilder = class {
|
|
2668
|
+
data;
|
|
2669
|
+
constructor(data = {}) {
|
|
2670
|
+
this.data = { type: "Text", ...data };
|
|
2671
|
+
}
|
|
2672
|
+
setTitle(title) {
|
|
2673
|
+
this.data.title = title;
|
|
2674
|
+
return this;
|
|
2675
|
+
}
|
|
2676
|
+
setDescription(description) {
|
|
2677
|
+
this.data.description = description;
|
|
2678
|
+
return this;
|
|
2679
|
+
}
|
|
2680
|
+
setUrl(url) {
|
|
2681
|
+
this.data.url = url;
|
|
2682
|
+
return this;
|
|
2683
|
+
}
|
|
2684
|
+
setIconUrl(iconUrl) {
|
|
2685
|
+
this.data.icon_url = iconUrl;
|
|
2686
|
+
return this;
|
|
2687
|
+
}
|
|
2688
|
+
setColor(color) {
|
|
2689
|
+
this.data.colour = color;
|
|
2690
|
+
return this;
|
|
2691
|
+
}
|
|
2692
|
+
setMedia(fileId) {
|
|
2693
|
+
this.data.media = fileId;
|
|
2694
|
+
return this;
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* Serializes the builder into the raw JSON required by the API
|
|
2698
|
+
*/
|
|
2699
|
+
toJSON() {
|
|
2700
|
+
return { ...this.data };
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
export {
|
|
2704
|
+
Attachment,
|
|
2705
|
+
Base,
|
|
2706
|
+
BaseChannel,
|
|
2707
|
+
ChannelManager,
|
|
2708
|
+
ChannelType,
|
|
2709
|
+
Client,
|
|
2710
|
+
ClientUser,
|
|
2711
|
+
Collection,
|
|
2712
|
+
DMChannel,
|
|
2713
|
+
EmbedBuilder,
|
|
2714
|
+
GatewayManager,
|
|
2715
|
+
Member,
|
|
2716
|
+
MemberManager,
|
|
2717
|
+
Message,
|
|
2718
|
+
MessageManager,
|
|
2719
|
+
PermissionFlags,
|
|
2720
|
+
Permissions,
|
|
2721
|
+
RESTManager,
|
|
2722
|
+
Role,
|
|
2723
|
+
RoleManager,
|
|
2724
|
+
Server,
|
|
2725
|
+
ServerChannelManager,
|
|
2726
|
+
ServerManager,
|
|
2727
|
+
SweeperManager,
|
|
2728
|
+
TextChannel,
|
|
2729
|
+
UnknownChannel,
|
|
2730
|
+
User,
|
|
2731
|
+
UserManager,
|
|
2732
|
+
UserPresence,
|
|
2733
|
+
UserRelationship
|
|
2734
|
+
};
|