@fluxerjs/core 1.0.8 → 1.1.0

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