@fluxerjs/core 1.0.9 → 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 (33) hide show
  1. package/dist/{Channel-ICWNKXBR.mjs → Channel-WJZZSNML.mjs} +3 -1
  2. package/dist/{ClientUser-WWXUMO5O.mjs → ClientUser-DJO2FS7P.mjs} +1 -1
  3. package/dist/Guild-2P77HBQM.mjs +11 -0
  4. package/dist/{GuildBan-M4PA3HAA.mjs → GuildBan-7CXLTPKY.mjs} +1 -1
  5. package/dist/GuildMember-43B5E5CH.mjs +9 -0
  6. package/dist/Message-OFIVTTAZ.mjs +9 -0
  7. package/dist/{MessageReaction-XRPYZDSC.mjs → MessageReaction-V4UZ7OXE.mjs} +1 -1
  8. package/dist/{Role-SVLWIAMN.mjs → Role-5MWSGL66.mjs} +1 -1
  9. package/dist/Webhook-RWDDYW2Q.mjs +10 -0
  10. package/dist/chunk-4XJIM6SC.mjs +315 -0
  11. package/dist/chunk-AH7KYH2Z.mjs +50 -0
  12. package/dist/{chunk-HBF5QEDH.mjs → chunk-CEABHTAF.mjs} +1 -0
  13. package/dist/{chunk-53Y37KRG.mjs → chunk-CJVQNARM.mjs} +44 -10
  14. package/dist/chunk-DQ4TNBPG.mjs +63 -0
  15. package/dist/chunk-DUQAD7F6.mjs +173 -0
  16. package/dist/{chunk-GCIJYVRC.mjs → chunk-JHNKZIHY.mjs} +54 -3
  17. package/dist/{chunk-FJS5FBXO.mjs → chunk-LU2SNC5G.mjs} +172 -13
  18. package/dist/chunk-PM2IUGNR.mjs +29 -0
  19. package/dist/{chunk-RCP27MRC.mjs → chunk-UXIF75BV.mjs} +3 -0
  20. package/dist/{chunk-DSPSRPHF.mjs → chunk-V7LPVPGH.mjs} +123 -18
  21. package/dist/chunk-X6K3ZD62.mjs +53 -0
  22. package/dist/index.d.mts +603 -113
  23. package/dist/index.d.ts +603 -113
  24. package/dist/index.js +1278 -410
  25. package/dist/index.mjs +183 -124
  26. package/package.json +7 -7
  27. package/dist/Guild-TM6YGJWB.mjs +0 -10
  28. package/dist/GuildMember-RZWZ3OCG.mjs +0 -7
  29. package/dist/Message-6IYEYSV6.mjs +0 -7
  30. package/dist/Webhook-32VJD4AL.mjs +0 -7
  31. package/dist/chunk-GFUJVQ7L.mjs +0 -64
  32. package/dist/chunk-SQVCCSNN.mjs +0 -41
  33. package/dist/chunk-X77DFNE3.mjs +0 -136
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,116 +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 });
89
- }
90
- /**
91
- * Edit this webhook. With token: name and avatar only. Without token (bot auth): name, avatar, and channel_id.
92
- * @param options - Fields to update (name, avatar, channel_id when using bot auth)
93
- * @returns This webhook instance with updated fields
94
- */
95
- async edit(options) {
96
- const body = {};
97
- if (options.name !== void 0) body.name = options.name;
98
- if (options.avatar !== void 0) body.avatar = options.avatar;
99
- if ("channel_id" in options && options.channel_id !== void 0 && !this.token) {
100
- body.channel_id = options.channel_id;
101
- }
102
- if (this.token) {
103
- const data2 = await this.client.rest.patch(
104
- import_types.Routes.webhookExecute(this.id, this.token),
105
- { body, auth: false }
106
- );
107
- const w2 = data2;
108
- this.name = w2.name ?? this.name;
109
- this.avatar = w2.avatar ?? null;
110
- return this;
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);
111
330
  }
112
- const data = await this.client.rest.patch(import_types.Routes.webhook(this.id), {
113
- body,
114
- auth: true
115
- });
116
- const w = data;
117
- this.name = w.name ?? this.name;
118
- this.avatar = w.avatar ?? null;
119
- this.channelId = w.channel_id ?? this.channelId;
120
- return this;
121
331
  }
122
- /**
123
- * Send a message via this webhook. Requires the webhook token (only present when created, not when fetched).
124
- * @throws Error if token is not available
125
- */
126
- async send(options) {
127
- if (!this.token) {
128
- throw new Error(
129
- "Webhook token is required to send. The token is only returned when creating a webhook; fetched webhooks cannot send."
130
- );
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;
131
339
  }
132
- const body = typeof options === "string" ? { content: options } : options;
133
- await this.client.rest.post(import_types.Routes.webhookExecute(this.id, this.token), {
134
- body,
135
- auth: false
136
- });
340
+ this.emit("end", this.collected, reason);
137
341
  }
138
- /**
139
- * Fetch a webhook by ID using bot auth.
140
- * @param client - The client instance
141
- * @param webhookId - The webhook ID
142
- * @returns Webhook without token (cannot send)
143
- */
144
- static async fetch(client, webhookId) {
145
- const data = await client.rest.get(import_types.Routes.webhook(webhookId));
146
- return new _Webhook(client, data);
342
+ on(event, listener) {
343
+ return super.on(event, listener);
147
344
  }
148
- /**
149
- * Create a Webhook instance from an ID and token (e.g. from a stored webhook URL).
150
- * @param client - The client instance
151
- * @param webhookId - The webhook ID
152
- * @param token - The webhook token (from createWebhook or stored)
153
- * @param options - Optional channelId, guildId, name for display
154
- */
155
- static fromToken(client, webhookId, token, options) {
156
- return new _Webhook(client, {
157
- id: webhookId,
158
- guild_id: options?.guildId ?? "",
159
- channel_id: options?.channelId ?? "",
160
- name: options?.name ?? "Webhook",
161
- avatar: null,
162
- token,
163
- user: { id: "", username: "webhook", discriminator: "0" }
164
- });
345
+ emit(event, ...args) {
346
+ return super.emit(event, ...args);
165
347
  }
166
348
  };
167
349
  }
@@ -172,14 +354,16 @@ var Message_exports = {};
172
354
  __export(Message_exports, {
173
355
  Message: () => Message
174
356
  });
175
- var import_collection, import_types2, import_builders, Message;
357
+ var import_collection3, import_types, import_builders2, Message;
176
358
  var init_Message = __esm({
177
359
  "src/structures/Message.ts"() {
178
360
  "use strict";
179
361
  init_Base();
180
- import_collection = require("@fluxerjs/collection");
181
- import_types2 = require("@fluxerjs/types");
182
- 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();
183
367
  Message = class _Message extends Base {
184
368
  client;
185
369
  id;
@@ -202,6 +386,14 @@ var init_Message = __esm({
202
386
  messageSnapshots;
203
387
  call;
204
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;
205
397
  /** Channel where this message was sent. Resolved from cache; null if not cached (e.g. DM channel not in cache). */
206
398
  get channel() {
207
399
  return this.client.channels.get(this.channelId) ?? null;
@@ -222,9 +414,9 @@ var init_Message = __esm({
222
414
  this.createdAt = new Date(data.timestamp);
223
415
  this.editedAt = data.edited_timestamp ? new Date(data.edited_timestamp) : null;
224
416
  this.pinned = data.pinned;
225
- this.attachments = new import_collection.Collection();
417
+ this.attachments = new import_collection3.Collection();
226
418
  for (const a of data.attachments ?? []) this.attachments.set(a.id, a);
227
- this.type = data.type ?? import_types2.MessageType.Default;
419
+ this.type = data.type ?? import_types.MessageType.Default;
228
420
  this.flags = data.flags ?? 0;
229
421
  this.mentionEveryone = data.mention_everyone ?? false;
230
422
  this.tts = data.tts ?? false;
@@ -235,17 +427,24 @@ var init_Message = __esm({
235
427
  this.messageSnapshots = data.message_snapshots ?? [];
236
428
  this.call = data.call ?? null;
237
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;
238
434
  }
239
435
  /**
240
436
  * Send a message to this channel without replying. Use when you want a standalone message.
241
- * @param options - Text content or object with content and/or embeds
437
+ * @param options - Text content or object with content, embeds, and/or files
242
438
  * @example
243
439
  * await message.send('Pong!');
244
440
  * await message.send({ embeds: [embed.toJSON()] });
441
+ * await message.send({ content: 'File', files: [{ name: 'data.txt', data }] });
245
442
  */
246
443
  async send(options) {
247
- const body = typeof options === "string" ? { content: options } : options;
248
- const data = await this.client.rest.post(import_types2.Routes.channelMessages(this.channelId), { body });
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);
249
448
  return new _Message(this.client, data);
250
449
  }
251
450
  /**
@@ -261,25 +460,21 @@ var init_Message = __esm({
261
460
  }
262
461
  /**
263
462
  * Reply to this message.
264
- * @param options - Text content or object with content and/or embeds
463
+ * @param options - Text content or object with content, embeds, and/or files
265
464
  */
266
465
  async reply(options) {
267
- const body = typeof options === "string" ? {
268
- content: options,
269
- message_reference: {
270
- channel_id: this.channelId,
271
- message_id: this.id,
272
- guild_id: this.guildId ?? void 0
273
- }
274
- } : {
275
- ...options,
466
+ const opts = typeof options === "string" ? { content: options } : options;
467
+ const base = buildSendBody(options);
468
+ const body = {
469
+ ...base,
276
470
  message_reference: {
277
471
  channel_id: this.channelId,
278
472
  message_id: this.id,
279
473
  guild_id: this.guildId ?? void 0
280
474
  }
281
475
  };
282
- 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);
283
478
  return new _Message(this.client, data);
284
479
  }
285
480
  /**
@@ -290,36 +485,49 @@ var init_Message = __esm({
290
485
  const body = {};
291
486
  if (options.content !== void 0) body.content = options.content;
292
487
  if (options.embeds?.length) {
293
- 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);
294
489
  }
295
- 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), {
296
491
  body
297
492
  });
298
493
  return new _Message(this.client, data);
299
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
+ }
300
507
  /**
301
508
  * Re-fetch this message from the API to get the latest content, embeds, reactions, etc.
302
509
  * Use when you have a stale Message (e.g. from an old event or cache) and need fresh data.
303
- * @returns The updated message, or null if deleted or not found
510
+ * @returns The updated message
511
+ * @throws FluxerError with MESSAGE_NOT_FOUND if the message was deleted or does not exist
304
512
  * @example
305
513
  * const updated = await message.fetch();
306
- * if (updated) console.log('Latest content:', updated.content);
514
+ * console.log('Latest content:', updated.content);
307
515
  */
308
516
  async fetch() {
309
517
  return this.client.channels.fetchMessage(this.channelId, this.id);
310
518
  }
311
519
  /** Delete this message. */
312
520
  async delete() {
313
- 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));
314
522
  }
315
523
  /** Pin this message to the channel. Requires Manage Messages permission. */
316
524
  async pin() {
317
- await this.client.rest.put(import_types2.Routes.channelPinMessage(this.channelId, this.id));
525
+ await this.client.rest.put(import_types.Routes.channelPinMessage(this.channelId, this.id));
318
526
  this.pinned = true;
319
527
  }
320
528
  /** Unpin this message from the channel. Requires Manage Messages permission. */
321
529
  async unpin() {
322
- await this.client.rest.delete(import_types2.Routes.channelPinMessage(this.channelId, this.id));
530
+ await this.client.rest.delete(import_types.Routes.channelPinMessage(this.channelId, this.id));
323
531
  this.pinned = false;
324
532
  }
325
533
  /**
@@ -339,7 +547,7 @@ var init_Message = __esm({
339
547
  */
340
548
  async react(emoji) {
341
549
  const emojiStr = await this.resolveEmojiForReaction(emoji);
342
- 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`;
343
551
  await this.client.rest.put(route);
344
552
  }
345
553
  /**
@@ -349,7 +557,7 @@ var init_Message = __esm({
349
557
  */
350
558
  async removeReaction(emoji, userId) {
351
559
  const emojiStr = await this.resolveEmojiForReaction(emoji);
352
- 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"}`;
353
561
  await this.client.rest.delete(route);
354
562
  }
355
563
  /**
@@ -357,7 +565,7 @@ var init_Message = __esm({
357
565
  * Requires moderator permissions.
358
566
  */
359
567
  async removeAllReactions() {
360
- 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));
361
569
  }
362
570
  /**
363
571
  * Remove all reactions of a specific emoji from this message.
@@ -365,7 +573,235 @@ var init_Message = __esm({
365
573
  */
366
574
  async removeReactionEmoji(emoji) {
367
575
  const emojiStr = await this.resolveEmojiForReaction(emoji);
368
- 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 });
369
805
  }
370
806
  };
371
807
  }
@@ -382,13 +818,16 @@ __export(Channel_exports, {
382
818
  TextChannel: () => TextChannel,
383
819
  VoiceChannel: () => VoiceChannel
384
820
  });
385
- var import_types3, Channel, GuildChannel, TextChannel, CategoryChannel, VoiceChannel, LinkChannel, DMChannel;
821
+ var import_types4, import_util, Channel, GuildChannel, TextChannel, CategoryChannel, VoiceChannel, LinkChannel, DMChannel;
386
822
  var init_Channel = __esm({
387
823
  "src/structures/Channel.ts"() {
388
824
  "use strict";
389
825
  init_MessageManager();
826
+ init_MessageCollector();
390
827
  init_Base();
391
- import_types3 = require("@fluxerjs/types");
828
+ init_messageUtils();
829
+ import_types4 = require("@fluxerjs/types");
830
+ import_util = require("@fluxerjs/util");
392
831
  Channel = class _Channel extends Base {
393
832
  /** Whether this channel has a send method (TextChannel, DMChannel). */
394
833
  isSendable() {
@@ -396,7 +835,7 @@ var init_Channel = __esm({
396
835
  }
397
836
  /** Whether this channel is a DM or Group DM. */
398
837
  isDM() {
399
- 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;
400
839
  }
401
840
  /** Whether this channel is voice-based (VoiceChannel). */
402
841
  isVoice() {
@@ -409,12 +848,21 @@ var init_Channel = __esm({
409
848
  client;
410
849
  id;
411
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;
412
857
  /** @param data - API channel from GET /channels/{id} or GET /guilds/{id}/channels */
413
858
  constructor(client, data) {
414
859
  super();
415
860
  this.client = client;
416
861
  this.id = data.id;
417
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;
418
866
  }
419
867
  /**
420
868
  * Create the appropriate channel subclass from API data.
@@ -423,10 +871,10 @@ var init_Channel = __esm({
423
871
  */
424
872
  static from(client, data) {
425
873
  const type = data.type ?? 0;
426
- if (type === import_types3.ChannelType.GuildText) return new TextChannel(client, data);
427
- if (type === import_types3.ChannelType.GuildCategory) return new CategoryChannel(client, data);
428
- if (type === import_types3.ChannelType.GuildVoice) return new VoiceChannel(client, data);
429
- if (type === import_types3.ChannelType.GuildLink || type === import_types3.ChannelType.GuildLinkExtended)
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)
430
878
  return new LinkChannel(client, data);
431
879
  return new GuildChannel(client, data);
432
880
  }
@@ -436,22 +884,41 @@ var init_Channel = __esm({
436
884
  */
437
885
  static fromOrCreate(client, data) {
438
886
  const type = data.type ?? 0;
439
- if (type === import_types3.ChannelType.DM || type === import_types3.ChannelType.GroupDM)
887
+ if (type === import_types4.ChannelType.DM || type === import_types4.ChannelType.GroupDM)
440
888
  return _Channel.createDM(client, data);
441
889
  return _Channel.from(client, data);
442
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
+ }
443
907
  };
444
908
  GuildChannel = class extends Channel {
445
909
  guildId;
446
910
  name;
447
911
  position;
448
912
  parentId;
913
+ /** Permission overwrites for roles and members. */
914
+ permissionOverwrites;
449
915
  constructor(client, data) {
450
916
  super(client, data);
451
917
  this.guildId = data.guild_id ?? "";
452
918
  this.name = data.name ?? null;
453
919
  this.position = data.position;
454
920
  this.parentId = data.parent_id ?? null;
921
+ this.permissionOverwrites = data.permission_overwrites ?? [];
455
922
  }
456
923
  /**
457
924
  * Create a webhook in this channel.
@@ -460,7 +927,7 @@ var init_Channel = __esm({
460
927
  */
461
928
  async createWebhook(options) {
462
929
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
463
- const data = await this.client.rest.post(import_types3.Routes.channelWebhooks(this.id), {
930
+ const data = await this.client.rest.post(import_types4.Routes.channelWebhooks(this.id), {
464
931
  body: options,
465
932
  auth: true
466
933
  });
@@ -472,10 +939,38 @@ var init_Channel = __esm({
472
939
  */
473
940
  async fetchWebhooks() {
474
941
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
475
- const data = await this.client.rest.get(import_types3.Routes.channelWebhooks(this.id));
942
+ const data = await this.client.rest.get(import_types4.Routes.channelWebhooks(this.id));
476
943
  const list = Array.isArray(data) ? data : Object.values(data ?? {});
477
944
  return list.map((w) => new Webhook2(this.client, w));
478
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
+ }
479
974
  };
480
975
  TextChannel = class extends GuildChannel {
481
976
  topic;
@@ -491,25 +986,39 @@ var init_Channel = __esm({
491
986
  }
492
987
  /**
493
988
  * Send a message to this channel.
494
- * @param options - Text content or object with `content` and/or `embeds`
989
+ * @param options - Text content or object with content, embeds, and/or files
495
990
  */
496
991
  async send(options) {
497
- const body = typeof options === "string" ? { content: options } : options;
992
+ const opts = typeof options === "string" ? { content: options } : options;
993
+ const body = buildSendBody(options);
498
994
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
499
- const data = await this.client.rest.post(import_types3.Routes.channelMessages(this.id), { body });
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);
500
997
  return new Message2(this.client, data);
501
998
  }
502
999
  /** Message manager for this channel. Use channel.messages.fetch(messageId). */
503
1000
  get messages() {
504
1001
  return new MessageManager(this.client, this.id);
505
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
+ }
506
1015
  /**
507
1016
  * Fetch pinned messages in this channel.
508
1017
  * @returns Pinned messages
509
1018
  */
510
1019
  async fetchPinnedMessages() {
511
1020
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
512
- const data = await this.client.rest.get(import_types3.Routes.channelPins(this.id));
1021
+ const data = await this.client.rest.get(import_types4.Routes.channelPins(this.id));
513
1022
  const list = Array.isArray(data) ? data : data?.items ?? [];
514
1023
  return list.map((item) => {
515
1024
  const msg = typeof item === "object" && item && "message" in item ? item.message : item;
@@ -523,6 +1032,10 @@ var init_Channel = __esm({
523
1032
  * @deprecated Use channel.messages.fetch(messageId) instead.
524
1033
  */
525
1034
  async fetchMessage(messageId) {
1035
+ (0, import_util.emitDeprecationWarning)(
1036
+ "Channel.fetchMessage()",
1037
+ "Use channel.messages.fetch(messageId) instead."
1038
+ );
526
1039
  return this.client.channels.fetchMessage(this.id, messageId);
527
1040
  }
528
1041
  };
@@ -548,31 +1061,51 @@ var init_Channel = __esm({
548
1061
  };
549
1062
  DMChannel = class extends Channel {
550
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;
551
1070
  constructor(client, data) {
552
1071
  super(client, data);
553
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 ?? {};
554
1078
  }
555
1079
  /**
556
1080
  * Send a message to this DM channel.
557
- * @param options - Text content or object with `content` and/or `embeds`
1081
+ * @param options - Text content or object with content, embeds, and/or files
558
1082
  */
559
1083
  async send(options) {
560
- const body = typeof options === "string" ? { content: options } : options;
1084
+ const opts = typeof options === "string" ? { content: options } : options;
1085
+ const body = buildSendBody(options);
561
1086
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
562
- const data = await this.client.rest.post(import_types3.Routes.channelMessages(this.id), { body });
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);
563
1089
  return new Message2(this.client, data);
564
1090
  }
565
1091
  /** Message manager for this channel. Use channel.messages.fetch(messageId). */
566
1092
  get messages() {
567
1093
  return new MessageManager(this.client, this.id);
568
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
+ }
569
1102
  /**
570
1103
  * Fetch pinned messages in this DM channel.
571
1104
  * @returns Pinned messages
572
1105
  */
573
1106
  async fetchPinnedMessages() {
574
1107
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
575
- const data = await this.client.rest.get(import_types3.Routes.channelPins(this.id));
1108
+ const data = await this.client.rest.get(import_types4.Routes.channelPins(this.id));
576
1109
  const list = Array.isArray(data) ? data : data?.items ?? [];
577
1110
  return list.map((item) => {
578
1111
  const msg = typeof item === "object" && item && "message" in item ? item.message : item;
@@ -586,23 +1119,57 @@ var init_Channel = __esm({
586
1119
  * @deprecated Use channel.messages.fetch(messageId) instead.
587
1120
  */
588
1121
  async fetchMessage(messageId) {
1122
+ (0, import_util.emitDeprecationWarning)(
1123
+ "Channel.fetchMessage()",
1124
+ "Use channel.messages.fetch(messageId) instead."
1125
+ );
589
1126
  return this.client.channels.fetchMessage(this.id, messageId);
590
1127
  }
591
1128
  };
592
1129
  }
593
1130
  });
594
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
+
595
1159
  // src/structures/GuildMember.ts
596
1160
  var GuildMember_exports = {};
597
1161
  __export(GuildMember_exports, {
598
1162
  GuildMember: () => GuildMember
599
1163
  });
600
- var import_types5, GuildMember;
1164
+ var import_util4, import_types7, GuildMember;
601
1165
  var init_GuildMember = __esm({
602
1166
  "src/structures/GuildMember.ts"() {
603
1167
  "use strict";
604
1168
  init_Base();
605
- import_types5 = require("@fluxerjs/types");
1169
+ import_util4 = require("@fluxerjs/util");
1170
+ import_types7 = require("@fluxerjs/types");
1171
+ init_cdn();
1172
+ init_permissions();
606
1173
  GuildMember = class extends Base {
607
1174
  client;
608
1175
  id;
@@ -640,21 +1207,102 @@ var init_GuildMember = __esm({
640
1207
  get displayName() {
641
1208
  return this.nick ?? this.user.globalName ?? this.user.username;
642
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
+ }
643
1231
  /**
644
1232
  * Add a role to this member.
645
1233
  * @param roleId - The role ID to add
646
1234
  * Requires Manage Roles permission.
647
1235
  */
648
1236
  async addRole(roleId) {
649
- 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));
650
1238
  }
651
1239
  /**
652
1240
  * Remove a role from this member.
653
1241
  * @param roleId - The role ID to remove
654
1242
  * Requires Manage Roles permission.
655
1243
  */
656
- async removeRole(roleId) {
657
- await this.client.rest.delete(import_types5.Routes.guildMemberRole(this.guild.id, this.id, roleId));
1244
+ async removeRole(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;
658
1306
  }
659
1307
  };
660
1308
  }
@@ -665,11 +1313,12 @@ var Role_exports = {};
665
1313
  __export(Role_exports, {
666
1314
  Role: () => Role
667
1315
  });
668
- var Role;
1316
+ var import_util5, Role;
669
1317
  var init_Role = __esm({
670
1318
  "src/structures/Role.ts"() {
671
1319
  "use strict";
672
1320
  init_Base();
1321
+ import_util5 = require("@fluxerjs/util");
673
1322
  Role = class extends Base {
674
1323
  client;
675
1324
  id;
@@ -681,6 +1330,8 @@ var init_Role = __esm({
681
1330
  hoist;
682
1331
  mentionable;
683
1332
  unicodeEmoji;
1333
+ /** Separately sorted position for hoisted roles. Null if not set. */
1334
+ hoistPosition;
684
1335
  /** @param client - The client instance */
685
1336
  /** @param data - API role from GET /guilds/{id}/roles or gateway role events */
686
1337
  /** @param guildId - The guild this role belongs to */
@@ -696,21 +1347,71 @@ var init_Role = __esm({
696
1347
  this.hoist = !!data.hoist;
697
1348
  this.mentionable = !!data.mentionable;
698
1349
  this.unicodeEmoji = data.unicode_emoji ?? null;
1350
+ this.hoistPosition = data.hoist_position ?? null;
699
1351
  }
700
1352
  /** Returns a mention string (e.g. `<@&123456>`). */
701
1353
  toString() {
702
1354
  return `<@&${this.id}>`;
703
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
+ }
704
1374
  };
705
1375
  }
706
1376
  });
707
1377
 
708
- // src/util/Constants.ts
709
- var CDN_URL;
710
- var init_Constants = __esm({
711
- "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"() {
712
1386
  "use strict";
713
- 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
+ };
714
1415
  }
715
1416
  });
716
1417
 
@@ -719,17 +1420,20 @@ var Guild_exports = {};
719
1420
  __export(Guild_exports, {
720
1421
  Guild: () => Guild
721
1422
  });
722
- var import_util, import_collection3, import_types6, Guild;
1423
+ var import_util6, import_rest2, import_collection5, import_types9, Guild;
723
1424
  var init_Guild = __esm({
724
1425
  "src/structures/Guild.ts"() {
725
1426
  "use strict";
726
- import_util = require("@fluxerjs/util");
1427
+ import_util6 = require("@fluxerjs/util");
1428
+ import_rest2 = require("@fluxerjs/rest");
727
1429
  init_Base();
728
- import_collection3 = require("@fluxerjs/collection");
1430
+ init_FluxerError();
1431
+ init_ErrorCodes();
1432
+ import_collection5 = require("@fluxerjs/collection");
729
1433
  init_GuildMember();
730
1434
  init_Role();
731
1435
  init_Constants();
732
- import_types6 = require("@fluxerjs/types");
1436
+ import_types9 = require("@fluxerjs/types");
733
1437
  Guild = class extends Base {
734
1438
  client;
735
1439
  id;
@@ -737,9 +1441,36 @@ var init_Guild = __esm({
737
1441
  icon;
738
1442
  banner;
739
1443
  ownerId;
740
- members = new import_collection3.Collection();
741
- channels = new import_collection3.Collection();
742
- 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();
743
1474
  /** @param data - API guild from GET /guilds/{id} or gateway GUILD_CREATE */
744
1475
  constructor(client, data) {
745
1476
  super();
@@ -748,7 +1479,23 @@ var init_Guild = __esm({
748
1479
  this.name = data.name;
749
1480
  this.icon = data.icon ?? null;
750
1481
  this.banner = data.banner ?? null;
751
- 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;
752
1499
  for (const r of data.roles ?? []) {
753
1500
  this.roles.set(r.id, new Role(client, r, this.id));
754
1501
  }
@@ -765,6 +1512,12 @@ var init_Guild = __esm({
765
1512
  const size = options?.size ? `?size=${options.size}` : "";
766
1513
  return `${CDN_URL}/banners/${this.id}/${this.banner}.png${size}`;
767
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
+ }
768
1521
  /**
769
1522
  * Add a role to a member by user ID. Does not require fetching the member first.
770
1523
  * @param userId - The user ID of the member
@@ -772,7 +1525,7 @@ var init_Guild = __esm({
772
1525
  * Requires Manage Roles permission.
773
1526
  */
774
1527
  async addRoleToMember(userId, roleId) {
775
- 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));
776
1529
  }
777
1530
  /**
778
1531
  * Remove a role from a member by user ID. Does not require fetching the member first.
@@ -781,7 +1534,7 @@ var init_Guild = __esm({
781
1534
  * Requires Manage Roles permission.
782
1535
  */
783
1536
  async removeRoleFromMember(userId, roleId) {
784
- 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));
785
1538
  }
786
1539
  /**
787
1540
  * Resolve a role ID from an argument (role mention, raw ID, or name).
@@ -790,14 +1543,14 @@ var init_Guild = __esm({
790
1543
  * @returns The role ID, or null if not found
791
1544
  */
792
1545
  async resolveRoleId(arg) {
793
- const parsed = (0, import_util.parseRoleMention)(arg);
1546
+ const parsed = (0, import_util6.parseRoleMention)(arg);
794
1547
  if (parsed) return parsed;
795
1548
  if (/^\d{17,19}$/.test(arg.trim())) return arg.trim();
796
1549
  const cached = this.roles.find(
797
1550
  (r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase())
798
1551
  );
799
1552
  if (cached) return cached.id;
800
- 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));
801
1554
  const list = Array.isArray(roles) ? roles : Object.values(roles ?? {});
802
1555
  const role = list.find((r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase()));
803
1556
  if (role) {
@@ -806,19 +1559,75 @@ var init_Guild = __esm({
806
1559
  }
807
1560
  return null;
808
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
+ }
809
1607
  /**
810
1608
  * Fetch a guild member by user ID.
811
1609
  * @param userId - The user ID of the member to fetch
812
- * @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
813
1613
  */
814
1614
  async fetchMember(userId) {
815
1615
  try {
816
1616
  const data = await this.client.rest.get(
817
- import_types6.Routes.guildMember(this.id, userId)
1617
+ import_types9.Routes.guildMember(this.id, userId)
818
1618
  );
819
- return new GuildMember(this.client, { ...data, guild_id: this.id }, this);
820
- } catch {
821
- 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 });
822
1631
  }
823
1632
  }
824
1633
  /**
@@ -831,30 +1640,77 @@ var init_Guild = __esm({
831
1640
  if (options?.before) params.set("before", options.before);
832
1641
  if (options?.after) params.set("after", options.after);
833
1642
  if (options?.userId) params.set("user_id", options.userId);
834
- if (options?.actionType != null)
835
- params.set("action_type", String(options.actionType));
1643
+ if (options?.actionType != null) params.set("action_type", String(options.actionType));
836
1644
  const qs = params.toString();
837
- const url = import_types6.Routes.guildAuditLogs(this.id) + (qs ? `?${qs}` : "");
1645
+ const url = import_types9.Routes.guildAuditLogs(this.id) + (qs ? `?${qs}` : "");
838
1646
  return this.client.rest.get(url);
839
1647
  }
840
1648
  /** Fetch all webhooks in this guild. Returned webhooks do not include the token (cannot send). */
841
1649
  async fetchWebhooks() {
842
1650
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
843
- const data = await this.client.rest.get(import_types6.Routes.guildWebhooks(this.id));
1651
+ const data = await this.client.rest.get(import_types9.Routes.guildWebhooks(this.id));
844
1652
  const list = Array.isArray(data) ? data : Object.values(data ?? {});
845
1653
  return list.map((w) => new Webhook2(this.client, w));
846
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
+ }
847
1703
  };
848
1704
  }
849
1705
  });
850
1706
 
851
1707
  // src/structures/User.ts
852
- var import_types8, User;
1708
+ var import_types11, User;
853
1709
  var init_User = __esm({
854
1710
  "src/structures/User.ts"() {
855
1711
  "use strict";
856
1712
  init_Base();
857
- import_types8 = require("@fluxerjs/types");
1713
+ import_types11 = require("@fluxerjs/types");
858
1714
  init_Constants();
859
1715
  User = class extends Base {
860
1716
  client;
@@ -864,6 +1720,14 @@ var init_User = __esm({
864
1720
  globalName;
865
1721
  avatar;
866
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;
867
1731
  /** @param data - API user from message author, GET /users/{id}, or GET /users/@me */
868
1732
  constructor(client, data) {
869
1733
  super();
@@ -874,6 +1738,10 @@ var init_User = __esm({
874
1738
  this.globalName = data.global_name ?? null;
875
1739
  this.avatar = data.avatar ?? null;
876
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;
877
1745
  }
878
1746
  /** Update mutable fields from fresh API data. Used by getOrCreateUser cache. */
879
1747
  _patch(data) {
@@ -881,14 +1749,18 @@ var init_User = __esm({
881
1749
  this.discriminator = data.discriminator;
882
1750
  this.globalName = data.global_name ?? null;
883
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;
884
1755
  }
885
1756
  /**
886
1757
  * Get the URL for this user's avatar.
887
- * @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)
888
1760
  */
889
1761
  avatarURL(options) {
890
1762
  if (!this.avatar) return null;
891
- const ext = options?.extension ?? "png";
1763
+ const ext = this.avatar.startsWith("a_") ? "gif" : options?.extension ?? "png";
892
1764
  const size = options?.size ? `?size=${options.size}` : "";
893
1765
  return `${CDN_URL}/avatars/${this.id}/${this.avatar}.${ext}${size}`;
894
1766
  }
@@ -896,6 +1768,16 @@ var init_User = __esm({
896
1768
  displayAvatarURL(options) {
897
1769
  return this.avatarURL(options) ?? `${CDN_URL}/avatars/0/0.png`;
898
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
+ }
899
1781
  /** Returns a mention string (e.g. `<@123456>`). */
900
1782
  toString() {
901
1783
  return `<@${this.id}>`;
@@ -906,7 +1788,7 @@ var init_User = __esm({
906
1788
  */
907
1789
  async createDM() {
908
1790
  const { DMChannel: DMChannelClass } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
909
- const data = await this.client.rest.post(import_types8.Routes.userMeChannels(), {
1791
+ const data = await this.client.rest.post(import_types11.Routes.userMeChannels(), {
910
1792
  body: { recipient_id: this.id },
911
1793
  auth: true
912
1794
  });
@@ -962,6 +1844,7 @@ var init_MessageReaction = __esm({
962
1844
  /**
963
1845
  * Fetch the message this reaction belongs to.
964
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
965
1848
  */
966
1849
  async fetchMessage() {
967
1850
  return this.client.channels.fetchMessage(this.channelId, this.messageId);
@@ -970,123 +1853,45 @@ var init_MessageReaction = __esm({
970
1853
  }
971
1854
  });
972
1855
 
973
- // src/structures/GuildBan.ts
974
- var GuildBan_exports = {};
975
- __export(GuildBan_exports, {
976
- GuildBan: () => GuildBan
977
- });
978
- var import_types9, GuildBan;
979
- var init_GuildBan = __esm({
980
- "src/structures/GuildBan.ts"() {
981
- "use strict";
982
- init_Base();
983
- import_types9 = require("@fluxerjs/types");
984
- GuildBan = class extends Base {
985
- client;
986
- guildId;
987
- user;
988
- reason;
989
- /** @param data - API ban from GET /guilds/{id}/bans or gateway GUILD_BAN_ADD */
990
- constructor(client, data, guildId) {
991
- super();
992
- this.client = client;
993
- this.guildId = data.guild_id ?? guildId;
994
- this.user = client.getOrCreateUser(data.user);
995
- this.reason = data.reason ?? null;
996
- }
997
- /**
998
- * Remove this ban (unban the user).
999
- * Requires Ban Members permission.
1000
- */
1001
- async unban() {
1002
- await this.client.rest.delete(import_types9.Routes.guildBan(this.guildId, this.user.id), {
1003
- auth: true
1004
- });
1005
- }
1006
- };
1007
- }
1008
- });
1009
-
1010
- // src/structures/Invite.ts
1011
- var Invite_exports = {};
1012
- __export(Invite_exports, {
1013
- Invite: () => Invite
1014
- });
1015
- var import_types10, Invite;
1016
- var init_Invite = __esm({
1017
- "src/structures/Invite.ts"() {
1018
- "use strict";
1019
- init_Base();
1020
- import_types10 = require("@fluxerjs/types");
1021
- Invite = class extends Base {
1022
- client;
1023
- code;
1024
- type;
1025
- guild;
1026
- channel;
1027
- inviter;
1028
- memberCount;
1029
- presenceCount;
1030
- expiresAt;
1031
- temporary;
1032
- createdAt;
1033
- uses;
1034
- maxUses;
1035
- maxAge;
1036
- /** @param data - API invite from GET /invites/{code}, channel/guild invite list, or gateway INVITE_CREATE */
1037
- constructor(client, data) {
1038
- super();
1039
- this.client = client;
1040
- this.code = data.code;
1041
- this.type = data.type;
1042
- this.guild = data.guild;
1043
- this.channel = data.channel;
1044
- this.inviter = data.inviter ? client.getOrCreateUser(data.inviter) : null;
1045
- this.memberCount = data.member_count ?? null;
1046
- this.presenceCount = data.presence_count ?? null;
1047
- this.expiresAt = data.expires_at ?? null;
1048
- this.temporary = data.temporary ?? null;
1049
- this.createdAt = data.created_at ?? null;
1050
- this.uses = data.uses ?? null;
1051
- this.maxUses = data.max_uses ?? null;
1052
- this.maxAge = data.max_age ?? null;
1053
- }
1054
- /** Full invite URL (https://fluxer.gg/{code} or instance-specific). */
1055
- get url() {
1056
- return `https://fluxer.gg/${this.code}`;
1057
- }
1058
- /**
1059
- * Resolve the guild from cache if available.
1060
- * @returns The guild, or null if not cached
1061
- */
1062
- getGuild() {
1063
- return this.guild?.id ? this.client.guilds.get(this.guild.id) ?? null : null;
1064
- }
1065
- /**
1066
- * Delete this invite.
1067
- * Requires Manage Guild or Create Instant Invite permission.
1068
- */
1069
- async delete() {
1070
- await this.client.rest.delete(import_types10.Routes.invite(this.code), { auth: true });
1071
- }
1072
- };
1073
- }
1074
- });
1075
-
1076
1856
  // src/client/ClientUser.ts
1077
1857
  var ClientUser_exports = {};
1078
1858
  __export(ClientUser_exports, {
1079
1859
  ClientUser: () => ClientUser
1080
1860
  });
1081
- var ClientUser;
1861
+ var import_types12, ClientUser;
1082
1862
  var init_ClientUser = __esm({
1083
1863
  "src/client/ClientUser.ts"() {
1084
1864
  "use strict";
1085
1865
  init_User();
1866
+ import_types12 = require("@fluxerjs/types");
1086
1867
  ClientUser = class extends User {
1087
1868
  constructor(client, data) {
1088
1869
  super(client, { ...data });
1089
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
+ }
1090
1895
  };
1091
1896
  }
1092
1897
  });
@@ -1094,7 +1899,7 @@ var init_ClientUser = __esm({
1094
1899
  // src/index.ts
1095
1900
  var index_exports = {};
1096
1901
  __export(index_exports, {
1097
- AttachmentBuilder: () => import_builders2.AttachmentBuilder,
1902
+ AttachmentBuilder: () => import_builders3.AttachmentBuilder,
1098
1903
  Base: () => Base,
1099
1904
  CategoryChannel: () => CategoryChannel,
1100
1905
  Channel: () => Channel,
@@ -1102,11 +1907,11 @@ __export(index_exports, {
1102
1907
  Client: () => Client,
1103
1908
  ClientUser: () => ClientUser,
1104
1909
  DMChannel: () => DMChannel,
1105
- EmbedBuilder: () => import_builders2.EmbedBuilder,
1910
+ EmbedBuilder: () => import_builders3.EmbedBuilder,
1106
1911
  ErrorCodes: () => ErrorCodes,
1107
1912
  Events: () => Events,
1108
1913
  FluxerError: () => FluxerError,
1109
- GatewayOpcodes: () => import_types14.GatewayOpcodes,
1914
+ GatewayOpcodes: () => import_types16.GatewayOpcodes,
1110
1915
  Guild: () => Guild,
1111
1916
  GuildBan: () => GuildBan,
1112
1917
  GuildChannel: () => GuildChannel,
@@ -1116,29 +1921,46 @@ __export(index_exports, {
1116
1921
  Invite: () => Invite,
1117
1922
  LinkChannel: () => LinkChannel,
1118
1923
  Message: () => Message,
1924
+ MessageAttachmentFlags: () => import_types16.MessageAttachmentFlags,
1925
+ MessageCollector: () => MessageCollector,
1119
1926
  MessageManager: () => MessageManager,
1120
- MessagePayload: () => import_builders2.MessagePayload,
1927
+ MessagePayload: () => import_builders3.MessagePayload,
1121
1928
  MessageReaction: () => MessageReaction,
1929
+ PermissionFlags: () => import_util9.PermissionFlags,
1930
+ PermissionsBitField: () => import_util9.PermissionsBitField,
1931
+ ReactionCollector: () => ReactionCollector,
1122
1932
  Role: () => Role,
1123
- Routes: () => import_types14.Routes,
1933
+ Routes: () => import_types16.Routes,
1124
1934
  TextChannel: () => TextChannel,
1125
1935
  User: () => User,
1126
1936
  VoiceChannel: () => VoiceChannel,
1127
- 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
1128
1945
  });
1129
1946
  module.exports = __toCommonJS(index_exports);
1130
1947
 
1131
1948
  // src/client/Client.ts
1132
- var import_events = require("events");
1133
- var import_rest = require("@fluxerjs/rest");
1949
+ var import_events3 = require("events");
1950
+ var import_rest3 = require("@fluxerjs/rest");
1134
1951
  var import_ws = require("@fluxerjs/ws");
1135
- var import_types11 = require("@fluxerjs/types");
1136
- var import_collection5 = require("@fluxerjs/collection");
1952
+ var import_types13 = require("@fluxerjs/types");
1953
+ var import_collection7 = require("@fluxerjs/collection");
1137
1954
 
1138
1955
  // src/client/ChannelManager.ts
1139
- var import_collection2 = require("@fluxerjs/collection");
1140
- var import_types4 = require("@fluxerjs/types");
1141
- var ChannelManager = class extends import_collection2.Collection {
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 {
1142
1964
  constructor(client) {
1143
1965
  super();
1144
1966
  this.client = client;
@@ -1146,7 +1968,8 @@ var ChannelManager = class extends import_collection2.Collection {
1146
1968
  /**
1147
1969
  * Fetch a channel by ID from the API (or return from cache if present).
1148
1970
  * @param channelId - Snowflake of the channel
1149
- * @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
1150
1973
  * @example
1151
1974
  * const channel = await client.channels.fetch(channelId);
1152
1975
  * if (channel?.isSendable()) await channel.send('Hello!');
@@ -1157,58 +1980,85 @@ var ChannelManager = class extends import_collection2.Collection {
1157
1980
  try {
1158
1981
  const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1159
1982
  const data = await this.client.rest.get(
1160
- import_types4.Routes.channel(channelId)
1983
+ import_types5.Routes.channel(channelId)
1161
1984
  );
1162
1985
  const channel = Channel2.fromOrCreate(this.client, data);
1163
- 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);
1164
1992
  return channel;
1165
- } catch {
1166
- 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 });
1167
2002
  }
1168
2003
  }
1169
2004
  /**
1170
2005
  * Fetch a message by ID from the API.
1171
2006
  * @param channelId - Snowflake of the channel
1172
2007
  * @param messageId - Snowflake of the message
1173
- * @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
1174
2010
  * @deprecated Use channel.messages.fetch(messageId). Prefer (await client.channels.fetch(channelId))?.messages?.fetch(messageId).
1175
2011
  * @example
1176
2012
  * const channel = await client.channels.fetch(channelId);
1177
2013
  * const message = await channel?.messages?.fetch(messageId);
1178
2014
  */
1179
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
+ );
1180
2020
  try {
1181
2021
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1182
2022
  const data = await this.client.rest.get(
1183
- import_types4.Routes.channelMessage(channelId, messageId)
2023
+ import_types5.Routes.channelMessage(channelId, messageId)
1184
2024
  );
1185
2025
  return new Message2(this.client, data);
1186
- } catch {
1187
- 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 });
1188
2035
  }
1189
2036
  }
1190
2037
  /**
1191
2038
  * Send a message to a channel by ID. Works even when the channel is not cached.
1192
2039
  * Skips the fetch when you only need to send.
1193
2040
  * @param channelId - Snowflake of the channel (text channel or DM)
1194
- * @param payload - Text content or object with content and/or embeds
2041
+ * @param payload - Text content or object with content, embeds, and/or files
1195
2042
  * @returns The created message
1196
2043
  * @example
1197
2044
  * await client.channels.send(logChannelId, 'User joined!');
1198
2045
  * await client.channels.send(channelId, { embeds: [embed.toJSON()] });
2046
+ * await client.channels.send(channelId, { content: 'Report', files: [{ name: 'log.txt', data }] });
1199
2047
  */
1200
2048
  async send(channelId, payload) {
1201
- const body = typeof payload === "string" ? { content: payload } : payload;
2049
+ const opts = typeof payload === "string" ? { content: payload } : payload;
2050
+ const body = buildSendBody(payload);
1202
2051
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1203
- const data = await this.client.rest.post(import_types4.Routes.channelMessages(channelId), { body });
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);
1204
2054
  return new Message2(this.client, data);
1205
2055
  }
1206
2056
  };
1207
2057
 
1208
2058
  // src/client/GuildManager.ts
1209
- var import_collection4 = require("@fluxerjs/collection");
1210
- var import_types7 = require("@fluxerjs/types");
1211
- var GuildManager = class extends import_collection4.Collection {
2059
+ var import_collection6 = require("@fluxerjs/collection");
2060
+ var import_types10 = require("@fluxerjs/types");
2061
+ var GuildManager = class extends import_collection6.Collection {
1212
2062
  constructor(client) {
1213
2063
  super();
1214
2064
  this.client = client;
@@ -1227,7 +2077,7 @@ var GuildManager = class extends import_collection4.Collection {
1227
2077
  try {
1228
2078
  const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1229
2079
  const data = await this.client.rest.get(
1230
- import_types7.Routes.guild(guildId)
2080
+ import_types10.Routes.guild(guildId)
1231
2081
  );
1232
2082
  const guild = new Guild2(this.client, data);
1233
2083
  this.set(guild.id, guild);
@@ -1238,71 +2088,29 @@ var GuildManager = class extends import_collection4.Collection {
1238
2088
  }
1239
2089
  };
1240
2090
 
1241
- // src/errors/FluxerError.ts
1242
- var FluxerError = class _FluxerError extends Error {
1243
- constructor(message) {
1244
- super(message);
1245
- this.name = "FluxerError";
1246
- Object.setPrototypeOf(this, _FluxerError.prototype);
1247
- }
1248
- };
1249
-
1250
- // src/util/Events.ts
1251
- var Events = {
1252
- Ready: "ready",
1253
- MessageCreate: "messageCreate",
1254
- MessageUpdate: "messageUpdate",
1255
- MessageDelete: "messageDelete",
1256
- MessageDeleteBulk: "messageDeleteBulk",
1257
- MessageReactionAdd: "messageReactionAdd",
1258
- MessageReactionRemove: "messageReactionRemove",
1259
- MessageReactionRemoveAll: "messageReactionRemoveAll",
1260
- MessageReactionRemoveEmoji: "messageReactionRemoveEmoji",
1261
- InteractionCreate: "interactionCreate",
1262
- GuildCreate: "guildCreate",
1263
- GuildUpdate: "guildUpdate",
1264
- GuildDelete: "guildDelete",
1265
- GuildBanAdd: "guildBanAdd",
1266
- GuildBanRemove: "guildBanRemove",
1267
- GuildEmojisUpdate: "guildEmojisUpdate",
1268
- GuildStickersUpdate: "guildStickersUpdate",
1269
- GuildIntegrationsUpdate: "guildIntegrationsUpdate",
1270
- GuildMemberAdd: "guildMemberAdd",
1271
- GuildMemberUpdate: "guildMemberUpdate",
1272
- GuildMemberRemove: "guildMemberRemove",
1273
- GuildRoleCreate: "guildRoleCreate",
1274
- GuildRoleUpdate: "guildRoleUpdate",
1275
- GuildRoleDelete: "guildRoleDelete",
1276
- GuildScheduledEventCreate: "guildScheduledEventCreate",
1277
- GuildScheduledEventUpdate: "guildScheduledEventUpdate",
1278
- GuildScheduledEventDelete: "guildScheduledEventDelete",
1279
- ChannelCreate: "channelCreate",
1280
- ChannelUpdate: "channelUpdate",
1281
- ChannelDelete: "channelDelete",
1282
- ChannelPinsUpdate: "channelPinsUpdate",
1283
- InviteCreate: "inviteCreate",
1284
- InviteDelete: "inviteDelete",
1285
- TypingStart: "typingStart",
1286
- UserUpdate: "userUpdate",
1287
- PresenceUpdate: "presenceUpdate",
1288
- VoiceStateUpdate: "voiceStateUpdate",
1289
- VoiceServerUpdate: "voiceServerUpdate",
1290
- VoiceStatesSync: "voiceStatesSync",
1291
- WebhooksUpdate: "webhooksUpdate",
1292
- Resumed: "resumed",
1293
- Error: "error",
1294
- Debug: "debug"
1295
- };
1296
-
1297
2091
  // src/client/Client.ts
1298
- var import_util2 = require("@fluxerjs/util");
2092
+ init_FluxerError();
2093
+ init_ErrorCodes();
2094
+ init_Events();
2095
+ var import_util7 = require("@fluxerjs/util");
1299
2096
  init_User();
1300
2097
 
1301
2098
  // src/client/EventHandlerRegistry.ts
2099
+ init_Events();
1302
2100
  var handlers = /* @__PURE__ */ new Map();
1303
2101
  handlers.set("MESSAGE_CREATE", async (client, d) => {
1304
2102
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1305
- 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));
1306
2114
  });
1307
2115
  handlers.set("MESSAGE_UPDATE", async (client, d) => {
1308
2116
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
@@ -1326,7 +2134,15 @@ handlers.set("MESSAGE_REACTION_ADD", async (client, d) => {
1326
2134
  username: "Unknown",
1327
2135
  discriminator: "0"
1328
2136
  });
1329
- 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
+ );
1330
2146
  });
1331
2147
  handlers.set("MESSAGE_REACTION_REMOVE", async (client, d) => {
1332
2148
  const data = d;
@@ -1337,7 +2153,15 @@ handlers.set("MESSAGE_REACTION_REMOVE", async (client, d) => {
1337
2153
  username: "Unknown",
1338
2154
  discriminator: "0"
1339
2155
  });
1340
- 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
+ );
1341
2165
  });
1342
2166
  handlers.set("MESSAGE_REACTION_REMOVE_ALL", async (client, d) => {
1343
2167
  client.emit(Events.MessageReactionRemoveAll, d);
@@ -1351,7 +2175,9 @@ handlers.set("MESSAGE_REACTION_REMOVE_EMOJI", async (client, d) => {
1351
2175
  handlers.set("GUILD_CREATE", async (client, d) => {
1352
2176
  const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1353
2177
  const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1354
- 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);
1355
2181
  client.guilds.set(guild.id, guild);
1356
2182
  const g = d;
1357
2183
  for (const ch of g.channels ?? []) {
@@ -1365,9 +2191,10 @@ handlers.set("GUILD_CREATE", async (client, d) => {
1365
2191
  });
1366
2192
  handlers.set("GUILD_UPDATE", async (client, d) => {
1367
2193
  const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1368
- const g = d;
1369
- const old = client.guilds.get(g.id);
1370
- const updated = new Guild2(client, g);
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);
1371
2198
  client.guilds.set(updated.id, updated);
1372
2199
  client.emit(Events.GuildUpdate, old ?? updated, updated);
1373
2200
  });
@@ -1536,11 +2363,23 @@ handlers.set("RESUMED", async (client) => {
1536
2363
  var eventHandlers = handlers;
1537
2364
 
1538
2365
  // src/client/Client.ts
1539
- 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 {
1540
2378
  /** @param options - Token, REST config, WebSocket, presence, etc. */
1541
2379
  constructor(options = {}) {
1542
2380
  super();
1543
2381
  this.options = options;
2382
+ this.events = createEventMethods(this);
1544
2383
  Object.defineProperty(this.channels, "cache", {
1545
2384
  get: () => this.channels,
1546
2385
  configurable: true
@@ -1549,7 +2388,7 @@ var Client = class extends import_events.EventEmitter {
1549
2388
  get: () => this.guilds,
1550
2389
  configurable: true
1551
2390
  });
1552
- this.rest = new import_rest.REST({
2391
+ this.rest = new import_rest3.REST({
1553
2392
  api: options.rest?.api ?? "https://api.fluxer.app",
1554
2393
  version: options.rest?.version ?? "1",
1555
2394
  ...options.rest
@@ -1558,8 +2397,12 @@ var Client = class extends import_events.EventEmitter {
1558
2397
  rest;
1559
2398
  guilds = new GuildManager(this);
1560
2399
  channels = new ChannelManager(this);
1561
- 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. */
1562
2404
  user = null;
2405
+ /** Timestamp when the client became ready. Null until READY is received. */
1563
2406
  readyAt = null;
1564
2407
  _ws = null;
1565
2408
  /**
@@ -1572,16 +2415,16 @@ var Client = class extends import_events.EventEmitter {
1572
2415
  */
1573
2416
  async resolveEmoji(emoji, guildId) {
1574
2417
  if (typeof emoji === "object" && emoji.id) {
1575
- 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 });
1576
2419
  }
1577
- 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}:`);
1578
2421
  if (!parsed) throw new Error("Invalid emoji");
1579
- if (parsed.id) return (0, import_util2.formatEmoji)(parsed);
2422
+ if (parsed.id) return (0, import_util7.formatEmoji)(parsed);
1580
2423
  if (guildId) {
1581
- const emojis = await this.rest.get(import_types11.Routes.guildEmojis(guildId));
2424
+ const emojis = await this.rest.get(import_types13.Routes.guildEmojis(guildId));
1582
2425
  const list = Array.isArray(emojis) ? emojis : Object.values(emojis ?? {});
1583
2426
  const found = list.find((e) => e.name && e.name.toLowerCase() === parsed.name.toLowerCase());
1584
- if (found) return (0, import_util2.formatEmoji)({ ...parsed, id: found.id, animated: found.animated });
2427
+ if (found) return (0, import_util7.formatEmoji)({ ...parsed, id: found.id, animated: found.animated });
1585
2428
  throw new Error(
1586
2429
  `Custom emoji ":${parsed.name}:" not found in guild. Use name:id or <:name:id> format.`
1587
2430
  );
@@ -1597,13 +2440,18 @@ var Client = class extends import_events.EventEmitter {
1597
2440
  * Fetch a message by channel and message ID. Use when you have IDs (e.g. from a DB).
1598
2441
  * @param channelId - Snowflake of the channel
1599
2442
  * @param messageId - Snowflake of the message
1600
- * @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
1601
2445
  * @deprecated Use channel.messages.fetch(messageId). For IDs-only: (await client.channels.fetch(channelId))?.messages?.fetch(messageId)
1602
2446
  * @example
1603
2447
  * const channel = await client.channels.fetch(channelId);
1604
2448
  * const message = await channel?.messages?.fetch(messageId);
1605
2449
  */
1606
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
+ );
1607
2455
  return this.channels.fetchMessage(channelId, messageId);
1608
2456
  }
1609
2457
  /**
@@ -1656,7 +2504,9 @@ var Client = class extends import_events.EventEmitter {
1656
2504
  */
1657
2505
  async login(token) {
1658
2506
  if (this._ws) {
1659
- 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
+ });
1660
2510
  }
1661
2511
  this.rest.setToken(token);
1662
2512
  let intents = this.options.intents ?? 0;
@@ -1693,7 +2543,11 @@ var Client = class extends import_events.EventEmitter {
1693
2543
  const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1694
2544
  this.user = new ClientUser2(this, data.user);
1695
2545
  for (const g of data.guilds ?? []) {
1696
- 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);
1697
2551
  this.guilds.set(guild.id, guild);
1698
2552
  const withCh = g;
1699
2553
  for (const ch of withCh.channels ?? []) {
@@ -1734,7 +2588,7 @@ var Client = class extends import_events.EventEmitter {
1734
2588
  return this.readyAt !== null && this.user !== null;
1735
2589
  }
1736
2590
  static get Routes() {
1737
- return import_types11.Routes;
2591
+ return import_types13.Routes;
1738
2592
  }
1739
2593
  };
1740
2594
 
@@ -1755,7 +2609,7 @@ init_GuildBan();
1755
2609
 
1756
2610
  // src/structures/GuildEmoji.ts
1757
2611
  init_Base();
1758
- var import_types12 = require("@fluxerjs/types");
2612
+ var import_types14 = require("@fluxerjs/types");
1759
2613
  init_Constants();
1760
2614
  var GuildEmoji = class extends Base {
1761
2615
  client;
@@ -1783,7 +2637,7 @@ var GuildEmoji = class extends Base {
1783
2637
  }
1784
2638
  /** Delete this emoji. Requires Manage Emojis and Stickers permission. */
1785
2639
  async delete() {
1786
- await this.client.rest.delete(import_types12.Routes.guildEmoji(this.guildId, this.id), {
2640
+ await this.client.rest.delete(import_types14.Routes.guildEmoji(this.guildId, this.id), {
1787
2641
  auth: true
1788
2642
  });
1789
2643
  }
@@ -1792,7 +2646,7 @@ var GuildEmoji = class extends Base {
1792
2646
  * Requires Manage Emojis and Stickers permission.
1793
2647
  */
1794
2648
  async edit(options) {
1795
- const data = await this.client.rest.patch(import_types12.Routes.guildEmoji(this.guildId, this.id), {
2649
+ const data = await this.client.rest.patch(import_types14.Routes.guildEmoji(this.guildId, this.id), {
1796
2650
  body: options,
1797
2651
  auth: true
1798
2652
  });
@@ -1803,7 +2657,7 @@ var GuildEmoji = class extends Base {
1803
2657
 
1804
2658
  // src/structures/GuildSticker.ts
1805
2659
  init_Base();
1806
- var import_types13 = require("@fluxerjs/types");
2660
+ var import_types15 = require("@fluxerjs/types");
1807
2661
  init_Constants();
1808
2662
  var GuildSticker = class extends Base {
1809
2663
  client;
@@ -1831,7 +2685,7 @@ var GuildSticker = class extends Base {
1831
2685
  }
1832
2686
  /** Delete this sticker. Requires Manage Emojis and Stickers permission. */
1833
2687
  async delete() {
1834
- await this.client.rest.delete(import_types13.Routes.guildSticker(this.guildId, this.id), {
2688
+ await this.client.rest.delete(import_types15.Routes.guildSticker(this.guildId, this.id), {
1835
2689
  auth: true
1836
2690
  });
1837
2691
  }
@@ -1840,7 +2694,7 @@ var GuildSticker = class extends Base {
1840
2694
  * Requires Manage Emojis and Stickers permission.
1841
2695
  */
1842
2696
  async edit(options) {
1843
- const data = await this.client.rest.patch(import_types13.Routes.guildSticker(this.guildId, this.id), {
2697
+ const data = await this.client.rest.patch(import_types15.Routes.guildSticker(this.guildId, this.id), {
1844
2698
  body: options,
1845
2699
  auth: true
1846
2700
  });
@@ -1851,15 +2705,17 @@ var GuildSticker = class extends Base {
1851
2705
  }
1852
2706
  };
1853
2707
 
1854
- // src/errors/ErrorCodes.ts
1855
- var ErrorCodes = {
1856
- ClientNotReady: "CLIENT_NOT_READY",
1857
- InvalidToken: "INVALID_TOKEN"
1858
- };
1859
-
1860
2708
  // src/index.ts
1861
- var import_builders2 = require("@fluxerjs/builders");
1862
- var import_types14 = 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();
1863
2719
  // Annotate the CommonJS export names for ESM import in node:
1864
2720
  0 && (module.exports = {
1865
2721
  AttachmentBuilder,
@@ -1884,13 +2740,25 @@ var import_types14 = require("@fluxerjs/types");
1884
2740
  Invite,
1885
2741
  LinkChannel,
1886
2742
  Message,
2743
+ MessageAttachmentFlags,
2744
+ MessageCollector,
1887
2745
  MessageManager,
1888
2746
  MessagePayload,
1889
2747
  MessageReaction,
2748
+ PermissionFlags,
2749
+ PermissionsBitField,
2750
+ ReactionCollector,
1890
2751
  Role,
1891
2752
  Routes,
1892
2753
  TextChannel,
1893
2754
  User,
1894
2755
  VoiceChannel,
1895
- Webhook
2756
+ Webhook,
2757
+ cdnAvatarURL,
2758
+ cdnBannerURL,
2759
+ cdnDefaultAvatarURL,
2760
+ cdnDisplayAvatarURL,
2761
+ cdnMemberAvatarURL,
2762
+ cdnMemberBannerURL,
2763
+ resolveTenorToImageUrl
1896
2764
  });