@fluxerjs/core 1.0.9 → 1.1.1

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.
Files changed (36) hide show
  1. package/LICENSE +203 -0
  2. package/dist/{Channel-ICWNKXBR.mjs → Channel-4WVFDOCG.mjs} +4 -1
  3. package/dist/{ClientUser-WWXUMO5O.mjs → ClientUser-PXAAKR2P.mjs} +1 -1
  4. package/dist/Guild-FMBCTAV4.mjs +12 -0
  5. package/dist/{GuildBan-M4PA3HAA.mjs → GuildBan-7CXLTPKY.mjs} +1 -1
  6. package/dist/GuildMember-43B5E5CH.mjs +9 -0
  7. package/dist/Message-OFIVTTAZ.mjs +9 -0
  8. package/dist/{MessageReaction-XRPYZDSC.mjs → MessageReaction-AYSOCOMX.mjs} +2 -1
  9. package/dist/{Role-SVLWIAMN.mjs → Role-5MWSGL66.mjs} +1 -1
  10. package/dist/Webhook-RWDDYW2Q.mjs +10 -0
  11. package/dist/{chunk-GCIJYVRC.mjs → chunk-4F765HVV.mjs} +54 -3
  12. package/dist/chunk-AH7KYH2Z.mjs +50 -0
  13. package/dist/{chunk-53Y37KRG.mjs → chunk-CJVQNARM.mjs} +44 -10
  14. package/dist/chunk-DQ4TNBPG.mjs +63 -0
  15. package/dist/chunk-DUQAD7F6.mjs +173 -0
  16. package/dist/chunk-FRVZ7D6D.mjs +293 -0
  17. package/dist/{chunk-HBF5QEDH.mjs → chunk-K6NLD6SB.mjs} +23 -1
  18. package/dist/chunk-PM2IUGNR.mjs +29 -0
  19. package/dist/chunk-RWFKZ3DF.mjs +413 -0
  20. package/dist/{chunk-RCP27MRC.mjs → chunk-UXIF75BV.mjs} +3 -0
  21. package/dist/chunk-V6T5VMWD.mjs +26 -0
  22. package/dist/{chunk-DSPSRPHF.mjs → chunk-V7LPVPGH.mjs} +123 -18
  23. package/dist/chunk-X6K3ZD62.mjs +53 -0
  24. package/dist/index.d.mts +603 -113
  25. package/dist/index.d.ts +603 -113
  26. package/dist/index.js +1335 -426
  27. package/dist/index.mjs +189 -125
  28. package/package.json +8 -8
  29. package/dist/Guild-TM6YGJWB.mjs +0 -10
  30. package/dist/GuildMember-RZWZ3OCG.mjs +0 -7
  31. package/dist/Message-6IYEYSV6.mjs +0 -7
  32. package/dist/Webhook-32VJD4AL.mjs +0 -7
  33. package/dist/chunk-FJS5FBXO.mjs +0 -233
  34. package/dist/chunk-GFUJVQ7L.mjs +0 -64
  35. package/dist/chunk-SQVCCSNN.mjs +0 -41
  36. package/dist/chunk-X77DFNE3.mjs +0 -136
package/dist/index.js CHANGED
@@ -20,28 +20,72 @@ var __copyProps = (to, from, except, desc) => {
20
20
  };
21
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
22
 
23
- // src/client/MessageManager.ts
24
- var MessageManager;
25
- var init_MessageManager = __esm({
26
- "src/client/MessageManager.ts"() {
23
+ // src/errors/FluxerError.ts
24
+ var FluxerError;
25
+ var init_FluxerError = __esm({
26
+ "src/errors/FluxerError.ts"() {
27
27
  "use strict";
28
- MessageManager = class {
29
- constructor(client, channelId) {
30
- this.client = client;
31
- this.channelId = channelId;
32
- }
33
- /**
34
- * Fetch a message by ID from this channel.
35
- * @param messageId - Snowflake of the message
36
- * @returns The message, or null if not found
37
- */
38
- async fetch(messageId) {
39
- return this.client.channels.fetchMessage(this.channelId, messageId);
28
+ FluxerError = class _FluxerError extends Error {
29
+ code;
30
+ constructor(message, options) {
31
+ super(message, options?.cause ? { cause: options.cause } : void 0);
32
+ this.name = "FluxerError";
33
+ this.code = options?.code;
34
+ Object.setPrototypeOf(this, _FluxerError.prototype);
40
35
  }
41
36
  };
42
37
  }
43
38
  });
44
39
 
40
+ // src/errors/ErrorCodes.ts
41
+ var ErrorCodes;
42
+ var init_ErrorCodes = __esm({
43
+ "src/errors/ErrorCodes.ts"() {
44
+ "use strict";
45
+ ErrorCodes = {
46
+ ClientNotReady: "CLIENT_NOT_READY",
47
+ InvalidToken: "INVALID_TOKEN",
48
+ AlreadyLoggedIn: "ALREADY_LOGGED_IN",
49
+ ChannelNotFound: "CHANNEL_NOT_FOUND",
50
+ MessageNotFound: "MESSAGE_NOT_FOUND",
51
+ GuildNotFound: "GUILD_NOT_FOUND",
52
+ MemberNotFound: "MEMBER_NOT_FOUND"
53
+ };
54
+ }
55
+ });
56
+
57
+ // src/util/messageUtils.ts
58
+ function buildSendBody(options) {
59
+ const body = typeof options === "string" ? { content: options } : options;
60
+ const result = {};
61
+ if (body.content !== void 0) result.content = body.content;
62
+ if (body.embeds?.length) {
63
+ result.embeds = body.embeds.map((e) => e instanceof import_builders.EmbedBuilder ? e.toJSON() : e);
64
+ }
65
+ if (body.files?.length && body.attachments) {
66
+ result.attachments = body.attachments.map((a) => ({
67
+ id: a.id,
68
+ filename: a.filename,
69
+ ...a.title != null && { title: a.title },
70
+ ...a.description != null && { description: a.description },
71
+ ...a.flags != null && { flags: a.flags }
72
+ }));
73
+ } else if (body.files?.length) {
74
+ result.attachments = body.files.map((f, i) => ({
75
+ id: i,
76
+ filename: f.filename ?? f.name
77
+ }));
78
+ }
79
+ return result;
80
+ }
81
+ var import_builders;
82
+ var init_messageUtils = __esm({
83
+ "src/util/messageUtils.ts"() {
84
+ "use strict";
85
+ import_builders = require("@fluxerjs/builders");
86
+ }
87
+ });
88
+
45
89
  // src/structures/Base.ts
46
90
  var Base;
47
91
  var init_Base = __esm({
@@ -52,116 +96,116 @@ var init_Base = __esm({
52
96
  }
53
97
  });
54
98
 
55
- // src/structures/Webhook.ts
56
- var Webhook_exports = {};
57
- __export(Webhook_exports, {
58
- Webhook: () => Webhook
99
+ // src/util/Events.ts
100
+ var Events;
101
+ var init_Events = __esm({
102
+ "src/util/Events.ts"() {
103
+ "use strict";
104
+ Events = {
105
+ Ready: "ready",
106
+ MessageCreate: "messageCreate",
107
+ MessageUpdate: "messageUpdate",
108
+ MessageDelete: "messageDelete",
109
+ MessageDeleteBulk: "messageDeleteBulk",
110
+ MessageReactionAdd: "messageReactionAdd",
111
+ MessageReactionRemove: "messageReactionRemove",
112
+ MessageReactionRemoveAll: "messageReactionRemoveAll",
113
+ MessageReactionRemoveEmoji: "messageReactionRemoveEmoji",
114
+ InteractionCreate: "interactionCreate",
115
+ GuildCreate: "guildCreate",
116
+ GuildUpdate: "guildUpdate",
117
+ GuildDelete: "guildDelete",
118
+ GuildBanAdd: "guildBanAdd",
119
+ GuildBanRemove: "guildBanRemove",
120
+ GuildEmojisUpdate: "guildEmojisUpdate",
121
+ GuildStickersUpdate: "guildStickersUpdate",
122
+ GuildIntegrationsUpdate: "guildIntegrationsUpdate",
123
+ GuildMemberAdd: "guildMemberAdd",
124
+ GuildMemberUpdate: "guildMemberUpdate",
125
+ GuildMemberRemove: "guildMemberRemove",
126
+ GuildRoleCreate: "guildRoleCreate",
127
+ GuildRoleUpdate: "guildRoleUpdate",
128
+ GuildRoleDelete: "guildRoleDelete",
129
+ GuildScheduledEventCreate: "guildScheduledEventCreate",
130
+ GuildScheduledEventUpdate: "guildScheduledEventUpdate",
131
+ GuildScheduledEventDelete: "guildScheduledEventDelete",
132
+ ChannelCreate: "channelCreate",
133
+ ChannelUpdate: "channelUpdate",
134
+ ChannelDelete: "channelDelete",
135
+ ChannelPinsUpdate: "channelPinsUpdate",
136
+ InviteCreate: "inviteCreate",
137
+ InviteDelete: "inviteDelete",
138
+ TypingStart: "typingStart",
139
+ UserUpdate: "userUpdate",
140
+ PresenceUpdate: "presenceUpdate",
141
+ VoiceStateUpdate: "voiceStateUpdate",
142
+ VoiceServerUpdate: "voiceServerUpdate",
143
+ VoiceStatesSync: "voiceStatesSync",
144
+ WebhooksUpdate: "webhooksUpdate",
145
+ Resumed: "resumed",
146
+ Error: "error",
147
+ Debug: "debug"
148
+ };
149
+ }
59
150
  });
60
- var import_types, Webhook;
61
- var init_Webhook = __esm({
62
- "src/structures/Webhook.ts"() {
151
+
152
+ // src/util/ReactionCollector.ts
153
+ var import_events, import_collection, ReactionCollector;
154
+ var init_ReactionCollector = __esm({
155
+ "src/util/ReactionCollector.ts"() {
63
156
  "use strict";
64
- init_Base();
65
- import_types = require("@fluxerjs/types");
66
- Webhook = class _Webhook extends Base {
157
+ import_events = require("events");
158
+ import_collection = require("@fluxerjs/collection");
159
+ init_Events();
160
+ ReactionCollector = class extends import_events.EventEmitter {
67
161
  client;
68
- id;
69
- guildId;
162
+ messageId;
70
163
  channelId;
71
- name;
72
- avatar;
73
- /** Present only when webhook was created via createWebhook(); not returned when fetching. */
74
- token;
75
- /** @param data - API webhook from POST /channels/{id}/webhooks (has token) or GET /webhooks/{id} (no token) */
76
- constructor(client, data) {
164
+ options;
165
+ collected = new import_collection.Collection();
166
+ _timeout = null;
167
+ _ended = false;
168
+ _listener;
169
+ constructor(client, messageId, channelId, options = {}) {
77
170
  super();
78
171
  this.client = client;
79
- this.id = data.id;
80
- this.guildId = data.guild_id;
81
- this.channelId = data.channel_id;
82
- this.name = data.name ?? "Unknown";
83
- this.avatar = data.avatar ?? null;
84
- this.token = data.token ?? null;
85
- }
86
- /** Delete this webhook. Requires bot token with Manage Webhooks permission. */
87
- async delete() {
88
- await this.client.rest.delete(import_types.Routes.webhook(this.id), { auth: true });
89
- }
90
- /**
91
- * Edit this webhook. With token: name and avatar only. Without token (bot auth): name, avatar, and channel_id.
92
- * @param options - Fields to update (name, avatar, channel_id when using bot auth)
93
- * @returns This webhook instance with updated fields
94
- */
95
- async edit(options) {
96
- const body = {};
97
- if (options.name !== void 0) body.name = options.name;
98
- if (options.avatar !== void 0) body.avatar = options.avatar;
99
- if ("channel_id" in options && options.channel_id !== void 0 && !this.token) {
100
- body.channel_id = options.channel_id;
101
- }
102
- if (this.token) {
103
- const data2 = await this.client.rest.patch(
104
- import_types.Routes.webhookExecute(this.id, this.token),
105
- { body, auth: false }
106
- );
107
- const w2 = data2;
108
- this.name = w2.name ?? this.name;
109
- this.avatar = w2.avatar ?? null;
110
- return this;
172
+ this.messageId = messageId;
173
+ this.channelId = channelId;
174
+ this.options = {
175
+ filter: options.filter ?? (() => true),
176
+ time: options.time ?? 0,
177
+ max: options.max ?? 0
178
+ };
179
+ this._listener = (reaction, user, _msgId, chId, _emoji, userId) => {
180
+ if (this._ended || reaction.messageId !== this.messageId || chId !== this.channelId) return;
181
+ if (!this.options.filter(reaction, user)) return;
182
+ const key = `${userId}:${reaction.emoji.id ?? reaction.emoji.name}`;
183
+ this.collected.set(key, { reaction, user });
184
+ this.emit("collect", reaction, user);
185
+ if (this.options.max > 0 && this.collected.size >= this.options.max) {
186
+ this.stop("limit");
187
+ }
188
+ };
189
+ this.client.on(Events.MessageReactionAdd, this._listener);
190
+ if (this.options.time > 0) {
191
+ this._timeout = setTimeout(() => this.stop("time"), this.options.time);
111
192
  }
112
- const data = await this.client.rest.patch(import_types.Routes.webhook(this.id), {
113
- body,
114
- auth: true
115
- });
116
- const w = data;
117
- this.name = w.name ?? this.name;
118
- this.avatar = w.avatar ?? null;
119
- this.channelId = w.channel_id ?? this.channelId;
120
- return this;
121
193
  }
122
- /**
123
- * Send a message via this webhook. Requires the webhook token (only present when created, not when fetched).
124
- * @throws Error if token is not available
125
- */
126
- async send(options) {
127
- if (!this.token) {
128
- throw new Error(
129
- "Webhook token is required to send. The token is only returned when creating a webhook; fetched webhooks cannot send."
130
- );
194
+ stop(reason = "user") {
195
+ if (this._ended) return;
196
+ this._ended = true;
197
+ this.client.off(Events.MessageReactionAdd, this._listener);
198
+ if (this._timeout) {
199
+ clearTimeout(this._timeout);
200
+ this._timeout = null;
131
201
  }
132
- const body = typeof options === "string" ? { content: options } : options;
133
- await this.client.rest.post(import_types.Routes.webhookExecute(this.id, this.token), {
134
- body,
135
- auth: false
136
- });
202
+ this.emit("end", this.collected, reason);
137
203
  }
138
- /**
139
- * Fetch a webhook by ID using bot auth.
140
- * @param client - The client instance
141
- * @param webhookId - The webhook ID
142
- * @returns Webhook without token (cannot send)
143
- */
144
- static async fetch(client, webhookId) {
145
- const data = await client.rest.get(import_types.Routes.webhook(webhookId));
146
- return new _Webhook(client, data);
204
+ on(event, listener) {
205
+ return super.on(event, listener);
147
206
  }
148
- /**
149
- * Create a Webhook instance from an ID and token (e.g. from a stored webhook URL).
150
- * @param client - The client instance
151
- * @param webhookId - The webhook ID
152
- * @param token - The webhook token (from createWebhook or stored)
153
- * @param options - Optional channelId, guildId, name for display
154
- */
155
- static fromToken(client, webhookId, token, options) {
156
- return new _Webhook(client, {
157
- id: webhookId,
158
- guild_id: options?.guildId ?? "",
159
- channel_id: options?.channelId ?? "",
160
- name: options?.name ?? "Webhook",
161
- avatar: null,
162
- token,
163
- user: { id: "", username: "webhook", discriminator: "0" }
164
- });
207
+ emit(event, ...args) {
208
+ return super.emit(event, ...args);
165
209
  }
166
210
  };
167
211
  }
@@ -172,14 +216,16 @@ var Message_exports = {};
172
216
  __export(Message_exports, {
173
217
  Message: () => Message
174
218
  });
175
- var import_collection, import_types2, import_builders, Message;
219
+ var import_collection2, import_types, import_builders2, Message;
176
220
  var init_Message = __esm({
177
221
  "src/structures/Message.ts"() {
178
222
  "use strict";
179
223
  init_Base();
180
- import_collection = require("@fluxerjs/collection");
181
- import_types2 = require("@fluxerjs/types");
182
- import_builders = require("@fluxerjs/builders");
224
+ import_collection2 = require("@fluxerjs/collection");
225
+ import_types = require("@fluxerjs/types");
226
+ import_builders2 = require("@fluxerjs/builders");
227
+ init_messageUtils();
228
+ init_ReactionCollector();
183
229
  Message = class _Message extends Base {
184
230
  client;
185
231
  id;
@@ -202,6 +248,14 @@ var init_Message = __esm({
202
248
  messageSnapshots;
203
249
  call;
204
250
  referencedMessage;
251
+ /** Webhook ID if this message was sent via webhook. Null otherwise. */
252
+ webhookId;
253
+ /** Users mentioned in this message. */
254
+ mentions;
255
+ /** Role IDs mentioned in this message. */
256
+ mentionRoles;
257
+ /** Client-side nonce for acknowledgment. Null if not provided. */
258
+ nonce;
205
259
  /** Channel where this message was sent. Resolved from cache; null if not cached (e.g. DM channel not in cache). */
206
260
  get channel() {
207
261
  return this.client.channels.get(this.channelId) ?? null;
@@ -222,9 +276,9 @@ var init_Message = __esm({
222
276
  this.createdAt = new Date(data.timestamp);
223
277
  this.editedAt = data.edited_timestamp ? new Date(data.edited_timestamp) : null;
224
278
  this.pinned = data.pinned;
225
- this.attachments = new import_collection.Collection();
279
+ this.attachments = new import_collection2.Collection();
226
280
  for (const a of data.attachments ?? []) this.attachments.set(a.id, a);
227
- this.type = data.type ?? import_types2.MessageType.Default;
281
+ this.type = data.type ?? import_types.MessageType.Default;
228
282
  this.flags = data.flags ?? 0;
229
283
  this.mentionEveryone = data.mention_everyone ?? false;
230
284
  this.tts = data.tts ?? false;
@@ -235,17 +289,24 @@ var init_Message = __esm({
235
289
  this.messageSnapshots = data.message_snapshots ?? [];
236
290
  this.call = data.call ?? null;
237
291
  this.referencedMessage = data.referenced_message ? new _Message(client, data.referenced_message) : null;
292
+ this.webhookId = data.webhook_id ?? null;
293
+ this.mentions = (data.mentions ?? []).map((u) => client.getOrCreateUser(u));
294
+ this.mentionRoles = data.mention_roles ?? [];
295
+ this.nonce = data.nonce ?? null;
238
296
  }
239
297
  /**
240
298
  * Send a message to this channel without replying. Use when you want a standalone message.
241
- * @param options - Text content or object with content and/or embeds
299
+ * @param options - Text content or object with content, embeds, and/or files
242
300
  * @example
243
301
  * await message.send('Pong!');
244
302
  * await message.send({ embeds: [embed.toJSON()] });
303
+ * await message.send({ content: 'File', files: [{ name: 'data.txt', data }] });
245
304
  */
246
305
  async send(options) {
247
- const body = typeof options === "string" ? { content: options } : options;
248
- const data = await this.client.rest.post(import_types2.Routes.channelMessages(this.channelId), { body });
306
+ const opts = typeof options === "string" ? { content: options } : options;
307
+ const body = buildSendBody(options);
308
+ const postOptions = opts.files?.length ? { body, files: opts.files } : { body };
309
+ const data = await this.client.rest.post(import_types.Routes.channelMessages(this.channelId), postOptions);
249
310
  return new _Message(this.client, data);
250
311
  }
251
312
  /**
@@ -261,25 +322,21 @@ var init_Message = __esm({
261
322
  }
262
323
  /**
263
324
  * Reply to this message.
264
- * @param options - Text content or object with content and/or embeds
325
+ * @param options - Text content or object with content, embeds, and/or files
265
326
  */
266
327
  async reply(options) {
267
- const body = typeof options === "string" ? {
268
- content: options,
269
- message_reference: {
270
- channel_id: this.channelId,
271
- message_id: this.id,
272
- guild_id: this.guildId ?? void 0
273
- }
274
- } : {
275
- ...options,
328
+ const opts = typeof options === "string" ? { content: options } : options;
329
+ const base = buildSendBody(options);
330
+ const body = {
331
+ ...base,
276
332
  message_reference: {
277
333
  channel_id: this.channelId,
278
334
  message_id: this.id,
279
335
  guild_id: this.guildId ?? void 0
280
336
  }
281
337
  };
282
- const data = await this.client.rest.post(import_types2.Routes.channelMessages(this.channelId), { body });
338
+ const postOptions = opts.files?.length ? { body, files: opts.files } : { body };
339
+ const data = await this.client.rest.post(import_types.Routes.channelMessages(this.channelId), postOptions);
283
340
  return new _Message(this.client, data);
284
341
  }
285
342
  /**
@@ -290,36 +347,49 @@ var init_Message = __esm({
290
347
  const body = {};
291
348
  if (options.content !== void 0) body.content = options.content;
292
349
  if (options.embeds?.length) {
293
- body.embeds = options.embeds.map((e) => e instanceof import_builders.EmbedBuilder ? e.toJSON() : e);
350
+ body.embeds = options.embeds.map((e) => e instanceof import_builders2.EmbedBuilder ? e.toJSON() : e);
294
351
  }
295
- const data = await this.client.rest.patch(import_types2.Routes.channelMessage(this.channelId, this.id), {
352
+ const data = await this.client.rest.patch(import_types.Routes.channelMessage(this.channelId, this.id), {
296
353
  body
297
354
  });
298
355
  return new _Message(this.client, data);
299
356
  }
357
+ /**
358
+ * Create a reaction collector for this message.
359
+ * Collects reactions matching the filter until time expires or max is reached.
360
+ * @param options - Filter, time (ms), and max count
361
+ * @example
362
+ * const collector = message.createReactionCollector({ filter: (r, u) => u.id === userId, time: 10000 });
363
+ * collector.on('collect', (reaction, user) => console.log(user.username, 'reacted with', reaction.emoji.name));
364
+ * collector.on('end', (collected, reason) => { ... });
365
+ */
366
+ createReactionCollector(options) {
367
+ return new ReactionCollector(this.client, this.id, this.channelId, options);
368
+ }
300
369
  /**
301
370
  * Re-fetch this message from the API to get the latest content, embeds, reactions, etc.
302
371
  * Use when you have a stale Message (e.g. from an old event or cache) and need fresh data.
303
- * @returns The updated message, or null if deleted or not found
372
+ * @returns The updated message
373
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message was deleted or does not exist
304
374
  * @example
305
375
  * const updated = await message.fetch();
306
- * if (updated) console.log('Latest content:', updated.content);
376
+ * console.log('Latest content:', updated.content);
307
377
  */
308
378
  async fetch() {
309
379
  return this.client.channels.fetchMessage(this.channelId, this.id);
310
380
  }
311
381
  /** Delete this message. */
312
382
  async delete() {
313
- await this.client.rest.delete(import_types2.Routes.channelMessage(this.channelId, this.id));
383
+ await this.client.rest.delete(import_types.Routes.channelMessage(this.channelId, this.id));
314
384
  }
315
385
  /** Pin this message to the channel. Requires Manage Messages permission. */
316
386
  async pin() {
317
- await this.client.rest.put(import_types2.Routes.channelPinMessage(this.channelId, this.id));
387
+ await this.client.rest.put(import_types.Routes.channelPinMessage(this.channelId, this.id));
318
388
  this.pinned = true;
319
389
  }
320
390
  /** Unpin this message from the channel. Requires Manage Messages permission. */
321
391
  async unpin() {
322
- await this.client.rest.delete(import_types2.Routes.channelPinMessage(this.channelId, this.id));
392
+ await this.client.rest.delete(import_types.Routes.channelPinMessage(this.channelId, this.id));
323
393
  this.pinned = false;
324
394
  }
325
395
  /**
@@ -339,7 +409,7 @@ var init_Message = __esm({
339
409
  */
340
410
  async react(emoji) {
341
411
  const emojiStr = await this.resolveEmojiForReaction(emoji);
342
- const route = `${import_types2.Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/@me`;
412
+ const route = `${import_types.Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/@me`;
343
413
  await this.client.rest.put(route);
344
414
  }
345
415
  /**
@@ -349,7 +419,7 @@ var init_Message = __esm({
349
419
  */
350
420
  async removeReaction(emoji, userId) {
351
421
  const emojiStr = await this.resolveEmojiForReaction(emoji);
352
- const route = `${import_types2.Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/${userId ?? "@me"}`;
422
+ const route = `${import_types.Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/${userId ?? "@me"}`;
353
423
  await this.client.rest.delete(route);
354
424
  }
355
425
  /**
@@ -357,7 +427,7 @@ var init_Message = __esm({
357
427
  * Requires moderator permissions.
358
428
  */
359
429
  async removeAllReactions() {
360
- await this.client.rest.delete(import_types2.Routes.channelMessageReactions(this.channelId, this.id));
430
+ await this.client.rest.delete(import_types.Routes.channelMessageReactions(this.channelId, this.id));
361
431
  }
362
432
  /**
363
433
  * Remove all reactions of a specific emoji from this message.
@@ -365,7 +435,392 @@ var init_Message = __esm({
365
435
  */
366
436
  async removeReactionEmoji(emoji) {
367
437
  const emojiStr = await this.resolveEmojiForReaction(emoji);
368
- await this.client.rest.delete(import_types2.Routes.channelMessageReaction(this.channelId, this.id, emojiStr));
438
+ await this.client.rest.delete(import_types.Routes.channelMessageReaction(this.channelId, this.id, emojiStr));
439
+ }
440
+ /**
441
+ * Fetch users who reacted with the given emoji.
442
+ * @param emoji - Unicode emoji or custom `{ name, id }`
443
+ * @param options - limit (1–100), after (user ID for pagination)
444
+ * @returns Array of User objects
445
+ */
446
+ async fetchReactionUsers(emoji, options) {
447
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
448
+ const params = new URLSearchParams();
449
+ if (options?.limit != null) params.set("limit", String(options.limit));
450
+ if (options?.after) params.set("after", options.after);
451
+ const qs = params.toString();
452
+ const route = import_types.Routes.channelMessageReaction(this.channelId, this.id, emojiStr) + (qs ? `?${qs}` : "");
453
+ const data = await this.client.rest.get(route);
454
+ const list = Array.isArray(data) ? data : data?.users ?? [];
455
+ return list.map((u) => this.client.getOrCreateUser(u));
456
+ }
457
+ };
458
+ }
459
+ });
460
+
461
+ // src/client/MessageManager.ts
462
+ var import_types2, import_rest, MessageManager;
463
+ var init_MessageManager = __esm({
464
+ "src/client/MessageManager.ts"() {
465
+ "use strict";
466
+ import_types2 = require("@fluxerjs/types");
467
+ import_rest = require("@fluxerjs/rest");
468
+ init_FluxerError();
469
+ init_ErrorCodes();
470
+ MessageManager = class {
471
+ constructor(client, channelId) {
472
+ this.client = client;
473
+ this.channelId = channelId;
474
+ }
475
+ /**
476
+ * Fetch a message by ID from this channel.
477
+ * @param messageId - Snowflake of the message
478
+ * @returns The message
479
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
480
+ */
481
+ async fetch(messageId) {
482
+ try {
483
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
484
+ const data = await this.client.rest.get(
485
+ import_types2.Routes.channelMessage(this.channelId, messageId)
486
+ );
487
+ return new Message2(this.client, data);
488
+ } catch (err) {
489
+ if (err instanceof import_rest.RateLimitError) throw err;
490
+ if (err instanceof import_rest.FluxerAPIError && err.statusCode === 404) {
491
+ throw new FluxerError(`Message ${messageId} not found in channel ${this.channelId}`, {
492
+ code: ErrorCodes.MessageNotFound,
493
+ cause: err
494
+ });
495
+ }
496
+ throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
497
+ }
498
+ }
499
+ };
500
+ }
501
+ });
502
+
503
+ // src/util/MessageCollector.ts
504
+ var import_events2, import_collection3, MessageCollector;
505
+ var init_MessageCollector = __esm({
506
+ "src/util/MessageCollector.ts"() {
507
+ "use strict";
508
+ import_events2 = require("events");
509
+ import_collection3 = require("@fluxerjs/collection");
510
+ init_Events();
511
+ MessageCollector = class extends import_events2.EventEmitter {
512
+ client;
513
+ channelId;
514
+ options;
515
+ collected = new import_collection3.Collection();
516
+ _timeout = null;
517
+ _ended = false;
518
+ _listener;
519
+ constructor(client, channelId, options = {}) {
520
+ super();
521
+ this.client = client;
522
+ this.channelId = channelId;
523
+ this.options = {
524
+ filter: options.filter ?? (() => true),
525
+ time: options.time ?? 0,
526
+ max: options.max ?? 0
527
+ };
528
+ this._listener = (message) => {
529
+ if (this._ended || message.channelId !== this.channelId) return;
530
+ if (!this.options.filter(message)) return;
531
+ this.collected.set(message.id, message);
532
+ this.emit("collect", message);
533
+ if (this.options.max > 0 && this.collected.size >= this.options.max) {
534
+ this.stop("limit");
535
+ }
536
+ };
537
+ this.client.on(Events.MessageCreate, this._listener);
538
+ if (this.options.time > 0) {
539
+ this._timeout = setTimeout(() => this.stop("time"), this.options.time);
540
+ }
541
+ }
542
+ stop(reason = "user") {
543
+ if (this._ended) return;
544
+ this._ended = true;
545
+ this.client.off(Events.MessageCreate, this._listener);
546
+ if (this._timeout) {
547
+ clearTimeout(this._timeout);
548
+ this._timeout = null;
549
+ }
550
+ this.emit("end", this.collected, reason);
551
+ }
552
+ on(event, listener) {
553
+ return super.on(event, listener);
554
+ }
555
+ emit(event, ...args) {
556
+ return super.emit(event, ...args);
557
+ }
558
+ };
559
+ }
560
+ });
561
+
562
+ // src/util/Constants.ts
563
+ var CDN_URL;
564
+ var init_Constants = __esm({
565
+ "src/util/Constants.ts"() {
566
+ "use strict";
567
+ CDN_URL = "https://fluxerusercontent.com";
568
+ }
569
+ });
570
+
571
+ // src/util/cdn.ts
572
+ function getExtension(hash, options) {
573
+ const ext = options?.extension ?? "png";
574
+ if (hash?.startsWith("a_")) return "gif";
575
+ return ext;
576
+ }
577
+ function appendSize(options) {
578
+ return options?.size ? `?size=${options.size}` : "";
579
+ }
580
+ function cdnAvatarURL(userId, avatarHash, options) {
581
+ if (!avatarHash) return null;
582
+ const ext = getExtension(avatarHash, options);
583
+ const size = appendSize(options);
584
+ return `${CDN_URL}/avatars/${userId}/${avatarHash}.${ext}${size}`;
585
+ }
586
+ function cdnDisplayAvatarURL(userId, avatarHash, options) {
587
+ return cdnAvatarURL(userId, avatarHash, options) ?? `${CDN_URL}/avatars/0/0.png`;
588
+ }
589
+ function cdnBannerURL(resourceId, bannerHash, options) {
590
+ if (!bannerHash) return null;
591
+ const ext = getExtension(bannerHash, options);
592
+ const size = appendSize(options);
593
+ return `${CDN_URL}/banners/${resourceId}/${bannerHash}.${ext}${size}`;
594
+ }
595
+ function cdnMemberAvatarURL(guildId, userId, avatarHash, options) {
596
+ if (!avatarHash) return null;
597
+ const ext = getExtension(avatarHash, options);
598
+ const size = appendSize(options);
599
+ return `${CDN_URL}/guilds/${guildId}/users/${userId}/avatars/${avatarHash}.${ext}${size}`;
600
+ }
601
+ function cdnMemberBannerURL(guildId, userId, bannerHash, options) {
602
+ if (!bannerHash) return null;
603
+ const ext = getExtension(bannerHash, options);
604
+ const size = appendSize(options);
605
+ return `${CDN_URL}/guilds/${guildId}/users/${userId}/banners/${bannerHash}.${ext}${size}`;
606
+ }
607
+ function cdnDefaultAvatarURL(discriminatorIndex) {
608
+ const index = discriminatorIndex != null ? discriminatorIndex % 5 : 0;
609
+ return `${CDN_URL}/avatars/0/${index}.png`;
610
+ }
611
+ var init_cdn = __esm({
612
+ "src/util/cdn.ts"() {
613
+ "use strict";
614
+ init_Constants();
615
+ }
616
+ });
617
+
618
+ // src/structures/Webhook.ts
619
+ var Webhook_exports = {};
620
+ __export(Webhook_exports, {
621
+ Webhook: () => Webhook
622
+ });
623
+ var import_types3, Webhook;
624
+ var init_Webhook = __esm({
625
+ "src/structures/Webhook.ts"() {
626
+ "use strict";
627
+ init_Base();
628
+ import_types3 = require("@fluxerjs/types");
629
+ init_messageUtils();
630
+ init_cdn();
631
+ Webhook = class _Webhook extends Base {
632
+ client;
633
+ id;
634
+ guildId;
635
+ channelId;
636
+ name;
637
+ avatar;
638
+ /** Present only when webhook was created via createWebhook(); not returned when fetching. */
639
+ token;
640
+ /** User who created the webhook. */
641
+ user;
642
+ /** @param data - API webhook from POST /channels/{id}/webhooks (has token) or GET /webhooks/{id} (no token) */
643
+ constructor(client, data) {
644
+ super();
645
+ this.client = client;
646
+ this.id = data.id;
647
+ this.guildId = data.guild_id;
648
+ this.channelId = data.channel_id;
649
+ this.name = data.name ?? "Unknown";
650
+ this.avatar = data.avatar ?? null;
651
+ this.token = data.token ?? null;
652
+ this.user = client.getOrCreateUser(data.user);
653
+ }
654
+ /**
655
+ * Get the URL for this webhook's avatar.
656
+ * Returns null if the webhook has no custom avatar.
657
+ */
658
+ avatarURL(options) {
659
+ return cdnAvatarURL(this.id, this.avatar, options);
660
+ }
661
+ /** Delete this webhook. Requires bot token with Manage Webhooks permission. */
662
+ async delete() {
663
+ await this.client.rest.delete(import_types3.Routes.webhook(this.id), { auth: true });
664
+ }
665
+ /**
666
+ * Edit this webhook. With token: name and avatar only. Without token (bot auth): name, avatar, and channel_id.
667
+ * @param options - Fields to update (name, avatar, channel_id when using bot auth)
668
+ * @returns This webhook instance with updated fields
669
+ */
670
+ async edit(options) {
671
+ const body = {};
672
+ if (options.name !== void 0) body.name = options.name;
673
+ if (options.avatar !== void 0) body.avatar = options.avatar;
674
+ if ("channel_id" in options && options.channel_id !== void 0 && !this.token) {
675
+ body.channel_id = options.channel_id;
676
+ }
677
+ if (this.token) {
678
+ const data2 = await this.client.rest.patch(import_types3.Routes.webhookExecute(this.id, this.token), {
679
+ body,
680
+ auth: false
681
+ });
682
+ const w2 = data2;
683
+ this.name = w2.name ?? this.name;
684
+ this.avatar = w2.avatar ?? null;
685
+ return this;
686
+ }
687
+ const data = await this.client.rest.patch(import_types3.Routes.webhook(this.id), {
688
+ body,
689
+ auth: true
690
+ });
691
+ const w = data;
692
+ this.name = w.name ?? this.name;
693
+ this.avatar = w.avatar ?? null;
694
+ this.channelId = w.channel_id ?? this.channelId;
695
+ return this;
696
+ }
697
+ /**
698
+ * Send a message via this webhook. Requires the webhook token (only present when created, not when fetched).
699
+ * @param options - Text content or object with content, embeds, username, avatar_url, tts, files, attachments
700
+ * @param wait - If true, waits for the API and returns the created Message; otherwise returns void (204)
701
+ * @throws Error if token is not available
702
+ * @example
703
+ * await webhook.send('Hello!');
704
+ * await webhook.send({ embeds: [embed.toJSON()] });
705
+ * await webhook.send({ content: 'File attached', files: [{ name: 'data.txt', data: buffer }] });
706
+ * const msg = await webhook.send({ content: 'Hi' }, true);
707
+ */
708
+ async send(options, wait) {
709
+ if (!this.token) {
710
+ throw new Error(
711
+ "Webhook token is required to send. The token is only returned when creating a webhook; fetched webhooks cannot send."
712
+ );
713
+ }
714
+ const opts = typeof options === "string" ? { content: options } : options;
715
+ const body = buildSendBody(options);
716
+ if (opts.username !== void 0) body.username = opts.username;
717
+ if (opts.avatar_url !== void 0) body.avatar_url = opts.avatar_url;
718
+ if (opts.tts !== void 0) body.tts = opts.tts;
719
+ const route = import_types3.Routes.webhookExecute(this.id, this.token) + (wait ? "?wait=true" : "");
720
+ const postOptions = opts.files?.length ? { body, files: opts.files, auth: false } : { body, auth: false };
721
+ const data = await this.client.rest.post(
722
+ route,
723
+ postOptions
724
+ );
725
+ if (wait && data) {
726
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
727
+ return new Message2(this.client, data);
728
+ }
729
+ return void 0;
730
+ }
731
+ /**
732
+ * Fetch a webhook by ID using bot auth.
733
+ * @param client - The client instance
734
+ * @param webhookId - The webhook ID
735
+ * @returns Webhook without token (cannot send)
736
+ */
737
+ static async fetch(client, webhookId) {
738
+ const data = await client.rest.get(import_types3.Routes.webhook(webhookId));
739
+ return new _Webhook(client, data);
740
+ }
741
+ /**
742
+ * Create a Webhook instance from an ID and token (e.g. from a stored webhook URL).
743
+ * @param client - The client instance
744
+ * @param webhookId - The webhook ID
745
+ * @param token - The webhook token (from createWebhook or stored)
746
+ * @param options - Optional channelId, guildId, name for display
747
+ */
748
+ static fromToken(client, webhookId, token, options) {
749
+ return new _Webhook(client, {
750
+ id: webhookId,
751
+ guild_id: options?.guildId ?? "",
752
+ channel_id: options?.channelId ?? "",
753
+ name: options?.name ?? "Webhook",
754
+ avatar: null,
755
+ token,
756
+ user: { id: "", username: "webhook", discriminator: "0" }
757
+ });
758
+ }
759
+ };
760
+ }
761
+ });
762
+
763
+ // src/structures/Invite.ts
764
+ var Invite_exports = {};
765
+ __export(Invite_exports, {
766
+ Invite: () => Invite
767
+ });
768
+ var import_types4, Invite;
769
+ var init_Invite = __esm({
770
+ "src/structures/Invite.ts"() {
771
+ "use strict";
772
+ init_Base();
773
+ import_types4 = require("@fluxerjs/types");
774
+ Invite = class extends Base {
775
+ client;
776
+ code;
777
+ type;
778
+ guild;
779
+ channel;
780
+ inviter;
781
+ memberCount;
782
+ presenceCount;
783
+ expiresAt;
784
+ temporary;
785
+ createdAt;
786
+ uses;
787
+ maxUses;
788
+ maxAge;
789
+ /** @param data - API invite from GET /invites/{code}, channel/guild invite list, or gateway INVITE_CREATE */
790
+ constructor(client, data) {
791
+ super();
792
+ this.client = client;
793
+ this.code = data.code;
794
+ this.type = data.type;
795
+ this.guild = data.guild;
796
+ this.channel = data.channel;
797
+ this.inviter = data.inviter ? client.getOrCreateUser(data.inviter) : null;
798
+ this.memberCount = data.member_count ?? null;
799
+ this.presenceCount = data.presence_count ?? null;
800
+ this.expiresAt = data.expires_at ?? null;
801
+ this.temporary = data.temporary ?? null;
802
+ this.createdAt = data.created_at ?? null;
803
+ this.uses = data.uses ?? null;
804
+ this.maxUses = data.max_uses ?? null;
805
+ this.maxAge = data.max_age ?? null;
806
+ }
807
+ /** Full invite URL (https://fluxer.gg/{code} or instance-specific). */
808
+ get url() {
809
+ return `https://fluxer.gg/${this.code}`;
810
+ }
811
+ /**
812
+ * Resolve the guild from cache if available.
813
+ * @returns The guild, or null if not cached
814
+ */
815
+ getGuild() {
816
+ return this.guild?.id ? this.client.guilds.get(this.guild.id) ?? null : null;
817
+ }
818
+ /**
819
+ * Delete this invite.
820
+ * Requires Manage Guild or Create Instant Invite permission.
821
+ */
822
+ async delete() {
823
+ await this.client.rest.delete(import_types4.Routes.invite(this.code), { auth: true });
369
824
  }
370
825
  };
371
826
  }
@@ -382,13 +837,16 @@ __export(Channel_exports, {
382
837
  TextChannel: () => TextChannel,
383
838
  VoiceChannel: () => VoiceChannel
384
839
  });
385
- var import_types3, Channel, GuildChannel, TextChannel, CategoryChannel, VoiceChannel, LinkChannel, DMChannel;
840
+ var import_types5, import_util, Channel, GuildChannel, TextChannel, CategoryChannel, VoiceChannel, LinkChannel, DMChannel;
386
841
  var init_Channel = __esm({
387
842
  "src/structures/Channel.ts"() {
388
843
  "use strict";
389
844
  init_MessageManager();
845
+ init_MessageCollector();
390
846
  init_Base();
391
- import_types3 = require("@fluxerjs/types");
847
+ init_messageUtils();
848
+ import_types5 = require("@fluxerjs/types");
849
+ import_util = require("@fluxerjs/util");
392
850
  Channel = class _Channel extends Base {
393
851
  /** Whether this channel has a send method (TextChannel, DMChannel). */
394
852
  isSendable() {
@@ -396,7 +854,7 @@ var init_Channel = __esm({
396
854
  }
397
855
  /** Whether this channel is a DM or Group DM. */
398
856
  isDM() {
399
- return this.type === import_types3.ChannelType.DM || this.type === import_types3.ChannelType.GroupDM;
857
+ return this.type === import_types5.ChannelType.DM || this.type === import_types5.ChannelType.GroupDM;
400
858
  }
401
859
  /** Whether this channel is voice-based (VoiceChannel). */
402
860
  isVoice() {
@@ -409,12 +867,21 @@ var init_Channel = __esm({
409
867
  client;
410
868
  id;
411
869
  type;
870
+ /** Channel name. Guild channels and Group DMs have names; 1:1 DMs are typically null. */
871
+ name;
872
+ /** Channel icon hash (Group DMs). Null if none. */
873
+ icon;
874
+ /** ISO timestamp when the last message was pinned. Null if never pinned. */
875
+ lastPinTimestamp;
412
876
  /** @param data - API channel from GET /channels/{id} or GET /guilds/{id}/channels */
413
877
  constructor(client, data) {
414
878
  super();
415
879
  this.client = client;
416
880
  this.id = data.id;
417
881
  this.type = data.type;
882
+ this.name = data.name ?? null;
883
+ this.icon = data.icon ?? null;
884
+ this.lastPinTimestamp = data.last_pin_timestamp ?? null;
418
885
  }
419
886
  /**
420
887
  * Create the appropriate channel subclass from API data.
@@ -423,10 +890,10 @@ var init_Channel = __esm({
423
890
  */
424
891
  static from(client, data) {
425
892
  const type = data.type ?? 0;
426
- if (type === import_types3.ChannelType.GuildText) return new TextChannel(client, data);
427
- if (type === import_types3.ChannelType.GuildCategory) return new CategoryChannel(client, data);
428
- if (type === import_types3.ChannelType.GuildVoice) return new VoiceChannel(client, data);
429
- if (type === import_types3.ChannelType.GuildLink || type === import_types3.ChannelType.GuildLinkExtended)
893
+ if (type === import_types5.ChannelType.GuildText) return new TextChannel(client, data);
894
+ if (type === import_types5.ChannelType.GuildCategory) return new CategoryChannel(client, data);
895
+ if (type === import_types5.ChannelType.GuildVoice) return new VoiceChannel(client, data);
896
+ if (type === import_types5.ChannelType.GuildLink || type === import_types5.ChannelType.GuildLinkExtended)
430
897
  return new LinkChannel(client, data);
431
898
  return new GuildChannel(client, data);
432
899
  }
@@ -436,22 +903,41 @@ var init_Channel = __esm({
436
903
  */
437
904
  static fromOrCreate(client, data) {
438
905
  const type = data.type ?? 0;
439
- if (type === import_types3.ChannelType.DM || type === import_types3.ChannelType.GroupDM)
906
+ if (type === import_types5.ChannelType.DM || type === import_types5.ChannelType.GroupDM)
440
907
  return _Channel.createDM(client, data);
441
908
  return _Channel.from(client, data);
442
909
  }
910
+ /**
911
+ * Bulk delete messages. Requires Manage Messages permission.
912
+ * @param messageIds - Array of message IDs to delete (2–100)
913
+ */
914
+ async bulkDeleteMessages(messageIds) {
915
+ await this.client.rest.post(import_types5.Routes.channelBulkDelete(this.id), {
916
+ body: { message_ids: messageIds },
917
+ auth: true
918
+ });
919
+ }
920
+ /**
921
+ * Send a typing indicator to the channel. Lasts ~10 seconds.
922
+ */
923
+ async sendTyping() {
924
+ await this.client.rest.post(import_types5.Routes.channelTyping(this.id), { auth: true });
925
+ }
443
926
  };
444
927
  GuildChannel = class extends Channel {
445
928
  guildId;
446
929
  name;
447
930
  position;
448
931
  parentId;
932
+ /** Permission overwrites for roles and members. */
933
+ permissionOverwrites;
449
934
  constructor(client, data) {
450
935
  super(client, data);
451
936
  this.guildId = data.guild_id ?? "";
452
937
  this.name = data.name ?? null;
453
938
  this.position = data.position;
454
939
  this.parentId = data.parent_id ?? null;
940
+ this.permissionOverwrites = data.permission_overwrites ?? [];
455
941
  }
456
942
  /**
457
943
  * Create a webhook in this channel.
@@ -460,7 +946,7 @@ var init_Channel = __esm({
460
946
  */
461
947
  async createWebhook(options) {
462
948
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
463
- const data = await this.client.rest.post(import_types3.Routes.channelWebhooks(this.id), {
949
+ const data = await this.client.rest.post(import_types5.Routes.channelWebhooks(this.id), {
464
950
  body: options,
465
951
  auth: true
466
952
  });
@@ -472,10 +958,38 @@ var init_Channel = __esm({
472
958
  */
473
959
  async fetchWebhooks() {
474
960
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
475
- const data = await this.client.rest.get(import_types3.Routes.channelWebhooks(this.id));
961
+ const data = await this.client.rest.get(import_types5.Routes.channelWebhooks(this.id));
476
962
  const list = Array.isArray(data) ? data : Object.values(data ?? {});
477
963
  return list.map((w) => new Webhook2(this.client, w));
478
964
  }
965
+ /**
966
+ * Create an invite for this channel.
967
+ * @param options - max_uses (0–100), max_age (0–604800 seconds), unique, temporary
968
+ * Requires Create Instant Invite permission.
969
+ */
970
+ async createInvite(options) {
971
+ const { Invite: Invite2 } = await Promise.resolve().then(() => (init_Invite(), Invite_exports));
972
+ const body = {};
973
+ if (options?.max_uses != null) body.max_uses = options.max_uses;
974
+ if (options?.max_age != null) body.max_age = options.max_age;
975
+ if (options?.unique != null) body.unique = options.unique;
976
+ if (options?.temporary != null) body.temporary = options.temporary;
977
+ const data = await this.client.rest.post(import_types5.Routes.channelInvites(this.id), {
978
+ body: Object.keys(body).length ? body : void 0,
979
+ auth: true
980
+ });
981
+ return new Invite2(this.client, data);
982
+ }
983
+ /**
984
+ * Fetch invites for this channel.
985
+ * Requires Manage Channel permission.
986
+ */
987
+ async fetchInvites() {
988
+ const { Invite: Invite2 } = await Promise.resolve().then(() => (init_Invite(), Invite_exports));
989
+ const data = await this.client.rest.get(import_types5.Routes.channelInvites(this.id));
990
+ const list = Array.isArray(data) ? data : Object.values(data ?? {});
991
+ return list.map((i) => new Invite2(this.client, i));
992
+ }
479
993
  };
480
994
  TextChannel = class extends GuildChannel {
481
995
  topic;
@@ -491,25 +1005,39 @@ var init_Channel = __esm({
491
1005
  }
492
1006
  /**
493
1007
  * Send a message to this channel.
494
- * @param options - Text content or object with `content` and/or `embeds`
1008
+ * @param options - Text content or object with content, embeds, and/or files
495
1009
  */
496
1010
  async send(options) {
497
- const body = typeof options === "string" ? { content: options } : options;
1011
+ const opts = typeof options === "string" ? { content: options } : options;
1012
+ const body = buildSendBody(options);
498
1013
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
499
- const data = await this.client.rest.post(import_types3.Routes.channelMessages(this.id), { body });
1014
+ const postOptions = opts.files?.length ? { body, files: opts.files } : { body };
1015
+ const data = await this.client.rest.post(import_types5.Routes.channelMessages(this.id), postOptions);
500
1016
  return new Message2(this.client, data);
501
1017
  }
502
1018
  /** Message manager for this channel. Use channel.messages.fetch(messageId). */
503
1019
  get messages() {
504
1020
  return new MessageManager(this.client, this.id);
505
1021
  }
1022
+ /**
1023
+ * Create a message collector for this channel.
1024
+ * Collects messages matching the filter until time expires or max is reached.
1025
+ * @param options - Filter, time (ms), and max count
1026
+ * @example
1027
+ * const collector = channel.createMessageCollector({ filter: m => m.author.id === userId, time: 10000 });
1028
+ * collector.on('collect', m => console.log(m.content));
1029
+ * collector.on('end', (collected, reason) => { ... });
1030
+ */
1031
+ createMessageCollector(options) {
1032
+ return new MessageCollector(this.client, this.id, options);
1033
+ }
506
1034
  /**
507
1035
  * Fetch pinned messages in this channel.
508
1036
  * @returns Pinned messages
509
1037
  */
510
1038
  async fetchPinnedMessages() {
511
1039
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
512
- const data = await this.client.rest.get(import_types3.Routes.channelPins(this.id));
1040
+ const data = await this.client.rest.get(import_types5.Routes.channelPins(this.id));
513
1041
  const list = Array.isArray(data) ? data : data?.items ?? [];
514
1042
  return list.map((item) => {
515
1043
  const msg = typeof item === "object" && item && "message" in item ? item.message : item;
@@ -523,6 +1051,10 @@ var init_Channel = __esm({
523
1051
  * @deprecated Use channel.messages.fetch(messageId) instead.
524
1052
  */
525
1053
  async fetchMessage(messageId) {
1054
+ (0, import_util.emitDeprecationWarning)(
1055
+ "Channel.fetchMessage()",
1056
+ "Use channel.messages.fetch(messageId) instead."
1057
+ );
526
1058
  return this.client.channels.fetchMessage(this.id, messageId);
527
1059
  }
528
1060
  };
@@ -548,31 +1080,51 @@ var init_Channel = __esm({
548
1080
  };
549
1081
  DMChannel = class extends Channel {
550
1082
  lastMessageId;
1083
+ /** Group DM creator ID. Null for 1:1 DMs. */
1084
+ ownerId;
1085
+ /** Group DM recipients as User objects. Empty for 1:1 DMs. */
1086
+ recipients;
1087
+ /** Group DM member display names (userId -> nickname). */
1088
+ nicks;
551
1089
  constructor(client, data) {
552
1090
  super(client, data);
553
1091
  this.lastMessageId = data.last_message_id ?? null;
1092
+ this.ownerId = data.owner_id ?? null;
1093
+ this.recipients = (data.recipients ?? []).map(
1094
+ (u) => client.getOrCreateUser(u)
1095
+ );
1096
+ this.nicks = data.nicks ?? {};
554
1097
  }
555
1098
  /**
556
1099
  * Send a message to this DM channel.
557
- * @param options - Text content or object with `content` and/or `embeds`
1100
+ * @param options - Text content or object with content, embeds, and/or files
558
1101
  */
559
1102
  async send(options) {
560
- const body = typeof options === "string" ? { content: options } : options;
1103
+ const opts = typeof options === "string" ? { content: options } : options;
1104
+ const body = buildSendBody(options);
561
1105
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
562
- const data = await this.client.rest.post(import_types3.Routes.channelMessages(this.id), { body });
1106
+ const postOptions = opts.files?.length ? { body, files: opts.files } : { body };
1107
+ const data = await this.client.rest.post(import_types5.Routes.channelMessages(this.id), postOptions);
563
1108
  return new Message2(this.client, data);
564
1109
  }
565
1110
  /** Message manager for this channel. Use channel.messages.fetch(messageId). */
566
1111
  get messages() {
567
1112
  return new MessageManager(this.client, this.id);
568
1113
  }
1114
+ /**
1115
+ * Create a message collector for this DM channel.
1116
+ * @param options - Filter, time (ms), and max count
1117
+ */
1118
+ createMessageCollector(options) {
1119
+ return new MessageCollector(this.client, this.id, options);
1120
+ }
569
1121
  /**
570
1122
  * Fetch pinned messages in this DM channel.
571
1123
  * @returns Pinned messages
572
1124
  */
573
1125
  async fetchPinnedMessages() {
574
1126
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
575
- const data = await this.client.rest.get(import_types3.Routes.channelPins(this.id));
1127
+ const data = await this.client.rest.get(import_types5.Routes.channelPins(this.id));
576
1128
  const list = Array.isArray(data) ? data : data?.items ?? [];
577
1129
  return list.map((item) => {
578
1130
  const msg = typeof item === "object" && item && "message" in item ? item.message : item;
@@ -586,23 +1138,57 @@ var init_Channel = __esm({
586
1138
  * @deprecated Use channel.messages.fetch(messageId) instead.
587
1139
  */
588
1140
  async fetchMessage(messageId) {
1141
+ (0, import_util.emitDeprecationWarning)(
1142
+ "Channel.fetchMessage()",
1143
+ "Use channel.messages.fetch(messageId) instead."
1144
+ );
589
1145
  return this.client.channels.fetchMessage(this.id, messageId);
590
1146
  }
591
1147
  };
592
1148
  }
593
1149
  });
594
1150
 
1151
+ // src/util/permissions.ts
1152
+ function computePermissions(basePermissions, overwrites, memberRoles, memberId, isOwner) {
1153
+ if (isOwner) return import_util3.ALL_PERMISSIONS_BIGINT;
1154
+ let perms = basePermissions;
1155
+ for (const overwrite of overwrites ?? []) {
1156
+ const applies = overwrite.type === import_types7.OverwriteType.Role && memberRoles.includes(overwrite.id) || overwrite.type === import_types7.OverwriteType.Member && overwrite.id === memberId;
1157
+ if (!applies) continue;
1158
+ const allow = BigInt(overwrite.allow || "0");
1159
+ const deny = BigInt(overwrite.deny || "0");
1160
+ perms = perms & ~deny | allow;
1161
+ }
1162
+ return perms;
1163
+ }
1164
+ function hasPermission(bitfield, permission) {
1165
+ const Administrator = 1n << 3n;
1166
+ if ((bitfield & Administrator) !== 0n) return true;
1167
+ return (bitfield & permission) === permission;
1168
+ }
1169
+ var import_types7, import_util3;
1170
+ var init_permissions = __esm({
1171
+ "src/util/permissions.ts"() {
1172
+ "use strict";
1173
+ import_types7 = require("@fluxerjs/types");
1174
+ import_util3 = require("@fluxerjs/util");
1175
+ }
1176
+ });
1177
+
595
1178
  // src/structures/GuildMember.ts
596
1179
  var GuildMember_exports = {};
597
1180
  __export(GuildMember_exports, {
598
1181
  GuildMember: () => GuildMember
599
1182
  });
600
- var import_types5, GuildMember;
1183
+ var import_util4, import_types8, GuildMember;
601
1184
  var init_GuildMember = __esm({
602
1185
  "src/structures/GuildMember.ts"() {
603
1186
  "use strict";
604
1187
  init_Base();
605
- import_types5 = require("@fluxerjs/types");
1188
+ import_util4 = require("@fluxerjs/util");
1189
+ import_types8 = require("@fluxerjs/types");
1190
+ init_cdn();
1191
+ init_permissions();
606
1192
  GuildMember = class extends Base {
607
1193
  client;
608
1194
  id;
@@ -640,13 +1226,34 @@ var init_GuildMember = __esm({
640
1226
  get displayName() {
641
1227
  return this.nick ?? this.user.globalName ?? this.user.username;
642
1228
  }
1229
+ /**
1230
+ * Get the guild-specific avatar URL for this member.
1231
+ * Returns null if the member has no guild avatar (use displayAvatarURL for fallback).
1232
+ */
1233
+ avatarURL(options) {
1234
+ return cdnMemberAvatarURL(this.guild.id, this.id, this.avatar, options);
1235
+ }
1236
+ /**
1237
+ * Get the avatar URL to display for this member.
1238
+ * Uses guild-specific avatar if set, otherwise falls back to the user's avatar.
1239
+ */
1240
+ displayAvatarURL(options) {
1241
+ return this.avatarURL(options) ?? this.user.displayAvatarURL(options);
1242
+ }
1243
+ /**
1244
+ * Get the guild-specific banner URL for this member.
1245
+ * Returns null if the member has no guild banner.
1246
+ */
1247
+ bannerURL(options) {
1248
+ return cdnMemberBannerURL(this.guild.id, this.id, this.banner, options);
1249
+ }
643
1250
  /**
644
1251
  * Add a role to this member.
645
1252
  * @param roleId - The role ID to add
646
1253
  * Requires Manage Roles permission.
647
1254
  */
648
1255
  async addRole(roleId) {
649
- await this.client.rest.put(import_types5.Routes.guildMemberRole(this.guild.id, this.id, roleId));
1256
+ await this.client.rest.put(import_types8.Routes.guildMemberRole(this.guild.id, this.id, roleId));
650
1257
  }
651
1258
  /**
652
1259
  * Remove a role from this member.
@@ -654,7 +1261,67 @@ var init_GuildMember = __esm({
654
1261
  * Requires Manage Roles permission.
655
1262
  */
656
1263
  async removeRole(roleId) {
657
- await this.client.rest.delete(import_types5.Routes.guildMemberRole(this.guild.id, this.id, roleId));
1264
+ await this.client.rest.delete(import_types8.Routes.guildMemberRole(this.guild.id, this.id, roleId));
1265
+ }
1266
+ /**
1267
+ * Get the member's guild-level permissions (from roles only, no channel overwrites).
1268
+ * Use this for server-wide permission checks (e.g. ban, kick, manage roles).
1269
+ * @returns Object with has(permission) to check specific permissions
1270
+ * @example
1271
+ * const perms = member.permissions;
1272
+ * if (perms.has(PermissionFlags.BanMembers)) { ... }
1273
+ */
1274
+ get permissions() {
1275
+ const base = this._computeBasePermissions();
1276
+ const ownerId = this.guild.ownerId;
1277
+ const isOwner = ownerId != null && ownerId !== "" && String(ownerId) === String(this.id);
1278
+ const perms = computePermissions(base, [], [], this.id, isOwner);
1279
+ return {
1280
+ has(permission) {
1281
+ const perm = typeof permission === "number" ? permission : import_util4.PermissionFlagsMap[String(permission)];
1282
+ if (perm === void 0) return false;
1283
+ return hasPermission(perms, BigInt(perm));
1284
+ }
1285
+ };
1286
+ }
1287
+ /**
1288
+ * Compute the member's effective permissions in a guild channel.
1289
+ * Applies role permissions and channel overwrites.
1290
+ * @param channel - The guild channel to check permissions for
1291
+ * @returns Object with has(permission) to check specific permissions
1292
+ * @example
1293
+ * const perms = member.permissionsIn(channel);
1294
+ * if (perms.has(PermissionFlags.SendMessages)) { ... }
1295
+ */
1296
+ permissionsIn(channel) {
1297
+ const base = this._computeBasePermissions();
1298
+ const ownerId = this.guild.ownerId;
1299
+ const isOwner = ownerId != null && ownerId !== "" && String(ownerId) === String(this.id);
1300
+ const perms = computePermissions(
1301
+ base,
1302
+ channel.permissionOverwrites,
1303
+ this.roles,
1304
+ this.id,
1305
+ isOwner
1306
+ );
1307
+ return {
1308
+ has(permission) {
1309
+ const perm = typeof permission === "number" ? permission : import_util4.PermissionFlagsMap[String(permission)];
1310
+ if (perm === void 0) return false;
1311
+ return hasPermission(perms, BigInt(perm));
1312
+ }
1313
+ };
1314
+ }
1315
+ _computeBasePermissions() {
1316
+ let base = 0n;
1317
+ const everyone = this.guild.roles.get(this.guild.id);
1318
+ if (everyone) base |= BigInt(everyone.permissions);
1319
+ for (const roleId of this.roles) {
1320
+ if (roleId === this.guild.id) continue;
1321
+ const role = this.guild.roles.get(roleId);
1322
+ if (role) base |= BigInt(role.permissions);
1323
+ }
1324
+ return base;
658
1325
  }
659
1326
  };
660
1327
  }
@@ -665,11 +1332,12 @@ var Role_exports = {};
665
1332
  __export(Role_exports, {
666
1333
  Role: () => Role
667
1334
  });
668
- var Role;
1335
+ var import_util5, Role;
669
1336
  var init_Role = __esm({
670
1337
  "src/structures/Role.ts"() {
671
1338
  "use strict";
672
1339
  init_Base();
1340
+ import_util5 = require("@fluxerjs/util");
673
1341
  Role = class extends Base {
674
1342
  client;
675
1343
  id;
@@ -681,6 +1349,8 @@ var init_Role = __esm({
681
1349
  hoist;
682
1350
  mentionable;
683
1351
  unicodeEmoji;
1352
+ /** Separately sorted position for hoisted roles. Null if not set. */
1353
+ hoistPosition;
684
1354
  /** @param client - The client instance */
685
1355
  /** @param data - API role from GET /guilds/{id}/roles or gateway role events */
686
1356
  /** @param guildId - The guild this role belongs to */
@@ -696,21 +1366,71 @@ var init_Role = __esm({
696
1366
  this.hoist = !!data.hoist;
697
1367
  this.mentionable = !!data.mentionable;
698
1368
  this.unicodeEmoji = data.unicode_emoji ?? null;
1369
+ this.hoistPosition = data.hoist_position ?? null;
699
1370
  }
700
1371
  /** Returns a mention string (e.g. `<@&123456>`). */
701
1372
  toString() {
702
1373
  return `<@&${this.id}>`;
703
1374
  }
1375
+ /**
1376
+ * Check if this role has a permission. Administrator grants all permissions.
1377
+ * @param permission - Permission flag, name, or resolvable
1378
+ * @returns true if the role has the permission
1379
+ * @example
1380
+ * if (role.has(PermissionFlags.BanMembers)) { ... }
1381
+ * if (role.has('ManageChannels')) { ... }
1382
+ */
1383
+ has(permission) {
1384
+ const perm = typeof permission === "number" ? permission : import_util5.PermissionFlags[permission];
1385
+ if (perm === void 0) return false;
1386
+ const permNum = Number(perm);
1387
+ const rolePerms = BigInt(this.permissions);
1388
+ const permBig = BigInt(permNum);
1389
+ if (permBig < 0) return false;
1390
+ if ((rolePerms & BigInt(import_util5.PermissionFlags.Administrator)) !== 0n) return true;
1391
+ return (rolePerms & permBig) === permBig;
1392
+ }
704
1393
  };
705
1394
  }
706
1395
  });
707
1396
 
708
- // src/util/Constants.ts
709
- var CDN_URL;
710
- var init_Constants = __esm({
711
- "src/util/Constants.ts"() {
1397
+ // src/structures/GuildBan.ts
1398
+ var GuildBan_exports = {};
1399
+ __export(GuildBan_exports, {
1400
+ GuildBan: () => GuildBan
1401
+ });
1402
+ var import_types9, GuildBan;
1403
+ var init_GuildBan = __esm({
1404
+ "src/structures/GuildBan.ts"() {
712
1405
  "use strict";
713
- CDN_URL = "https://fluxerusercontent.com";
1406
+ init_Base();
1407
+ import_types9 = require("@fluxerjs/types");
1408
+ GuildBan = class extends Base {
1409
+ client;
1410
+ guildId;
1411
+ user;
1412
+ reason;
1413
+ /** ISO timestamp when a temporary ban expires. Null for permanent bans. */
1414
+ expiresAt;
1415
+ /** @param data - API ban from GET /guilds/{id}/bans or gateway GUILD_BAN_ADD */
1416
+ constructor(client, data, guildId) {
1417
+ super();
1418
+ this.client = client;
1419
+ this.guildId = data.guild_id ?? guildId;
1420
+ this.user = client.getOrCreateUser(data.user);
1421
+ this.reason = data.reason ?? null;
1422
+ this.expiresAt = data.expires_at ?? null;
1423
+ }
1424
+ /**
1425
+ * Remove this ban (unban the user).
1426
+ * Requires Ban Members permission.
1427
+ */
1428
+ async unban() {
1429
+ await this.client.rest.delete(import_types9.Routes.guildBan(this.guildId, this.user.id), {
1430
+ auth: true
1431
+ });
1432
+ }
1433
+ };
714
1434
  }
715
1435
  });
716
1436
 
@@ -719,17 +1439,20 @@ var Guild_exports = {};
719
1439
  __export(Guild_exports, {
720
1440
  Guild: () => Guild
721
1441
  });
722
- var import_util, import_collection3, import_types6, Guild;
1442
+ var import_util6, import_rest3, import_collection5, import_types10, Guild;
723
1443
  var init_Guild = __esm({
724
1444
  "src/structures/Guild.ts"() {
725
1445
  "use strict";
726
- import_util = require("@fluxerjs/util");
1446
+ import_util6 = require("@fluxerjs/util");
1447
+ import_rest3 = require("@fluxerjs/rest");
727
1448
  init_Base();
728
- import_collection3 = require("@fluxerjs/collection");
1449
+ init_FluxerError();
1450
+ init_ErrorCodes();
1451
+ import_collection5 = require("@fluxerjs/collection");
729
1452
  init_GuildMember();
730
1453
  init_Role();
731
1454
  init_Constants();
732
- import_types6 = require("@fluxerjs/types");
1455
+ import_types10 = require("@fluxerjs/types");
733
1456
  Guild = class extends Base {
734
1457
  client;
735
1458
  id;
@@ -737,9 +1460,36 @@ var init_Guild = __esm({
737
1460
  icon;
738
1461
  banner;
739
1462
  ownerId;
740
- members = new import_collection3.Collection();
741
- channels = new import_collection3.Collection();
742
- roles = new import_collection3.Collection();
1463
+ /** Invite splash image hash. Null if none. */
1464
+ splash;
1465
+ /** Custom vanity URL code (e.g. fluxer.gg/code). Null if none. */
1466
+ vanityURLCode;
1467
+ /** Enabled guild features. */
1468
+ features;
1469
+ verificationLevel;
1470
+ defaultMessageNotifications;
1471
+ explicitContentFilter;
1472
+ /** AFK voice channel ID. Null if none. */
1473
+ afkChannelId;
1474
+ /** AFK timeout in seconds. */
1475
+ afkTimeout;
1476
+ /** System messages channel ID. Null if none. */
1477
+ systemChannelId;
1478
+ /** Rules/guidelines channel ID. Null if none. */
1479
+ rulesChannelId;
1480
+ nsfwLevel;
1481
+ mfaLevel;
1482
+ /** Banner image width. Optional. */
1483
+ bannerWidth;
1484
+ /** Banner image height. Optional. */
1485
+ bannerHeight;
1486
+ /** Splash image width. Optional. */
1487
+ splashWidth;
1488
+ /** Splash image height. Optional. */
1489
+ splashHeight;
1490
+ members = new import_collection5.Collection();
1491
+ channels = new import_collection5.Collection();
1492
+ roles = new import_collection5.Collection();
743
1493
  /** @param data - API guild from GET /guilds/{id} or gateway GUILD_CREATE */
744
1494
  constructor(client, data) {
745
1495
  super();
@@ -748,7 +1498,23 @@ var init_Guild = __esm({
748
1498
  this.name = data.name;
749
1499
  this.icon = data.icon ?? null;
750
1500
  this.banner = data.banner ?? null;
751
- this.ownerId = data.owner_id;
1501
+ this.ownerId = data.owner_id ?? data.ownerId ?? "";
1502
+ this.splash = data.splash ?? null;
1503
+ this.vanityURLCode = data.vanity_url_code ?? null;
1504
+ this.features = data.features ?? [];
1505
+ this.verificationLevel = data.verification_level ?? 0;
1506
+ this.defaultMessageNotifications = data.default_message_notifications ?? 0;
1507
+ this.explicitContentFilter = data.explicit_content_filter ?? 0;
1508
+ this.afkChannelId = data.afk_channel_id ?? null;
1509
+ this.afkTimeout = data.afk_timeout ?? 0;
1510
+ this.systemChannelId = data.system_channel_id ?? null;
1511
+ this.rulesChannelId = data.rules_channel_id ?? null;
1512
+ this.nsfwLevel = data.nsfw_level ?? 0;
1513
+ this.mfaLevel = data.mfa_level ?? 0;
1514
+ this.bannerWidth = data.banner_width ?? null;
1515
+ this.bannerHeight = data.banner_height ?? null;
1516
+ this.splashWidth = data.splash_width ?? null;
1517
+ this.splashHeight = data.splash_height ?? null;
752
1518
  for (const r of data.roles ?? []) {
753
1519
  this.roles.set(r.id, new Role(client, r, this.id));
754
1520
  }
@@ -765,6 +1531,12 @@ var init_Guild = __esm({
765
1531
  const size = options?.size ? `?size=${options.size}` : "";
766
1532
  return `${CDN_URL}/banners/${this.id}/${this.banner}.png${size}`;
767
1533
  }
1534
+ /** Get the guild splash (invite background) URL, or null if no splash. */
1535
+ splashURL(options) {
1536
+ if (!this.splash) return null;
1537
+ const size = options?.size ? `?size=${options.size}` : "";
1538
+ return `${CDN_URL}/splashes/${this.id}/${this.splash}.png${size}`;
1539
+ }
768
1540
  /**
769
1541
  * Add a role to a member by user ID. Does not require fetching the member first.
770
1542
  * @param userId - The user ID of the member
@@ -772,7 +1544,7 @@ var init_Guild = __esm({
772
1544
  * Requires Manage Roles permission.
773
1545
  */
774
1546
  async addRoleToMember(userId, roleId) {
775
- await this.client.rest.put(import_types6.Routes.guildMemberRole(this.id, userId, roleId));
1547
+ await this.client.rest.put(import_types10.Routes.guildMemberRole(this.id, userId, roleId));
776
1548
  }
777
1549
  /**
778
1550
  * Remove a role from a member by user ID. Does not require fetching the member first.
@@ -781,7 +1553,7 @@ var init_Guild = __esm({
781
1553
  * Requires Manage Roles permission.
782
1554
  */
783
1555
  async removeRoleFromMember(userId, roleId) {
784
- await this.client.rest.delete(import_types6.Routes.guildMemberRole(this.id, userId, roleId));
1556
+ await this.client.rest.delete(import_types10.Routes.guildMemberRole(this.id, userId, roleId));
785
1557
  }
786
1558
  /**
787
1559
  * Resolve a role ID from an argument (role mention, raw ID, or name).
@@ -790,14 +1562,14 @@ var init_Guild = __esm({
790
1562
  * @returns The role ID, or null if not found
791
1563
  */
792
1564
  async resolveRoleId(arg) {
793
- const parsed = (0, import_util.parseRoleMention)(arg);
1565
+ const parsed = (0, import_util6.parseRoleMention)(arg);
794
1566
  if (parsed) return parsed;
795
1567
  if (/^\d{17,19}$/.test(arg.trim())) return arg.trim();
796
1568
  const cached = this.roles.find(
797
1569
  (r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase())
798
1570
  );
799
1571
  if (cached) return cached.id;
800
- const roles = await this.client.rest.get(import_types6.Routes.guildRoles(this.id));
1572
+ const roles = await this.client.rest.get(import_types10.Routes.guildRoles(this.id));
801
1573
  const list = Array.isArray(roles) ? roles : Object.values(roles ?? {});
802
1574
  const role = list.find((r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase()));
803
1575
  if (role) {
@@ -806,19 +1578,75 @@ var init_Guild = __esm({
806
1578
  }
807
1579
  return null;
808
1580
  }
1581
+ /**
1582
+ * Ban a user from this guild.
1583
+ * @param userId - The user ID to ban
1584
+ * @param options - Optional reason, delete_message_days (0–7), and ban_duration_seconds (temporary ban).
1585
+ * ban_duration_seconds: 0 = permanent, or use 3600, 43200, 86400, 259200, 432000, 604800, 1209600, 2592000.
1586
+ * Requires Ban Members permission.
1587
+ */
1588
+ async ban(userId, options) {
1589
+ const body = {};
1590
+ if (options?.reason) body.reason = options.reason;
1591
+ if (options?.delete_message_days != null)
1592
+ body.delete_message_days = options.delete_message_days;
1593
+ if (options?.ban_duration_seconds != null)
1594
+ body.ban_duration_seconds = options.ban_duration_seconds;
1595
+ await this.client.rest.put(import_types10.Routes.guildBan(this.id, userId), {
1596
+ body: Object.keys(body).length ? body : void 0,
1597
+ auth: true
1598
+ });
1599
+ }
1600
+ /**
1601
+ * Fetch guild bans. Requires Ban Members permission.
1602
+ * @returns List of GuildBan objects
1603
+ */
1604
+ async fetchBans() {
1605
+ const { GuildBan: GuildBan2 } = await Promise.resolve().then(() => (init_GuildBan(), GuildBan_exports));
1606
+ const data = await this.client.rest.get(import_types10.Routes.guildBans(this.id));
1607
+ const list = Array.isArray(data) ? data : data?.bans ?? [];
1608
+ return list.map((b) => new GuildBan2(this.client, { ...b, guild_id: this.id }, this.id));
1609
+ }
1610
+ /**
1611
+ * Remove a ban (unban a user).
1612
+ * @param userId - The user ID to unban
1613
+ * Requires Ban Members permission.
1614
+ */
1615
+ async unban(userId) {
1616
+ await this.client.rest.delete(import_types10.Routes.guildBan(this.id, userId), { auth: true });
1617
+ }
1618
+ /**
1619
+ * Kick a member from this guild.
1620
+ * @param userId - The user ID to kick
1621
+ * Requires Kick Members permission.
1622
+ */
1623
+ async kick(userId) {
1624
+ await this.client.rest.delete(import_types10.Routes.guildMember(this.id, userId), { auth: true });
1625
+ }
809
1626
  /**
810
1627
  * Fetch a guild member by user ID.
811
1628
  * @param userId - The user ID of the member to fetch
812
- * @returns The guild member, or null if not found
1629
+ * @returns The guild member
1630
+ * @throws FluxerError with MEMBER_NOT_FOUND if user is not in the guild (404)
1631
+ * @throws FluxerError with cause for permission denied (403) or other REST errors
813
1632
  */
814
1633
  async fetchMember(userId) {
815
1634
  try {
816
1635
  const data = await this.client.rest.get(
817
- import_types6.Routes.guildMember(this.id, userId)
1636
+ import_types10.Routes.guildMember(this.id, userId)
818
1637
  );
819
- return new GuildMember(this.client, { ...data, guild_id: this.id }, this);
820
- } catch {
821
- return null;
1638
+ const member = new GuildMember(this.client, { ...data, guild_id: this.id }, this);
1639
+ this.members.set(member.id, member);
1640
+ return member;
1641
+ } catch (err) {
1642
+ const statusCode = err instanceof import_rest3.FluxerAPIError ? err.statusCode : err?.statusCode;
1643
+ if (statusCode === 404) {
1644
+ throw new FluxerError(`Member ${userId} not found in guild`, {
1645
+ code: ErrorCodes.MemberNotFound,
1646
+ cause: err
1647
+ });
1648
+ }
1649
+ throw err instanceof FluxerError ? err : new FluxerError("Failed to fetch guild member", { cause: err });
822
1650
  }
823
1651
  }
824
1652
  /**
@@ -831,30 +1659,77 @@ var init_Guild = __esm({
831
1659
  if (options?.before) params.set("before", options.before);
832
1660
  if (options?.after) params.set("after", options.after);
833
1661
  if (options?.userId) params.set("user_id", options.userId);
834
- if (options?.actionType != null)
835
- params.set("action_type", String(options.actionType));
1662
+ if (options?.actionType != null) params.set("action_type", String(options.actionType));
836
1663
  const qs = params.toString();
837
- const url = import_types6.Routes.guildAuditLogs(this.id) + (qs ? `?${qs}` : "");
1664
+ const url = import_types10.Routes.guildAuditLogs(this.id) + (qs ? `?${qs}` : "");
838
1665
  return this.client.rest.get(url);
839
1666
  }
840
1667
  /** Fetch all webhooks in this guild. Returned webhooks do not include the token (cannot send). */
841
1668
  async fetchWebhooks() {
842
1669
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
843
- const data = await this.client.rest.get(import_types6.Routes.guildWebhooks(this.id));
1670
+ const data = await this.client.rest.get(import_types10.Routes.guildWebhooks(this.id));
844
1671
  const list = Array.isArray(data) ? data : Object.values(data ?? {});
845
1672
  return list.map((w) => new Webhook2(this.client, w));
846
1673
  }
1674
+ /**
1675
+ * Create a channel in this guild.
1676
+ * @param data - Channel data: type (0=text, 2=voice, 4=category, 5=link), name, and optional parent_id, topic, bitrate, user_limit, nsfw, permission_overwrites
1677
+ * Requires Manage Channels permission.
1678
+ */
1679
+ async createChannel(data) {
1680
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1681
+ const created = await this.client.rest.post(import_types10.Routes.guildChannels(this.id), {
1682
+ body: data,
1683
+ auth: true
1684
+ });
1685
+ const channel = Channel2.from(this.client, created);
1686
+ if (channel) {
1687
+ this.client.channels.set(channel.id, channel);
1688
+ this.channels.set(channel.id, channel);
1689
+ }
1690
+ return channel;
1691
+ }
1692
+ /**
1693
+ * Fetch all channels in this guild.
1694
+ * @returns Array of GuildChannel objects (cached in guild.channels and client.channels)
1695
+ */
1696
+ async fetchChannels() {
1697
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1698
+ const data = await this.client.rest.get(import_types10.Routes.guildChannels(this.id));
1699
+ const list = Array.isArray(data) ? data : Object.values(data ?? {});
1700
+ const channels = [];
1701
+ for (const ch of list) {
1702
+ const channel = Channel2.from(this.client, ch);
1703
+ if (channel) {
1704
+ this.client.channels.set(channel.id, channel);
1705
+ this.channels.set(channel.id, channel);
1706
+ channels.push(channel);
1707
+ }
1708
+ }
1709
+ return channels;
1710
+ }
1711
+ /**
1712
+ * Update channel positions.
1713
+ * @param updates - Array of { id, position?, parent_id?, lock_permissions? }
1714
+ * Requires Manage Channels permission.
1715
+ */
1716
+ async setChannelPositions(updates) {
1717
+ await this.client.rest.patch(import_types10.Routes.guildChannels(this.id), {
1718
+ body: updates,
1719
+ auth: true
1720
+ });
1721
+ }
847
1722
  };
848
1723
  }
849
1724
  });
850
1725
 
851
1726
  // src/structures/User.ts
852
- var import_types8, User;
1727
+ var import_types12, User;
853
1728
  var init_User = __esm({
854
1729
  "src/structures/User.ts"() {
855
1730
  "use strict";
856
1731
  init_Base();
857
- import_types8 = require("@fluxerjs/types");
1732
+ import_types12 = require("@fluxerjs/types");
858
1733
  init_Constants();
859
1734
  User = class extends Base {
860
1735
  client;
@@ -864,6 +1739,14 @@ var init_User = __esm({
864
1739
  globalName;
865
1740
  avatar;
866
1741
  bot;
1742
+ /** RGB avatar color (e.g. 7577782). Null if not set. */
1743
+ avatarColor;
1744
+ /** Public flags bitfield. Null if not set. */
1745
+ flags;
1746
+ /** Whether this is an official system user. */
1747
+ system;
1748
+ /** Banner hash (from profile, member, or invite context). Null when not available. */
1749
+ banner;
867
1750
  /** @param data - API user from message author, GET /users/{id}, or GET /users/@me */
868
1751
  constructor(client, data) {
869
1752
  super();
@@ -874,6 +1757,10 @@ var init_User = __esm({
874
1757
  this.globalName = data.global_name ?? null;
875
1758
  this.avatar = data.avatar ?? null;
876
1759
  this.bot = !!data.bot;
1760
+ this.avatarColor = data.avatar_color ?? null;
1761
+ this.flags = data.flags ?? data.public_flags ?? null;
1762
+ this.system = !!data.system;
1763
+ this.banner = data.banner ?? null;
877
1764
  }
878
1765
  /** Update mutable fields from fresh API data. Used by getOrCreateUser cache. */
879
1766
  _patch(data) {
@@ -881,14 +1768,18 @@ var init_User = __esm({
881
1768
  this.discriminator = data.discriminator;
882
1769
  this.globalName = data.global_name ?? null;
883
1770
  this.avatar = data.avatar ?? null;
1771
+ if (data.avatar_color !== void 0) this.avatarColor = data.avatar_color;
1772
+ if (data.flags !== void 0) this.flags = data.flags;
1773
+ if (data.banner !== void 0) this.banner = data.banner;
884
1774
  }
885
1775
  /**
886
1776
  * Get the URL for this user's avatar.
887
- * @param options - Optional `size` and `extension` (default: `png`)
1777
+ * Auto-detects animated avatars (hash starting with `a_`) and uses gif extension.
1778
+ * @param options - Optional `size` and `extension` (default: png, or gif for animated)
888
1779
  */
889
1780
  avatarURL(options) {
890
1781
  if (!this.avatar) return null;
891
- const ext = options?.extension ?? "png";
1782
+ const ext = this.avatar.startsWith("a_") ? "gif" : options?.extension ?? "png";
892
1783
  const size = options?.size ? `?size=${options.size}` : "";
893
1784
  return `${CDN_URL}/avatars/${this.id}/${this.avatar}.${ext}${size}`;
894
1785
  }
@@ -896,6 +1787,16 @@ var init_User = __esm({
896
1787
  displayAvatarURL(options) {
897
1788
  return this.avatarURL(options) ?? `${CDN_URL}/avatars/0/0.png`;
898
1789
  }
1790
+ /**
1791
+ * Get the URL for this user's banner.
1792
+ * Returns null if the user has no banner (only available when fetched from profile/member context).
1793
+ */
1794
+ bannerURL(options) {
1795
+ if (!this.banner) return null;
1796
+ const ext = this.banner.startsWith("a_") ? "gif" : options?.extension ?? "png";
1797
+ const size = options?.size ? `?size=${options.size}` : "";
1798
+ return `${CDN_URL}/banners/${this.id}/${this.banner}.${ext}${size}`;
1799
+ }
899
1800
  /** Returns a mention string (e.g. `<@123456>`). */
900
1801
  toString() {
901
1802
  return `<@${this.id}>`;
@@ -906,7 +1807,7 @@ var init_User = __esm({
906
1807
  */
907
1808
  async createDM() {
908
1809
  const { DMChannel: DMChannelClass } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
909
- const data = await this.client.rest.post(import_types8.Routes.userMeChannels(), {
1810
+ const data = await this.client.rest.post(import_types12.Routes.userMeChannels(), {
910
1811
  body: { recipient_id: this.id },
911
1812
  auth: true
912
1813
  });
@@ -929,10 +1830,14 @@ var MessageReaction_exports = {};
929
1830
  __export(MessageReaction_exports, {
930
1831
  MessageReaction: () => MessageReaction
931
1832
  });
932
- var MessageReaction;
1833
+ var import_types13, import_rest4, MessageReaction;
933
1834
  var init_MessageReaction = __esm({
934
1835
  "src/structures/MessageReaction.ts"() {
935
1836
  "use strict";
1837
+ import_types13 = require("@fluxerjs/types");
1838
+ import_rest4 = require("@fluxerjs/rest");
1839
+ init_FluxerError();
1840
+ init_ErrorCodes();
936
1841
  init_Base();
937
1842
  MessageReaction = class extends Base {
938
1843
  client;
@@ -962,112 +1867,25 @@ var init_MessageReaction = __esm({
962
1867
  /**
963
1868
  * Fetch the message this reaction belongs to.
964
1869
  * Use when you need to edit, delete, or otherwise interact with the message.
1870
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
965
1871
  */
966
1872
  async fetchMessage() {
967
- return this.client.channels.fetchMessage(this.channelId, this.messageId);
968
- }
969
- };
970
- }
971
- });
972
-
973
- // src/structures/GuildBan.ts
974
- var GuildBan_exports = {};
975
- __export(GuildBan_exports, {
976
- GuildBan: () => GuildBan
977
- });
978
- var import_types9, GuildBan;
979
- var init_GuildBan = __esm({
980
- "src/structures/GuildBan.ts"() {
981
- "use strict";
982
- init_Base();
983
- import_types9 = require("@fluxerjs/types");
984
- GuildBan = class extends Base {
985
- client;
986
- guildId;
987
- user;
988
- reason;
989
- /** @param data - API ban from GET /guilds/{id}/bans or gateway GUILD_BAN_ADD */
990
- constructor(client, data, guildId) {
991
- super();
992
- this.client = client;
993
- this.guildId = data.guild_id ?? guildId;
994
- this.user = client.getOrCreateUser(data.user);
995
- this.reason = data.reason ?? null;
996
- }
997
- /**
998
- * Remove this ban (unban the user).
999
- * Requires Ban Members permission.
1000
- */
1001
- async unban() {
1002
- await this.client.rest.delete(import_types9.Routes.guildBan(this.guildId, this.user.id), {
1003
- auth: true
1004
- });
1005
- }
1006
- };
1007
- }
1008
- });
1009
-
1010
- // src/structures/Invite.ts
1011
- var Invite_exports = {};
1012
- __export(Invite_exports, {
1013
- Invite: () => Invite
1014
- });
1015
- var import_types10, Invite;
1016
- var init_Invite = __esm({
1017
- "src/structures/Invite.ts"() {
1018
- "use strict";
1019
- init_Base();
1020
- import_types10 = require("@fluxerjs/types");
1021
- Invite = class extends Base {
1022
- client;
1023
- code;
1024
- type;
1025
- guild;
1026
- channel;
1027
- inviter;
1028
- memberCount;
1029
- presenceCount;
1030
- expiresAt;
1031
- temporary;
1032
- createdAt;
1033
- uses;
1034
- maxUses;
1035
- maxAge;
1036
- /** @param data - API invite from GET /invites/{code}, channel/guild invite list, or gateway INVITE_CREATE */
1037
- constructor(client, data) {
1038
- super();
1039
- this.client = client;
1040
- this.code = data.code;
1041
- this.type = data.type;
1042
- this.guild = data.guild;
1043
- this.channel = data.channel;
1044
- this.inviter = data.inviter ? client.getOrCreateUser(data.inviter) : null;
1045
- this.memberCount = data.member_count ?? null;
1046
- this.presenceCount = data.presence_count ?? null;
1047
- this.expiresAt = data.expires_at ?? null;
1048
- this.temporary = data.temporary ?? null;
1049
- this.createdAt = data.created_at ?? null;
1050
- this.uses = data.uses ?? null;
1051
- this.maxUses = data.max_uses ?? null;
1052
- this.maxAge = data.max_age ?? null;
1053
- }
1054
- /** Full invite URL (https://fluxer.gg/{code} or instance-specific). */
1055
- get url() {
1056
- return `https://fluxer.gg/${this.code}`;
1057
- }
1058
- /**
1059
- * Resolve the guild from cache if available.
1060
- * @returns The guild, or null if not cached
1061
- */
1062
- getGuild() {
1063
- return this.guild?.id ? this.client.guilds.get(this.guild.id) ?? null : null;
1064
- }
1065
- /**
1066
- * Delete this invite.
1067
- * Requires Manage Guild or Create Instant Invite permission.
1068
- */
1069
- async delete() {
1070
- await this.client.rest.delete(import_types10.Routes.invite(this.code), { auth: true });
1873
+ try {
1874
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1875
+ const data = await this.client.rest.get(
1876
+ import_types13.Routes.channelMessage(this.channelId, this.messageId)
1877
+ );
1878
+ return new Message2(this.client, data);
1879
+ } catch (err) {
1880
+ if (err instanceof import_rest4.RateLimitError) throw err;
1881
+ if (err instanceof import_rest4.FluxerAPIError && err.statusCode === 404) {
1882
+ throw new FluxerError(
1883
+ `Message ${this.messageId} not found in channel ${this.channelId}`,
1884
+ { code: ErrorCodes.MessageNotFound, cause: err }
1885
+ );
1886
+ }
1887
+ throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
1888
+ }
1071
1889
  }
1072
1890
  };
1073
1891
  }
@@ -1078,15 +1896,40 @@ var ClientUser_exports = {};
1078
1896
  __export(ClientUser_exports, {
1079
1897
  ClientUser: () => ClientUser
1080
1898
  });
1081
- var ClientUser;
1899
+ var import_types14, ClientUser;
1082
1900
  var init_ClientUser = __esm({
1083
1901
  "src/client/ClientUser.ts"() {
1084
1902
  "use strict";
1085
1903
  init_User();
1904
+ import_types14 = require("@fluxerjs/types");
1086
1905
  ClientUser = class extends User {
1087
1906
  constructor(client, data) {
1088
1907
  super(client, { ...data });
1089
1908
  }
1909
+ /**
1910
+ * Fetch guilds the bot is a member of.
1911
+ * @returns Array of Guild objects (cached in client.guilds)
1912
+ */
1913
+ async fetchGuilds() {
1914
+ const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1915
+ const data = await this.client.rest.get(import_types14.Routes.currentUserGuilds());
1916
+ const list = Array.isArray(data) ? data : data?.guilds ?? [];
1917
+ const guilds = [];
1918
+ for (const g of list) {
1919
+ const guild = new Guild2(this.client, g);
1920
+ this.client.guilds.set(guild.id, guild);
1921
+ guilds.push(guild);
1922
+ }
1923
+ return guilds;
1924
+ }
1925
+ /**
1926
+ * Leave a guild. Requires the bot to be a member.
1927
+ * @param guildId - The guild ID to leave
1928
+ */
1929
+ async leaveGuild(guildId) {
1930
+ await this.client.rest.delete(import_types14.Routes.leaveGuild(guildId), { auth: true });
1931
+ this.client.guilds.delete(guildId);
1932
+ }
1090
1933
  };
1091
1934
  }
1092
1935
  });
@@ -1094,7 +1937,7 @@ var init_ClientUser = __esm({
1094
1937
  // src/index.ts
1095
1938
  var index_exports = {};
1096
1939
  __export(index_exports, {
1097
- AttachmentBuilder: () => import_builders2.AttachmentBuilder,
1940
+ AttachmentBuilder: () => import_builders3.AttachmentBuilder,
1098
1941
  Base: () => Base,
1099
1942
  CategoryChannel: () => CategoryChannel,
1100
1943
  Channel: () => Channel,
@@ -1102,11 +1945,11 @@ __export(index_exports, {
1102
1945
  Client: () => Client,
1103
1946
  ClientUser: () => ClientUser,
1104
1947
  DMChannel: () => DMChannel,
1105
- EmbedBuilder: () => import_builders2.EmbedBuilder,
1948
+ EmbedBuilder: () => import_builders3.EmbedBuilder,
1106
1949
  ErrorCodes: () => ErrorCodes,
1107
1950
  Events: () => Events,
1108
1951
  FluxerError: () => FluxerError,
1109
- GatewayOpcodes: () => import_types14.GatewayOpcodes,
1952
+ GatewayOpcodes: () => import_types18.GatewayOpcodes,
1110
1953
  Guild: () => Guild,
1111
1954
  GuildBan: () => GuildBan,
1112
1955
  GuildChannel: () => GuildChannel,
@@ -1116,29 +1959,46 @@ __export(index_exports, {
1116
1959
  Invite: () => Invite,
1117
1960
  LinkChannel: () => LinkChannel,
1118
1961
  Message: () => Message,
1962
+ MessageAttachmentFlags: () => import_types18.MessageAttachmentFlags,
1963
+ MessageCollector: () => MessageCollector,
1119
1964
  MessageManager: () => MessageManager,
1120
- MessagePayload: () => import_builders2.MessagePayload,
1965
+ MessagePayload: () => import_builders3.MessagePayload,
1121
1966
  MessageReaction: () => MessageReaction,
1967
+ PermissionFlags: () => import_util9.PermissionFlags,
1968
+ PermissionsBitField: () => import_util9.PermissionsBitField,
1969
+ ReactionCollector: () => ReactionCollector,
1122
1970
  Role: () => Role,
1123
- Routes: () => import_types14.Routes,
1971
+ Routes: () => import_types18.Routes,
1124
1972
  TextChannel: () => TextChannel,
1125
1973
  User: () => User,
1126
1974
  VoiceChannel: () => VoiceChannel,
1127
- Webhook: () => Webhook
1975
+ Webhook: () => Webhook,
1976
+ cdnAvatarURL: () => cdnAvatarURL,
1977
+ cdnBannerURL: () => cdnBannerURL,
1978
+ cdnDefaultAvatarURL: () => cdnDefaultAvatarURL,
1979
+ cdnDisplayAvatarURL: () => cdnDisplayAvatarURL,
1980
+ cdnMemberAvatarURL: () => cdnMemberAvatarURL,
1981
+ cdnMemberBannerURL: () => cdnMemberBannerURL,
1982
+ resolveTenorToImageUrl: () => import_util8.resolveTenorToImageUrl
1128
1983
  });
1129
1984
  module.exports = __toCommonJS(index_exports);
1130
1985
 
1131
1986
  // src/client/Client.ts
1132
- var import_events = require("events");
1133
- var import_rest = require("@fluxerjs/rest");
1987
+ var import_events3 = require("events");
1988
+ var import_rest5 = require("@fluxerjs/rest");
1134
1989
  var import_ws = require("@fluxerjs/ws");
1135
- var import_types11 = require("@fluxerjs/types");
1136
- var import_collection5 = require("@fluxerjs/collection");
1990
+ var import_types15 = require("@fluxerjs/types");
1991
+ var import_collection7 = require("@fluxerjs/collection");
1137
1992
 
1138
1993
  // src/client/ChannelManager.ts
1139
- var import_collection2 = require("@fluxerjs/collection");
1140
- var import_types4 = require("@fluxerjs/types");
1141
- var ChannelManager = class extends import_collection2.Collection {
1994
+ var import_collection4 = require("@fluxerjs/collection");
1995
+ var import_types6 = require("@fluxerjs/types");
1996
+ var import_util2 = require("@fluxerjs/util");
1997
+ var import_rest2 = require("@fluxerjs/rest");
1998
+ init_FluxerError();
1999
+ init_ErrorCodes();
2000
+ init_messageUtils();
2001
+ var ChannelManager = class extends import_collection4.Collection {
1142
2002
  constructor(client) {
1143
2003
  super();
1144
2004
  this.client = client;
@@ -1146,7 +2006,8 @@ var ChannelManager = class extends import_collection2.Collection {
1146
2006
  /**
1147
2007
  * Fetch a channel by ID from the API (or return from cache if present).
1148
2008
  * @param channelId - Snowflake of the channel
1149
- * @returns The channel, or null if not found
2009
+ * @returns The channel
2010
+ * @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
1150
2011
  * @example
1151
2012
  * const channel = await client.channels.fetch(channelId);
1152
2013
  * if (channel?.isSendable()) await channel.send('Hello!');
@@ -1157,58 +2018,85 @@ var ChannelManager = class extends import_collection2.Collection {
1157
2018
  try {
1158
2019
  const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1159
2020
  const data = await this.client.rest.get(
1160
- import_types4.Routes.channel(channelId)
2021
+ import_types6.Routes.channel(channelId)
1161
2022
  );
1162
2023
  const channel = Channel2.fromOrCreate(this.client, data);
1163
- if (channel) this.set(channel.id, channel);
2024
+ if (!channel) {
2025
+ throw new FluxerError("Channel data invalid or unsupported type", {
2026
+ code: ErrorCodes.ChannelNotFound
2027
+ });
2028
+ }
2029
+ this.set(channel.id, channel);
1164
2030
  return channel;
1165
- } catch {
1166
- return null;
2031
+ } catch (err) {
2032
+ if (err instanceof import_rest2.RateLimitError) throw err;
2033
+ if (err instanceof import_rest2.FluxerAPIError && err.statusCode === 404) {
2034
+ throw new FluxerError(`Channel ${channelId} not found`, {
2035
+ code: ErrorCodes.ChannelNotFound,
2036
+ cause: err
2037
+ });
2038
+ }
2039
+ throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
1167
2040
  }
1168
2041
  }
1169
2042
  /**
1170
2043
  * Fetch a message by ID from the API.
1171
2044
  * @param channelId - Snowflake of the channel
1172
2045
  * @param messageId - Snowflake of the message
1173
- * @returns The message, or null if not found
2046
+ * @returns The message
2047
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
1174
2048
  * @deprecated Use channel.messages.fetch(messageId). Prefer (await client.channels.fetch(channelId))?.messages?.fetch(messageId).
1175
2049
  * @example
1176
2050
  * const channel = await client.channels.fetch(channelId);
1177
2051
  * const message = await channel?.messages?.fetch(messageId);
1178
2052
  */
1179
2053
  async fetchMessage(channelId, messageId) {
2054
+ (0, import_util2.emitDeprecationWarning)(
2055
+ "ChannelManager.fetchMessage()",
2056
+ "Use channel.messages.fetch(messageId). Prefer (await client.channels.fetch(channelId))?.messages?.fetch(messageId)."
2057
+ );
1180
2058
  try {
1181
2059
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1182
2060
  const data = await this.client.rest.get(
1183
- import_types4.Routes.channelMessage(channelId, messageId)
2061
+ import_types6.Routes.channelMessage(channelId, messageId)
1184
2062
  );
1185
2063
  return new Message2(this.client, data);
1186
- } catch {
1187
- return null;
2064
+ } catch (err) {
2065
+ if (err instanceof import_rest2.RateLimitError) throw err;
2066
+ if (err instanceof import_rest2.FluxerAPIError && err.statusCode === 404) {
2067
+ throw new FluxerError(`Message ${messageId} not found in channel ${channelId}`, {
2068
+ code: ErrorCodes.MessageNotFound,
2069
+ cause: err
2070
+ });
2071
+ }
2072
+ throw err instanceof FluxerError ? err : new FluxerError(String(err), { cause: err });
1188
2073
  }
1189
2074
  }
1190
2075
  /**
1191
2076
  * Send a message to a channel by ID. Works even when the channel is not cached.
1192
2077
  * Skips the fetch when you only need to send.
1193
2078
  * @param channelId - Snowflake of the channel (text channel or DM)
1194
- * @param payload - Text content or object with content and/or embeds
2079
+ * @param payload - Text content or object with content, embeds, and/or files
1195
2080
  * @returns The created message
1196
2081
  * @example
1197
2082
  * await client.channels.send(logChannelId, 'User joined!');
1198
2083
  * await client.channels.send(channelId, { embeds: [embed.toJSON()] });
2084
+ * await client.channels.send(channelId, { content: 'Report', files: [{ name: 'log.txt', data }] });
1199
2085
  */
1200
2086
  async send(channelId, payload) {
1201
- const body = typeof payload === "string" ? { content: payload } : payload;
2087
+ const opts = typeof payload === "string" ? { content: payload } : payload;
2088
+ const body = buildSendBody(payload);
1202
2089
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1203
- const data = await this.client.rest.post(import_types4.Routes.channelMessages(channelId), { body });
2090
+ const postOptions = opts.files?.length ? { body, files: opts.files } : { body };
2091
+ const data = await this.client.rest.post(import_types6.Routes.channelMessages(channelId), postOptions);
1204
2092
  return new Message2(this.client, data);
1205
2093
  }
1206
2094
  };
1207
2095
 
1208
2096
  // src/client/GuildManager.ts
1209
- var import_collection4 = require("@fluxerjs/collection");
1210
- var import_types7 = require("@fluxerjs/types");
1211
- var GuildManager = class extends import_collection4.Collection {
2097
+ var import_collection6 = require("@fluxerjs/collection");
2098
+ var import_types11 = require("@fluxerjs/types");
2099
+ var GuildManager = class extends import_collection6.Collection {
1212
2100
  constructor(client) {
1213
2101
  super();
1214
2102
  this.client = client;
@@ -1227,7 +2115,7 @@ var GuildManager = class extends import_collection4.Collection {
1227
2115
  try {
1228
2116
  const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1229
2117
  const data = await this.client.rest.get(
1230
- import_types7.Routes.guild(guildId)
2118
+ import_types11.Routes.guild(guildId)
1231
2119
  );
1232
2120
  const guild = new Guild2(this.client, data);
1233
2121
  this.set(guild.id, guild);
@@ -1238,71 +2126,29 @@ var GuildManager = class extends import_collection4.Collection {
1238
2126
  }
1239
2127
  };
1240
2128
 
1241
- // src/errors/FluxerError.ts
1242
- var FluxerError = class _FluxerError extends Error {
1243
- constructor(message) {
1244
- super(message);
1245
- this.name = "FluxerError";
1246
- Object.setPrototypeOf(this, _FluxerError.prototype);
1247
- }
1248
- };
1249
-
1250
- // src/util/Events.ts
1251
- var Events = {
1252
- Ready: "ready",
1253
- MessageCreate: "messageCreate",
1254
- MessageUpdate: "messageUpdate",
1255
- MessageDelete: "messageDelete",
1256
- MessageDeleteBulk: "messageDeleteBulk",
1257
- MessageReactionAdd: "messageReactionAdd",
1258
- MessageReactionRemove: "messageReactionRemove",
1259
- MessageReactionRemoveAll: "messageReactionRemoveAll",
1260
- MessageReactionRemoveEmoji: "messageReactionRemoveEmoji",
1261
- InteractionCreate: "interactionCreate",
1262
- GuildCreate: "guildCreate",
1263
- GuildUpdate: "guildUpdate",
1264
- GuildDelete: "guildDelete",
1265
- GuildBanAdd: "guildBanAdd",
1266
- GuildBanRemove: "guildBanRemove",
1267
- GuildEmojisUpdate: "guildEmojisUpdate",
1268
- GuildStickersUpdate: "guildStickersUpdate",
1269
- GuildIntegrationsUpdate: "guildIntegrationsUpdate",
1270
- GuildMemberAdd: "guildMemberAdd",
1271
- GuildMemberUpdate: "guildMemberUpdate",
1272
- GuildMemberRemove: "guildMemberRemove",
1273
- GuildRoleCreate: "guildRoleCreate",
1274
- GuildRoleUpdate: "guildRoleUpdate",
1275
- GuildRoleDelete: "guildRoleDelete",
1276
- GuildScheduledEventCreate: "guildScheduledEventCreate",
1277
- GuildScheduledEventUpdate: "guildScheduledEventUpdate",
1278
- GuildScheduledEventDelete: "guildScheduledEventDelete",
1279
- ChannelCreate: "channelCreate",
1280
- ChannelUpdate: "channelUpdate",
1281
- ChannelDelete: "channelDelete",
1282
- ChannelPinsUpdate: "channelPinsUpdate",
1283
- InviteCreate: "inviteCreate",
1284
- InviteDelete: "inviteDelete",
1285
- TypingStart: "typingStart",
1286
- UserUpdate: "userUpdate",
1287
- PresenceUpdate: "presenceUpdate",
1288
- VoiceStateUpdate: "voiceStateUpdate",
1289
- VoiceServerUpdate: "voiceServerUpdate",
1290
- VoiceStatesSync: "voiceStatesSync",
1291
- WebhooksUpdate: "webhooksUpdate",
1292
- Resumed: "resumed",
1293
- Error: "error",
1294
- Debug: "debug"
1295
- };
1296
-
1297
2129
  // src/client/Client.ts
1298
- var import_util2 = require("@fluxerjs/util");
2130
+ init_FluxerError();
2131
+ init_ErrorCodes();
2132
+ init_Events();
2133
+ var import_util7 = require("@fluxerjs/util");
1299
2134
  init_User();
1300
2135
 
1301
2136
  // src/client/EventHandlerRegistry.ts
2137
+ init_Events();
1302
2138
  var handlers = /* @__PURE__ */ new Map();
1303
2139
  handlers.set("MESSAGE_CREATE", async (client, d) => {
1304
2140
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1305
- client.emit(Events.MessageCreate, new Message2(client, d));
2141
+ const { GuildMember: GuildMember2 } = await Promise.resolve().then(() => (init_GuildMember(), GuildMember_exports));
2142
+ const data = d;
2143
+ if (data.guild_id && data.member && data.author) {
2144
+ const guild = client.guilds.get(data.guild_id);
2145
+ if (guild) {
2146
+ const memberData = { ...data.member, user: data.author, guild_id: data.guild_id };
2147
+ const member = new GuildMember2(client, memberData, guild);
2148
+ guild.members.set(member.id, member);
2149
+ }
2150
+ }
2151
+ client.emit(Events.MessageCreate, new Message2(client, data));
1306
2152
  });
1307
2153
  handlers.set("MESSAGE_UPDATE", async (client, d) => {
1308
2154
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
@@ -1326,7 +2172,15 @@ handlers.set("MESSAGE_REACTION_ADD", async (client, d) => {
1326
2172
  username: "Unknown",
1327
2173
  discriminator: "0"
1328
2174
  });
1329
- client.emit(Events.MessageReactionAdd, reaction, user);
2175
+ client.emit(
2176
+ Events.MessageReactionAdd,
2177
+ reaction,
2178
+ user,
2179
+ reaction.messageId,
2180
+ reaction.channelId,
2181
+ reaction.emoji,
2182
+ user.id
2183
+ );
1330
2184
  });
1331
2185
  handlers.set("MESSAGE_REACTION_REMOVE", async (client, d) => {
1332
2186
  const data = d;
@@ -1337,7 +2191,15 @@ handlers.set("MESSAGE_REACTION_REMOVE", async (client, d) => {
1337
2191
  username: "Unknown",
1338
2192
  discriminator: "0"
1339
2193
  });
1340
- client.emit(Events.MessageReactionRemove, reaction, user);
2194
+ client.emit(
2195
+ Events.MessageReactionRemove,
2196
+ reaction,
2197
+ user,
2198
+ reaction.messageId,
2199
+ reaction.channelId,
2200
+ reaction.emoji,
2201
+ user.id
2202
+ );
1341
2203
  });
1342
2204
  handlers.set("MESSAGE_REACTION_REMOVE_ALL", async (client, d) => {
1343
2205
  client.emit(Events.MessageReactionRemoveAll, d);
@@ -1351,7 +2213,9 @@ handlers.set("MESSAGE_REACTION_REMOVE_EMOJI", async (client, d) => {
1351
2213
  handlers.set("GUILD_CREATE", async (client, d) => {
1352
2214
  const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1353
2215
  const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1354
- const guild = new Guild2(client, d);
2216
+ const raw = d;
2217
+ const guildData = raw?.properties != null ? { ...raw.properties, roles: raw.roles } : raw;
2218
+ const guild = new Guild2(client, guildData);
1355
2219
  client.guilds.set(guild.id, guild);
1356
2220
  const g = d;
1357
2221
  for (const ch of g.channels ?? []) {
@@ -1365,9 +2229,10 @@ handlers.set("GUILD_CREATE", async (client, d) => {
1365
2229
  });
1366
2230
  handlers.set("GUILD_UPDATE", async (client, d) => {
1367
2231
  const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1368
- const g = d;
1369
- const old = client.guilds.get(g.id);
1370
- const updated = new Guild2(client, g);
2232
+ const raw = d;
2233
+ const guildData = raw?.properties != null ? { ...raw.properties, roles: raw.roles } : raw;
2234
+ const old = client.guilds.get(guildData.id);
2235
+ const updated = new Guild2(client, guildData);
1371
2236
  client.guilds.set(updated.id, updated);
1372
2237
  client.emit(Events.GuildUpdate, old ?? updated, updated);
1373
2238
  });
@@ -1536,11 +2401,23 @@ handlers.set("RESUMED", async (client) => {
1536
2401
  var eventHandlers = handlers;
1537
2402
 
1538
2403
  // src/client/Client.ts
1539
- var Client = class extends import_events.EventEmitter {
2404
+ function createEventMethods(client) {
2405
+ const result = {};
2406
+ for (const key of Object.keys(Events)) {
2407
+ const eventName = Events[key];
2408
+ result[key] = (cb) => {
2409
+ client.on(eventName, cb);
2410
+ return client;
2411
+ };
2412
+ }
2413
+ return result;
2414
+ }
2415
+ var Client = class extends import_events3.EventEmitter {
1540
2416
  /** @param options - Token, REST config, WebSocket, presence, etc. */
1541
2417
  constructor(options = {}) {
1542
2418
  super();
1543
2419
  this.options = options;
2420
+ this.events = createEventMethods(this);
1544
2421
  Object.defineProperty(this.channels, "cache", {
1545
2422
  get: () => this.channels,
1546
2423
  configurable: true
@@ -1549,7 +2426,7 @@ var Client = class extends import_events.EventEmitter {
1549
2426
  get: () => this.guilds,
1550
2427
  configurable: true
1551
2428
  });
1552
- this.rest = new import_rest.REST({
2429
+ this.rest = new import_rest5.REST({
1553
2430
  api: options.rest?.api ?? "https://api.fluxer.app",
1554
2431
  version: options.rest?.version ?? "1",
1555
2432
  ...options.rest
@@ -1558,8 +2435,12 @@ var Client = class extends import_events.EventEmitter {
1558
2435
  rest;
1559
2436
  guilds = new GuildManager(this);
1560
2437
  channels = new ChannelManager(this);
1561
- users = new import_collection5.Collection();
2438
+ users = new import_collection7.Collection();
2439
+ /** Typed event handlers. Use client.events.MessageReactionAdd((reaction, user, messageId, channelId, emoji, userId) => {...}) or client.on(Events.MessageReactionAdd, ...). */
2440
+ events;
2441
+ /** The authenticated bot user. Null until READY is received. */
1562
2442
  user = null;
2443
+ /** Timestamp when the client became ready. Null until READY is received. */
1563
2444
  readyAt = null;
1564
2445
  _ws = null;
1565
2446
  /**
@@ -1572,16 +2453,19 @@ var Client = class extends import_events.EventEmitter {
1572
2453
  */
1573
2454
  async resolveEmoji(emoji, guildId) {
1574
2455
  if (typeof emoji === "object" && emoji.id) {
1575
- return (0, import_util2.formatEmoji)({ name: emoji.name, id: emoji.id, animated: emoji.animated });
2456
+ return (0, import_util7.formatEmoji)({ name: emoji.name, id: emoji.id, animated: emoji.animated });
1576
2457
  }
1577
- const parsed = (0, import_util2.parseEmoji)(typeof emoji === "string" ? emoji : `:${emoji.name}:`);
2458
+ const parsed = (0, import_util7.parseEmoji)(
2459
+ typeof emoji === "string" ? emoji : emoji.id ? `:${emoji.name}:` : emoji.name
2460
+ );
1578
2461
  if (!parsed) throw new Error("Invalid emoji");
1579
- if (parsed.id) return (0, import_util2.formatEmoji)(parsed);
2462
+ if (parsed.id) return (0, import_util7.formatEmoji)(parsed);
2463
+ if (!/^\w+$/.test(parsed.name)) return encodeURIComponent(parsed.name);
1580
2464
  if (guildId) {
1581
- const emojis = await this.rest.get(import_types11.Routes.guildEmojis(guildId));
2465
+ const emojis = await this.rest.get(import_types15.Routes.guildEmojis(guildId));
1582
2466
  const list = Array.isArray(emojis) ? emojis : Object.values(emojis ?? {});
1583
2467
  const found = list.find((e) => e.name && e.name.toLowerCase() === parsed.name.toLowerCase());
1584
- if (found) return (0, import_util2.formatEmoji)({ ...parsed, id: found.id, animated: found.animated });
2468
+ if (found) return (0, import_util7.formatEmoji)({ ...parsed, id: found.id, animated: found.animated });
1585
2469
  throw new Error(
1586
2470
  `Custom emoji ":${parsed.name}:" not found in guild. Use name:id or <:name:id> format.`
1587
2471
  );
@@ -1597,13 +2481,18 @@ var Client = class extends import_events.EventEmitter {
1597
2481
  * Fetch a message by channel and message ID. Use when you have IDs (e.g. from a DB).
1598
2482
  * @param channelId - Snowflake of the channel
1599
2483
  * @param messageId - Snowflake of the message
1600
- * @returns The message, or null if not found
2484
+ * @returns The message
2485
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
1601
2486
  * @deprecated Use channel.messages.fetch(messageId). For IDs-only: (await client.channels.fetch(channelId))?.messages?.fetch(messageId)
1602
2487
  * @example
1603
2488
  * const channel = await client.channels.fetch(channelId);
1604
2489
  * const message = await channel?.messages?.fetch(messageId);
1605
2490
  */
1606
2491
  async fetchMessage(channelId, messageId) {
2492
+ (0, import_util7.emitDeprecationWarning)(
2493
+ "Client.fetchMessage()",
2494
+ "Use channel.messages.fetch(messageId). For IDs-only: (await client.channels.fetch(channelId))?.messages?.fetch(messageId)"
2495
+ );
1607
2496
  return this.channels.fetchMessage(channelId, messageId);
1608
2497
  }
1609
2498
  /**
@@ -1656,7 +2545,9 @@ var Client = class extends import_events.EventEmitter {
1656
2545
  */
1657
2546
  async login(token) {
1658
2547
  if (this._ws) {
1659
- throw new FluxerError("Client is already logged in. Call destroy() first.");
2548
+ throw new FluxerError("Client is already logged in. Call destroy() first.", {
2549
+ code: ErrorCodes.AlreadyLoggedIn
2550
+ });
1660
2551
  }
1661
2552
  this.rest.setToken(token);
1662
2553
  let intents = this.options.intents ?? 0;
@@ -1693,7 +2584,11 @@ var Client = class extends import_events.EventEmitter {
1693
2584
  const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1694
2585
  this.user = new ClientUser2(this, data.user);
1695
2586
  for (const g of data.guilds ?? []) {
1696
- const guild = new Guild2(this, g);
2587
+ const guildData = g && typeof g === "object" && "properties" in g && g.properties ? {
2588
+ ...g.properties,
2589
+ roles: g.roles
2590
+ } : g;
2591
+ const guild = new Guild2(this, guildData);
1697
2592
  this.guilds.set(guild.id, guild);
1698
2593
  const withCh = g;
1699
2594
  for (const ch of withCh.channels ?? []) {
@@ -1734,7 +2629,7 @@ var Client = class extends import_events.EventEmitter {
1734
2629
  return this.readyAt !== null && this.user !== null;
1735
2630
  }
1736
2631
  static get Routes() {
1737
- return import_types11.Routes;
2632
+ return import_types15.Routes;
1738
2633
  }
1739
2634
  };
1740
2635
 
@@ -1755,7 +2650,7 @@ init_GuildBan();
1755
2650
 
1756
2651
  // src/structures/GuildEmoji.ts
1757
2652
  init_Base();
1758
- var import_types12 = require("@fluxerjs/types");
2653
+ var import_types16 = require("@fluxerjs/types");
1759
2654
  init_Constants();
1760
2655
  var GuildEmoji = class extends Base {
1761
2656
  client;
@@ -1783,7 +2678,7 @@ var GuildEmoji = class extends Base {
1783
2678
  }
1784
2679
  /** Delete this emoji. Requires Manage Emojis and Stickers permission. */
1785
2680
  async delete() {
1786
- await this.client.rest.delete(import_types12.Routes.guildEmoji(this.guildId, this.id), {
2681
+ await this.client.rest.delete(import_types16.Routes.guildEmoji(this.guildId, this.id), {
1787
2682
  auth: true
1788
2683
  });
1789
2684
  }
@@ -1792,7 +2687,7 @@ var GuildEmoji = class extends Base {
1792
2687
  * Requires Manage Emojis and Stickers permission.
1793
2688
  */
1794
2689
  async edit(options) {
1795
- const data = await this.client.rest.patch(import_types12.Routes.guildEmoji(this.guildId, this.id), {
2690
+ const data = await this.client.rest.patch(import_types16.Routes.guildEmoji(this.guildId, this.id), {
1796
2691
  body: options,
1797
2692
  auth: true
1798
2693
  });
@@ -1803,7 +2698,7 @@ var GuildEmoji = class extends Base {
1803
2698
 
1804
2699
  // src/structures/GuildSticker.ts
1805
2700
  init_Base();
1806
- var import_types13 = require("@fluxerjs/types");
2701
+ var import_types17 = require("@fluxerjs/types");
1807
2702
  init_Constants();
1808
2703
  var GuildSticker = class extends Base {
1809
2704
  client;
@@ -1831,7 +2726,7 @@ var GuildSticker = class extends Base {
1831
2726
  }
1832
2727
  /** Delete this sticker. Requires Manage Emojis and Stickers permission. */
1833
2728
  async delete() {
1834
- await this.client.rest.delete(import_types13.Routes.guildSticker(this.guildId, this.id), {
2729
+ await this.client.rest.delete(import_types17.Routes.guildSticker(this.guildId, this.id), {
1835
2730
  auth: true
1836
2731
  });
1837
2732
  }
@@ -1840,7 +2735,7 @@ var GuildSticker = class extends Base {
1840
2735
  * Requires Manage Emojis and Stickers permission.
1841
2736
  */
1842
2737
  async edit(options) {
1843
- const data = await this.client.rest.patch(import_types13.Routes.guildSticker(this.guildId, this.id), {
2738
+ const data = await this.client.rest.patch(import_types17.Routes.guildSticker(this.guildId, this.id), {
1844
2739
  body: options,
1845
2740
  auth: true
1846
2741
  });
@@ -1851,15 +2746,17 @@ var GuildSticker = class extends Base {
1851
2746
  }
1852
2747
  };
1853
2748
 
1854
- // src/errors/ErrorCodes.ts
1855
- var ErrorCodes = {
1856
- ClientNotReady: "CLIENT_NOT_READY",
1857
- InvalidToken: "INVALID_TOKEN"
1858
- };
1859
-
1860
2749
  // src/index.ts
1861
- var import_builders2 = require("@fluxerjs/builders");
1862
- var import_types14 = require("@fluxerjs/types");
2750
+ init_Events();
2751
+ init_MessageCollector();
2752
+ init_ReactionCollector();
2753
+ init_FluxerError();
2754
+ init_ErrorCodes();
2755
+ var import_builders3 = require("@fluxerjs/builders");
2756
+ var import_types18 = require("@fluxerjs/types");
2757
+ var import_util8 = require("@fluxerjs/util");
2758
+ var import_util9 = require("@fluxerjs/util");
2759
+ init_cdn();
1863
2760
  // Annotate the CommonJS export names for ESM import in node:
1864
2761
  0 && (module.exports = {
1865
2762
  AttachmentBuilder,
@@ -1884,13 +2781,25 @@ var import_types14 = require("@fluxerjs/types");
1884
2781
  Invite,
1885
2782
  LinkChannel,
1886
2783
  Message,
2784
+ MessageAttachmentFlags,
2785
+ MessageCollector,
1887
2786
  MessageManager,
1888
2787
  MessagePayload,
1889
2788
  MessageReaction,
2789
+ PermissionFlags,
2790
+ PermissionsBitField,
2791
+ ReactionCollector,
1890
2792
  Role,
1891
2793
  Routes,
1892
2794
  TextChannel,
1893
2795
  User,
1894
2796
  VoiceChannel,
1895
- Webhook
2797
+ Webhook,
2798
+ cdnAvatarURL,
2799
+ cdnBannerURL,
2800
+ cdnDefaultAvatarURL,
2801
+ cdnDisplayAvatarURL,
2802
+ cdnMemberAvatarURL,
2803
+ cdnMemberBannerURL,
2804
+ resolveTenorToImageUrl
1896
2805
  });