@fluxerjs/core 1.0.6 → 1.0.8

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 (59) hide show
  1. package/dist/{Channel-2WNJ445K.mjs → Channel-TWPDKW2P.mjs} +3 -1
  2. package/dist/{ClientUser-J6HQVSDJ.mjs → ClientUser-2K2BACK7.mjs} +1 -2
  3. package/dist/{Guild-GOQZ7XP4.mjs → Guild-CMZGA6DW.mjs} +3 -1
  4. package/dist/GuildMember-DW2N6ITI.mjs +7 -0
  5. package/dist/{Webhook-NUQCJAWZ.mjs → Message-G2QIKZQK.mjs} +3 -3
  6. package/dist/MessageReaction-XRPYZDSC.mjs +7 -0
  7. package/dist/Role-SVLWIAMN.mjs +7 -0
  8. package/dist/{Webhook-2RHBXH7R.mjs → Webhook-2MQESB7Z.mjs} +1 -1
  9. package/dist/chunk-4GCZFOS5.mjs +86 -0
  10. package/dist/chunk-CO5EL5LH.mjs +168 -0
  11. package/dist/chunk-CZIO2D7F.mjs +207 -0
  12. package/dist/chunk-HBF5QEDH.mjs +42 -0
  13. package/dist/chunk-JVEOQFUX.mjs +52 -0
  14. package/dist/chunk-SQVCCSNN.mjs +41 -0
  15. package/dist/chunk-TJVZEILY.mjs +120 -0
  16. package/dist/{chunk-BUEXP5SZ.mjs → chunk-ZGMM6IPQ.mjs} +12 -3
  17. package/dist/index.d.mts +539 -73
  18. package/dist/index.d.ts +539 -73
  19. package/dist/index.js +1125 -355
  20. package/dist/index.mjs +520 -194
  21. package/package.json +27 -8
  22. package/dist/Channel-HM2UY4DN.mjs +0 -17
  23. package/dist/Channel-IKL3SJXN.mjs +0 -17
  24. package/dist/Channel-KILNV5V3.mjs +0 -17
  25. package/dist/Channel-TOAQGSRX.mjs +0 -17
  26. package/dist/Channel-VENHOL7S.mjs +0 -17
  27. package/dist/ClientUser-RNDKHQ3Z.mjs +0 -9
  28. package/dist/Guild-36EGAAEW.mjs +0 -8
  29. package/dist/Guild-CA3W6DOD.mjs +0 -8
  30. package/dist/Guild-NHNQ5TIA.mjs +0 -8
  31. package/dist/Guild-ZOFF5LFR.mjs +0 -8
  32. package/dist/GuildMember-RGVPVUAG.mjs +0 -9
  33. package/dist/GuildMember-XF7K2R45.mjs +0 -9
  34. package/dist/Message-23Z3RPCZ.mjs +0 -9
  35. package/dist/Message-33APPS76.mjs +0 -9
  36. package/dist/Message-PZUU7ZFR.mjs +0 -9
  37. package/dist/Message-XB5WNMHL.mjs +0 -9
  38. package/dist/chunk-3CNUPFDI.mjs +0 -59
  39. package/dist/chunk-4DBGMFOQ.mjs +0 -14
  40. package/dist/chunk-6CEMF2LO.mjs +0 -14
  41. package/dist/chunk-6EBNOON4.mjs +0 -86
  42. package/dist/chunk-72OY7B3D.mjs +0 -72
  43. package/dist/chunk-7FYM4D2E.mjs +0 -50
  44. package/dist/chunk-7GZN6JXT.mjs +0 -50
  45. package/dist/chunk-7H3TKJUT.mjs +0 -53
  46. package/dist/chunk-EF32ILJL.mjs +0 -102
  47. package/dist/chunk-F2EEQP5O.mjs +0 -86
  48. package/dist/chunk-GUNWHOQO.mjs +0 -42
  49. package/dist/chunk-L25ON7WB.mjs +0 -52
  50. package/dist/chunk-LBBIQOSH.mjs +0 -53
  51. package/dist/chunk-OHIHIQAS.mjs +0 -102
  52. package/dist/chunk-P4IRDGB4.mjs +0 -43
  53. package/dist/chunk-QDCFQF6J.mjs +0 -36
  54. package/dist/chunk-QDNFJVVE.mjs +0 -70
  55. package/dist/chunk-SW6KNICI.mjs +0 -52
  56. package/dist/chunk-TE5IC7IP.mjs +0 -36
  57. package/dist/chunk-WFONGZGK.mjs +0 -42
  58. package/dist/chunk-XXCBJJZE.mjs +0 -88
  59. package/dist/chunk-ZHRQQZ4X.mjs +0 -102
package/dist/index.js CHANGED
@@ -20,123 +20,34 @@ var __copyProps = (to, from, except, desc) => {
20
20
  };
21
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
22
 
23
- // src/structures/Base.ts
24
- var Base;
25
- var init_Base = __esm({
26
- "src/structures/Base.ts"() {
27
- "use strict";
28
- Base = class {
29
- };
30
- }
31
- });
32
-
33
- // src/util/Constants.ts
34
- var CDN_URL;
35
- var init_Constants = __esm({
36
- "src/util/Constants.ts"() {
37
- "use strict";
38
- CDN_URL = "https://fluxerusercontent.com";
39
- }
40
- });
41
-
42
- // src/structures/User.ts
43
- var User;
44
- var init_User = __esm({
45
- "src/structures/User.ts"() {
23
+ // src/client/MessageManager.ts
24
+ var MessageManager;
25
+ var init_MessageManager = __esm({
26
+ "src/client/MessageManager.ts"() {
46
27
  "use strict";
47
- init_Base();
48
- init_Constants();
49
- User = class extends Base {
50
- client;
51
- id;
52
- username;
53
- discriminator;
54
- globalName;
55
- avatar;
56
- bot;
57
- constructor(client, data) {
58
- super();
28
+ MessageManager = class {
29
+ constructor(client, channelId) {
59
30
  this.client = client;
60
- this.id = data.id;
61
- this.username = data.username;
62
- this.discriminator = data.discriminator;
63
- this.globalName = data.global_name ?? null;
64
- this.avatar = data.avatar ?? null;
65
- this.bot = !!data.bot;
66
- }
67
- avatarURL(options) {
68
- if (!this.avatar) return null;
69
- const ext = options?.extension ?? "png";
70
- const size = options?.size ? `?size=${options.size}` : "";
71
- return `${CDN_URL}/avatars/${this.id}/${this.avatar}.${ext}${size}`;
72
- }
73
- displayAvatarURL(options) {
74
- return this.avatarURL(options) ?? `${CDN_URL}/avatars/0/0.png`;
31
+ this.channelId = channelId;
75
32
  }
76
- toString() {
77
- return `<@${this.id}>`;
33
+ /**
34
+ * Fetch a message by ID from this channel.
35
+ * @param messageId - Snowflake of the message
36
+ * @returns The message, or null if not found
37
+ */
38
+ async fetch(messageId) {
39
+ return this.client.channels.fetchMessage(this.channelId, messageId);
78
40
  }
79
41
  };
80
42
  }
81
43
  });
82
44
 
83
- // src/structures/Message.ts
84
- var Message_exports = {};
85
- __export(Message_exports, {
86
- Message: () => Message
87
- });
88
- var import_collection, import_types, import_builders, Message;
89
- var init_Message = __esm({
90
- "src/structures/Message.ts"() {
45
+ // src/structures/Base.ts
46
+ var Base;
47
+ var init_Base = __esm({
48
+ "src/structures/Base.ts"() {
91
49
  "use strict";
92
- init_Base();
93
- import_collection = require("@fluxerjs/collection");
94
- import_types = require("@fluxerjs/types");
95
- import_builders = require("@fluxerjs/builders");
96
- init_User();
97
- Message = class _Message extends Base {
98
- client;
99
- id;
100
- channelId;
101
- guildId;
102
- author;
103
- content;
104
- createdAt;
105
- editedAt;
106
- pinned;
107
- attachments;
108
- channel;
109
- constructor(client, data) {
110
- super();
111
- this.client = client;
112
- this.id = data.id;
113
- this.channelId = data.channel_id;
114
- this.guildId = data.guild_id ?? null;
115
- this.author = new User(client, data.author);
116
- this.content = data.content;
117
- this.createdAt = new Date(data.timestamp);
118
- this.editedAt = data.edited_timestamp ? new Date(data.edited_timestamp) : null;
119
- this.pinned = data.pinned;
120
- this.attachments = new import_collection.Collection();
121
- for (const a of data.attachments ?? []) this.attachments.set(a.id, a);
122
- }
123
- async reply(options) {
124
- const body = typeof options === "string" ? { content: options, message_reference: { channel_id: this.channelId, message_id: this.id, guild_id: this.guildId ?? void 0 } } : { ...options, message_reference: { channel_id: this.channelId, message_id: this.id, guild_id: this.guildId ?? void 0 } };
125
- const data = await this.client.rest.post(import_types.Routes.channelMessages(this.channelId), { body });
126
- return new _Message(this.client, data);
127
- }
128
- async edit(options) {
129
- const body = {};
130
- if (options.content !== void 0) body.content = options.content;
131
- if (options.embeds?.length) {
132
- body.embeds = options.embeds.map((e) => e instanceof import_builders.EmbedBuilder ? e.toJSON() : e);
133
- }
134
- const data = await this.client.rest.patch(import_types.Routes.channelMessage(this.channelId, this.id), { body });
135
- return new _Message(this.client, data);
136
- }
137
- async delete() {
138
- await this.client.rest.delete(import_types.Routes.channelMessage(this.channelId, this.id));
139
- }
50
+ Base = class {
140
51
  };
141
52
  }
142
53
  });
@@ -146,12 +57,12 @@ var Webhook_exports = {};
146
57
  __export(Webhook_exports, {
147
58
  Webhook: () => Webhook
148
59
  });
149
- var import_types2, Webhook;
60
+ var import_types, Webhook;
150
61
  var init_Webhook = __esm({
151
62
  "src/structures/Webhook.ts"() {
152
63
  "use strict";
153
64
  init_Base();
154
- import_types2 = require("@fluxerjs/types");
65
+ import_types = require("@fluxerjs/types");
155
66
  Webhook = class _Webhook extends Base {
156
67
  client;
157
68
  id;
@@ -161,6 +72,7 @@ var init_Webhook = __esm({
161
72
  avatar;
162
73
  /** Present only when webhook was created via createWebhook(); not returned when fetching. */
163
74
  token;
75
+ /** @param data - API webhook from POST /channels/{id}/webhooks (has token) or GET /webhooks/{id} (no token) */
164
76
  constructor(client, data) {
165
77
  super();
166
78
  this.client = client;
@@ -173,7 +85,7 @@ var init_Webhook = __esm({
173
85
  }
174
86
  /** Delete this webhook. Requires bot token with Manage Webhooks permission. */
175
87
  async delete() {
176
- await this.client.rest.delete(import_types2.Routes.webhook(this.id), { auth: true });
88
+ await this.client.rest.delete(import_types.Routes.webhook(this.id), { auth: true });
177
89
  }
178
90
  /**
179
91
  * Send a message via this webhook. Requires the webhook token (only present when created, not when fetched).
@@ -181,24 +93,32 @@ var init_Webhook = __esm({
181
93
  */
182
94
  async send(options) {
183
95
  if (!this.token) {
184
- throw new Error("Webhook token is required to send. The token is only returned when creating a webhook; fetched webhooks cannot send.");
96
+ throw new Error(
97
+ "Webhook token is required to send. The token is only returned when creating a webhook; fetched webhooks cannot send."
98
+ );
185
99
  }
186
100
  const body = typeof options === "string" ? { content: options } : options;
187
- await this.client.rest.post(import_types2.Routes.webhookExecute(this.id, this.token), {
101
+ await this.client.rest.post(import_types.Routes.webhookExecute(this.id, this.token), {
188
102
  body,
189
103
  auth: false
190
104
  });
191
105
  }
192
106
  /**
193
- * Fetch a webhook by ID using bot auth. The returned webhook will not have a token (cannot send).
107
+ * Fetch a webhook by ID using bot auth.
108
+ * @param client - The client instance
109
+ * @param webhookId - The webhook ID
110
+ * @returns Webhook without token (cannot send)
194
111
  */
195
112
  static async fetch(client, webhookId) {
196
- const data = await client.rest.get(import_types2.Routes.webhook(webhookId));
113
+ const data = await client.rest.get(import_types.Routes.webhook(webhookId));
197
114
  return new _Webhook(client, data);
198
115
  }
199
116
  /**
200
117
  * Create a Webhook instance from an ID and token (e.g. from a stored webhook URL).
201
- * Useful when you have the token from a previous createWebhook() call.
118
+ * @param client - The client instance
119
+ * @param webhookId - The webhook ID
120
+ * @param token - The webhook token (from createWebhook or stored)
121
+ * @param options - Optional channelId, guildId, name for display
202
122
  */
203
123
  static fromToken(client, webhookId, token, options) {
204
124
  return new _Webhook(client, {
@@ -215,53 +135,173 @@ var init_Webhook = __esm({
215
135
  }
216
136
  });
217
137
 
218
- // src/structures/Guild.ts
219
- var Guild_exports = {};
220
- __export(Guild_exports, {
221
- Guild: () => Guild
138
+ // src/structures/Message.ts
139
+ var Message_exports = {};
140
+ __export(Message_exports, {
141
+ Message: () => Message
222
142
  });
223
- var import_collection2, import_types3, Guild;
224
- var init_Guild = __esm({
225
- "src/structures/Guild.ts"() {
143
+ var import_collection, import_types2, import_builders, Message;
144
+ var init_Message = __esm({
145
+ "src/structures/Message.ts"() {
226
146
  "use strict";
227
147
  init_Base();
228
- import_collection2 = require("@fluxerjs/collection");
229
- init_Constants();
230
- import_types3 = require("@fluxerjs/types");
231
- Guild = class extends Base {
148
+ import_collection = require("@fluxerjs/collection");
149
+ import_types2 = require("@fluxerjs/types");
150
+ import_builders = require("@fluxerjs/builders");
151
+ Message = class _Message extends Base {
232
152
  client;
233
153
  id;
234
- name;
235
- icon;
236
- banner;
237
- ownerId;
238
- members = new import_collection2.Collection();
239
- channels = new import_collection2.Collection();
154
+ channelId;
155
+ guildId;
156
+ author;
157
+ content;
158
+ createdAt;
159
+ editedAt;
160
+ pinned;
161
+ attachments;
162
+ /** Channel where this message was sent. Resolved from cache; null if not cached (e.g. DM channel not in cache). */
163
+ get channel() {
164
+ return this.client.channels.get(this.channelId) ?? null;
165
+ }
166
+ /** Guild where this message was sent. Resolved from cache; null for DMs or if not cached. */
167
+ get guild() {
168
+ return this.guildId ? this.client.guilds.get(this.guildId) ?? null : null;
169
+ }
170
+ /** @param data - API message from POST/PATCH /channels/{id}/messages or gateway MESSAGE_CREATE */
240
171
  constructor(client, data) {
241
172
  super();
242
173
  this.client = client;
243
174
  this.id = data.id;
244
- this.name = data.name;
245
- this.icon = data.icon ?? null;
246
- this.banner = data.banner ?? null;
247
- this.ownerId = data.owner_id;
175
+ this.channelId = data.channel_id;
176
+ this.guildId = data.guild_id ?? null;
177
+ this.author = client.getOrCreateUser(data.author);
178
+ this.content = data.content;
179
+ this.createdAt = new Date(data.timestamp);
180
+ this.editedAt = data.edited_timestamp ? new Date(data.edited_timestamp) : null;
181
+ this.pinned = data.pinned;
182
+ this.attachments = new import_collection.Collection();
183
+ for (const a of data.attachments ?? []) this.attachments.set(a.id, a);
248
184
  }
249
- iconURL(options) {
250
- if (!this.icon) return null;
251
- const size = options?.size ? `?size=${options.size}` : "";
252
- return `${CDN_URL}/icons/${this.id}/${this.icon}.png${size}`;
185
+ /**
186
+ * Send a message to this channel without replying. Use when you want a standalone message.
187
+ * @param options - Text content or object with content and/or embeds
188
+ * @example
189
+ * await message.send('Pong!');
190
+ * await message.send({ embeds: [embed.toJSON()] });
191
+ */
192
+ async send(options) {
193
+ const body = typeof options === "string" ? { content: options } : options;
194
+ const data = await this.client.rest.post(import_types2.Routes.channelMessages(this.channelId), { body });
195
+ return new _Message(this.client, data);
253
196
  }
254
- bannerURL(options) {
255
- if (!this.banner) return null;
256
- const size = options?.size ? `?size=${options.size}` : "";
257
- return `${CDN_URL}/banners/${this.id}/${this.banner}.png${size}`;
197
+ /**
198
+ * Send a message to a specific channel. Use for logging, forwarding, or sending to another channel in the guild.
199
+ * @param channelId - Snowflake of the target channel (e.g. log channel ID)
200
+ * @param options - Text content or object with content and/or embeds
201
+ * @example
202
+ * await message.sendTo(logChannelId, 'User ' + message.author.username + ' said: ' + message.content);
203
+ * await message.sendTo(announceChannelId, { embeds: [embed.toJSON()] });
204
+ */
205
+ async sendTo(channelId, options) {
206
+ return this.client.channels.send(channelId, options);
258
207
  }
259
- /** Fetch all webhooks in this guild. Returned webhooks do not include the token (cannot send). */
260
- async fetchWebhooks() {
261
- const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
262
- const data = await this.client.rest.get(import_types3.Routes.guildWebhooks(this.id));
263
- const list = Array.isArray(data) ? data : Object.values(data ?? {});
264
- return list.map((w) => new Webhook2(this.client, w));
208
+ /**
209
+ * Reply to this message.
210
+ * @param options - Text content or object with content and/or embeds
211
+ */
212
+ async reply(options) {
213
+ const body = typeof options === "string" ? {
214
+ content: options,
215
+ message_reference: {
216
+ channel_id: this.channelId,
217
+ message_id: this.id,
218
+ guild_id: this.guildId ?? void 0
219
+ }
220
+ } : {
221
+ ...options,
222
+ message_reference: {
223
+ channel_id: this.channelId,
224
+ message_id: this.id,
225
+ guild_id: this.guildId ?? void 0
226
+ }
227
+ };
228
+ const data = await this.client.rest.post(import_types2.Routes.channelMessages(this.channelId), { body });
229
+ return new _Message(this.client, data);
230
+ }
231
+ /**
232
+ * Edit this message. Only the author (or admins) can edit.
233
+ * @param options - New content and/or embeds
234
+ */
235
+ async edit(options) {
236
+ const body = {};
237
+ if (options.content !== void 0) body.content = options.content;
238
+ if (options.embeds?.length) {
239
+ body.embeds = options.embeds.map((e) => e instanceof import_builders.EmbedBuilder ? e.toJSON() : e);
240
+ }
241
+ const data = await this.client.rest.patch(import_types2.Routes.channelMessage(this.channelId, this.id), {
242
+ body
243
+ });
244
+ return new _Message(this.client, data);
245
+ }
246
+ /**
247
+ * Re-fetch this message from the API to get the latest content, embeds, reactions, etc.
248
+ * Use when you have a stale Message (e.g. from an old event or cache) and need fresh data.
249
+ * @returns The updated message, or null if deleted or not found
250
+ * @example
251
+ * const updated = await message.fetch();
252
+ * if (updated) console.log('Latest content:', updated.content);
253
+ */
254
+ async fetch() {
255
+ return this.client.channels.fetchMessage(this.channelId, this.id);
256
+ }
257
+ /** Delete this message. */
258
+ async delete() {
259
+ await this.client.rest.delete(import_types2.Routes.channelMessage(this.channelId, this.id));
260
+ }
261
+ /**
262
+ * Format emoji for reaction API: unicode string or "name:id" for custom.
263
+ * For string resolution (e.g. :name:), use client.resolveEmoji; Message methods resolve automatically when guildId is available.
264
+ */
265
+ static formatEmoji(emoji) {
266
+ if (typeof emoji === "string") return emoji;
267
+ return `${emoji.name}:${emoji.id}`;
268
+ }
269
+ resolveEmojiForReaction(emoji) {
270
+ return this.client.resolveEmoji(emoji, this.guildId);
271
+ }
272
+ /**
273
+ * Add a reaction to this message (as the bot).
274
+ * @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`
275
+ */
276
+ async react(emoji) {
277
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
278
+ const route = `${import_types2.Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/@me`;
279
+ await this.client.rest.put(route);
280
+ }
281
+ /**
282
+ * Remove the bot's reaction, or a specific user's reaction if userId is provided.
283
+ * @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`
284
+ * @param userId - If provided, removes that user's reaction (requires moderator permissions)
285
+ */
286
+ async removeReaction(emoji, userId) {
287
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
288
+ const route = `${import_types2.Routes.channelMessageReaction(this.channelId, this.id, emojiStr)}/${userId ?? "@me"}`;
289
+ await this.client.rest.delete(route);
290
+ }
291
+ /**
292
+ * Remove all reactions from this message.
293
+ * Requires moderator permissions.
294
+ */
295
+ async removeAllReactions() {
296
+ await this.client.rest.delete(import_types2.Routes.channelMessageReactions(this.channelId, this.id));
297
+ }
298
+ /**
299
+ * Remove all reactions of a specific emoji from this message.
300
+ * @param emoji - Unicode emoji, custom `{ name, id }`, `:name:`, `name:id`, or `<:name:id>`. Requires moderator permissions.
301
+ */
302
+ async removeReactionEmoji(emoji) {
303
+ const emojiStr = await this.resolveEmojiForReaction(emoji);
304
+ await this.client.rest.delete(import_types2.Routes.channelMessageReaction(this.channelId, this.id, emojiStr));
265
305
  }
266
306
  };
267
307
  }
@@ -272,35 +312,70 @@ var Channel_exports = {};
272
312
  __export(Channel_exports, {
273
313
  CategoryChannel: () => CategoryChannel,
274
314
  Channel: () => Channel,
315
+ DMChannel: () => DMChannel,
275
316
  GuildChannel: () => GuildChannel,
276
317
  LinkChannel: () => LinkChannel,
277
318
  TextChannel: () => TextChannel,
278
319
  VoiceChannel: () => VoiceChannel
279
320
  });
280
- var import_types4, Channel, GuildChannel, TextChannel, CategoryChannel, VoiceChannel, LinkChannel;
321
+ var import_types3, Channel, GuildChannel, TextChannel, CategoryChannel, VoiceChannel, LinkChannel, DMChannel;
281
322
  var init_Channel = __esm({
282
323
  "src/structures/Channel.ts"() {
283
324
  "use strict";
325
+ init_MessageManager();
284
326
  init_Base();
285
- import_types4 = require("@fluxerjs/types");
286
- Channel = class extends Base {
327
+ import_types3 = require("@fluxerjs/types");
328
+ Channel = class _Channel extends Base {
329
+ /** Whether this channel has a send method (TextChannel, DMChannel). */
330
+ isSendable() {
331
+ return "send" in this;
332
+ }
333
+ /** Whether this channel is a DM or Group DM. */
334
+ isDM() {
335
+ return this.type === import_types3.ChannelType.DM || this.type === import_types3.ChannelType.GroupDM;
336
+ }
337
+ /** Whether this channel is voice-based (VoiceChannel). */
338
+ isVoice() {
339
+ return "bitrate" in this;
340
+ }
341
+ /** Create a DM channel from API data (type DM or GroupDM). */
342
+ static createDM(client, data) {
343
+ return new DMChannel(client, data);
344
+ }
287
345
  client;
288
346
  id;
289
347
  type;
348
+ /** @param data - API channel from GET /channels/{id} or GET /guilds/{id}/channels */
290
349
  constructor(client, data) {
291
350
  super();
292
351
  this.client = client;
293
352
  this.id = data.id;
294
353
  this.type = data.type;
295
354
  }
355
+ /**
356
+ * Create the appropriate channel subclass from API data.
357
+ * @param client - The client instance
358
+ * @param data - Channel data from the API
359
+ */
296
360
  static from(client, data) {
297
361
  const type = data.type ?? 0;
298
- if (type === import_types4.ChannelType.GuildText) return new TextChannel(client, data);
299
- if (type === import_types4.ChannelType.GuildCategory) return new CategoryChannel(client, data);
300
- if (type === import_types4.ChannelType.GuildVoice) return new VoiceChannel(client, data);
301
- if (type === import_types4.ChannelType.GuildLink) return new LinkChannel(client, data);
362
+ if (type === import_types3.ChannelType.GuildText) return new TextChannel(client, data);
363
+ if (type === import_types3.ChannelType.GuildCategory) return new CategoryChannel(client, data);
364
+ if (type === import_types3.ChannelType.GuildVoice) return new VoiceChannel(client, data);
365
+ if (type === import_types3.ChannelType.GuildLink || type === import_types3.ChannelType.GuildLinkExtended)
366
+ return new LinkChannel(client, data);
302
367
  return new GuildChannel(client, data);
303
368
  }
369
+ /**
370
+ * Create a channel from API data, including DM and GroupDM.
371
+ * Used by ChannelManager.fetch() for GET /channels/{id}.
372
+ */
373
+ static fromOrCreate(client, data) {
374
+ const type = data.type ?? 0;
375
+ if (type === import_types3.ChannelType.DM || type === import_types3.ChannelType.GroupDM)
376
+ return _Channel.createDM(client, data);
377
+ return _Channel.from(client, data);
378
+ }
304
379
  };
305
380
  GuildChannel = class extends Channel {
306
381
  guildId;
@@ -314,19 +389,26 @@ var init_Channel = __esm({
314
389
  this.position = data.position;
315
390
  this.parentId = data.parent_id ?? null;
316
391
  }
317
- /** Create a webhook in this channel. Returns the webhook with token (required for send()). */
392
+ /**
393
+ * Create a webhook in this channel.
394
+ * @param options - Webhook name and optional avatar URL
395
+ * @returns The webhook with token (required for send()). Requires Manage Webhooks permission.
396
+ */
318
397
  async createWebhook(options) {
319
398
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
320
- const data = await this.client.rest.post(import_types4.Routes.channelWebhooks(this.id), {
399
+ const data = await this.client.rest.post(import_types3.Routes.channelWebhooks(this.id), {
321
400
  body: options,
322
401
  auth: true
323
402
  });
324
403
  return new Webhook2(this.client, data);
325
404
  }
326
- /** Fetch all webhooks in this channel. Returned webhooks do not include the token (cannot send). */
405
+ /**
406
+ * Fetch all webhooks in this channel.
407
+ * @returns Webhooks (includes token when listing from channel; can send via send())
408
+ */
327
409
  async fetchWebhooks() {
328
410
  const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
329
- const data = await this.client.rest.get(import_types4.Routes.channelWebhooks(this.id));
411
+ const data = await this.client.rest.get(import_types3.Routes.channelWebhooks(this.id));
330
412
  const list = Array.isArray(data) ? data : Object.values(data ?? {});
331
413
  return list.map((w) => new Webhook2(this.client, w));
332
414
  }
@@ -343,12 +425,29 @@ var init_Channel = __esm({
343
425
  this.rateLimitPerUser = data.rate_limit_per_user ?? 0;
344
426
  this.lastMessageId = data.last_message_id ?? null;
345
427
  }
428
+ /**
429
+ * Send a message to this channel.
430
+ * @param options - Text content or object with `content` and/or `embeds`
431
+ */
346
432
  async send(options) {
347
433
  const body = typeof options === "string" ? { content: options } : options;
348
434
  const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
349
- const data = await this.client.rest.post(import_types4.Routes.channelMessages(this.id), { body });
435
+ const data = await this.client.rest.post(import_types3.Routes.channelMessages(this.id), { body });
350
436
  return new Message2(this.client, data);
351
437
  }
438
+ /** Message manager for this channel. Use channel.messages.fetch(messageId). */
439
+ get messages() {
440
+ return new MessageManager(this.client, this.id);
441
+ }
442
+ /**
443
+ * Fetch a message by ID from this channel.
444
+ * @param messageId - Snowflake of the message
445
+ * @returns The message, or null if not found
446
+ * @deprecated Use channel.messages.fetch(messageId) instead.
447
+ */
448
+ async fetchMessage(messageId) {
449
+ return this.client.channels.fetchMessage(this.id, messageId);
450
+ }
352
451
  };
353
452
  CategoryChannel = class extends GuildChannel {
354
453
  };
@@ -370,6 +469,36 @@ var init_Channel = __esm({
370
469
  this.url = data.url ?? null;
371
470
  }
372
471
  };
472
+ DMChannel = class extends Channel {
473
+ lastMessageId;
474
+ constructor(client, data) {
475
+ super(client, data);
476
+ this.lastMessageId = data.last_message_id ?? null;
477
+ }
478
+ /**
479
+ * Send a message to this DM channel.
480
+ * @param options - Text content or object with `content` and/or `embeds`
481
+ */
482
+ async send(options) {
483
+ const body = typeof options === "string" ? { content: options } : options;
484
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
485
+ const data = await this.client.rest.post(import_types3.Routes.channelMessages(this.id), { body });
486
+ return new Message2(this.client, data);
487
+ }
488
+ /** Message manager for this channel. Use channel.messages.fetch(messageId). */
489
+ get messages() {
490
+ return new MessageManager(this.client, this.id);
491
+ }
492
+ /**
493
+ * Fetch a message by ID from this DM channel.
494
+ * @param messageId - Snowflake of the message
495
+ * @returns The message, or null if not found
496
+ * @deprecated Use channel.messages.fetch(messageId) instead.
497
+ */
498
+ async fetchMessage(messageId) {
499
+ return this.client.channels.fetchMessage(this.id, messageId);
500
+ }
501
+ };
373
502
  }
374
503
  });
375
504
 
@@ -378,12 +507,12 @@ var GuildMember_exports = {};
378
507
  __export(GuildMember_exports, {
379
508
  GuildMember: () => GuildMember
380
509
  });
381
- var GuildMember;
510
+ var import_types5, GuildMember;
382
511
  var init_GuildMember = __esm({
383
512
  "src/structures/GuildMember.ts"() {
384
513
  "use strict";
385
514
  init_Base();
386
- init_User();
515
+ import_types5 = require("@fluxerjs/types");
387
516
  GuildMember = class extends Base {
388
517
  client;
389
518
  id;
@@ -393,10 +522,11 @@ var init_GuildMember = __esm({
393
522
  roles;
394
523
  joinedAt;
395
524
  communicationDisabledUntil;
525
+ /** @param data - API guild member from GET /guilds/{id}/members or GET /guilds/{id}/members/{user_id} */
396
526
  constructor(client, data, guild) {
397
527
  super();
398
528
  this.client = client;
399
- this.user = new User(client, data.user);
529
+ this.user = client.getOrCreateUser(data.user);
400
530
  this.id = data.user.id;
401
531
  this.guild = guild;
402
532
  this.nick = data.nick ?? null;
@@ -404,14 +534,325 @@ var init_GuildMember = __esm({
404
534
  this.joinedAt = new Date(data.joined_at);
405
535
  this.communicationDisabledUntil = data.communication_disabled_until ? new Date(data.communication_disabled_until) : null;
406
536
  }
537
+ /** Nickname, or global name, or username. */
407
538
  get displayName() {
408
539
  return this.nick ?? this.user.globalName ?? this.user.username;
409
540
  }
410
- };
411
- }
412
- });
413
-
414
- // src/client/ClientUser.ts
541
+ /**
542
+ * Add a role to this member.
543
+ * @param roleId - The role ID to add
544
+ * Requires Manage Roles permission.
545
+ */
546
+ async addRole(roleId) {
547
+ await this.client.rest.put(import_types5.Routes.guildMemberRole(this.guild.id, this.id, roleId));
548
+ }
549
+ /**
550
+ * Remove a role from this member.
551
+ * @param roleId - The role ID to remove
552
+ * Requires Manage Roles permission.
553
+ */
554
+ async removeRole(roleId) {
555
+ await this.client.rest.delete(import_types5.Routes.guildMemberRole(this.guild.id, this.id, roleId));
556
+ }
557
+ };
558
+ }
559
+ });
560
+
561
+ // src/structures/Role.ts
562
+ var Role_exports = {};
563
+ __export(Role_exports, {
564
+ Role: () => Role
565
+ });
566
+ var Role;
567
+ var init_Role = __esm({
568
+ "src/structures/Role.ts"() {
569
+ "use strict";
570
+ init_Base();
571
+ Role = class extends Base {
572
+ client;
573
+ id;
574
+ guildId;
575
+ name;
576
+ color;
577
+ position;
578
+ permissions;
579
+ hoist;
580
+ mentionable;
581
+ unicodeEmoji;
582
+ /** @param client - The client instance */
583
+ /** @param data - API role from GET /guilds/{id}/roles or gateway role events */
584
+ /** @param guildId - The guild this role belongs to */
585
+ constructor(client, data, guildId) {
586
+ super();
587
+ this.client = client;
588
+ this.id = data.id;
589
+ this.guildId = guildId;
590
+ this.name = data.name;
591
+ this.color = data.color;
592
+ this.position = data.position;
593
+ this.permissions = data.permissions;
594
+ this.hoist = !!data.hoist;
595
+ this.mentionable = !!data.mentionable;
596
+ this.unicodeEmoji = data.unicode_emoji ?? null;
597
+ }
598
+ /** Returns a mention string (e.g. `<@&123456>`). */
599
+ toString() {
600
+ return `<@&${this.id}>`;
601
+ }
602
+ };
603
+ }
604
+ });
605
+
606
+ // src/util/Constants.ts
607
+ var CDN_URL;
608
+ var init_Constants = __esm({
609
+ "src/util/Constants.ts"() {
610
+ "use strict";
611
+ CDN_URL = "https://fluxerusercontent.com";
612
+ }
613
+ });
614
+
615
+ // src/structures/Guild.ts
616
+ var Guild_exports = {};
617
+ __export(Guild_exports, {
618
+ Guild: () => Guild
619
+ });
620
+ var import_util, import_collection3, import_types6, Guild;
621
+ var init_Guild = __esm({
622
+ "src/structures/Guild.ts"() {
623
+ "use strict";
624
+ import_util = require("@fluxerjs/util");
625
+ init_Base();
626
+ import_collection3 = require("@fluxerjs/collection");
627
+ init_GuildMember();
628
+ init_Role();
629
+ init_Constants();
630
+ import_types6 = require("@fluxerjs/types");
631
+ Guild = class extends Base {
632
+ client;
633
+ id;
634
+ name;
635
+ icon;
636
+ banner;
637
+ ownerId;
638
+ members = new import_collection3.Collection();
639
+ channels = new import_collection3.Collection();
640
+ roles = new import_collection3.Collection();
641
+ /** @param data - API guild from GET /guilds/{id} or gateway GUILD_CREATE */
642
+ constructor(client, data) {
643
+ super();
644
+ this.client = client;
645
+ this.id = data.id;
646
+ this.name = data.name;
647
+ this.icon = data.icon ?? null;
648
+ this.banner = data.banner ?? null;
649
+ this.ownerId = data.owner_id;
650
+ for (const r of data.roles ?? []) {
651
+ this.roles.set(r.id, new Role(client, r, this.id));
652
+ }
653
+ }
654
+ /** Get the guild icon URL, or null if no icon. */
655
+ iconURL(options) {
656
+ if (!this.icon) return null;
657
+ const size = options?.size ? `?size=${options.size}` : "";
658
+ return `${CDN_URL}/icons/${this.id}/${this.icon}.png${size}`;
659
+ }
660
+ /** Get the guild banner URL, or null if no banner. */
661
+ bannerURL(options) {
662
+ if (!this.banner) return null;
663
+ const size = options?.size ? `?size=${options.size}` : "";
664
+ return `${CDN_URL}/banners/${this.id}/${this.banner}.png${size}`;
665
+ }
666
+ /**
667
+ * Add a role to a member by user ID. Does not require fetching the member first.
668
+ * @param userId - The user ID of the member
669
+ * @param roleId - The role ID to add (or use guild.resolveRoleId for mention/name resolution)
670
+ * Requires Manage Roles permission.
671
+ */
672
+ async addRoleToMember(userId, roleId) {
673
+ await this.client.rest.put(import_types6.Routes.guildMemberRole(this.id, userId, roleId));
674
+ }
675
+ /**
676
+ * Remove a role from a member by user ID. Does not require fetching the member first.
677
+ * @param userId - The user ID of the member
678
+ * @param roleId - The role ID to remove
679
+ * Requires Manage Roles permission.
680
+ */
681
+ async removeRoleFromMember(userId, roleId) {
682
+ await this.client.rest.delete(import_types6.Routes.guildMemberRole(this.id, userId, roleId));
683
+ }
684
+ /**
685
+ * Resolve a role ID from an argument (role mention, raw ID, or name).
686
+ * Fetches guild roles if name is provided.
687
+ * @param arg - Role mention (@role), role ID, or role name
688
+ * @returns The role ID, or null if not found
689
+ */
690
+ async resolveRoleId(arg) {
691
+ const parsed = (0, import_util.parseRoleMention)(arg);
692
+ if (parsed) return parsed;
693
+ if (/^\d{17,19}$/.test(arg.trim())) return arg.trim();
694
+ const cached = this.roles.find(
695
+ (r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase())
696
+ );
697
+ if (cached) return cached.id;
698
+ const roles = await this.client.rest.get(import_types6.Routes.guildRoles(this.id));
699
+ const list = Array.isArray(roles) ? roles : Object.values(roles ?? {});
700
+ const role = list.find((r) => !!(r.name && r.name.toLowerCase() === arg.trim().toLowerCase()));
701
+ if (role) {
702
+ this.roles.set(role.id, new Role(this.client, role, this.id));
703
+ return role.id;
704
+ }
705
+ return null;
706
+ }
707
+ /**
708
+ * Fetch a guild member by user ID.
709
+ * @param userId - The user ID of the member to fetch
710
+ * @returns The guild member, or null if not found
711
+ */
712
+ async fetchMember(userId) {
713
+ try {
714
+ const data = await this.client.rest.get(
715
+ import_types6.Routes.guildMember(this.id, userId)
716
+ );
717
+ return new GuildMember(this.client, { ...data, guild_id: this.id }, this);
718
+ } catch {
719
+ return null;
720
+ }
721
+ }
722
+ /** Fetch all webhooks in this guild. Returned webhooks do not include the token (cannot send). */
723
+ async fetchWebhooks() {
724
+ const { Webhook: Webhook2 } = await Promise.resolve().then(() => (init_Webhook(), Webhook_exports));
725
+ const data = await this.client.rest.get(import_types6.Routes.guildWebhooks(this.id));
726
+ const list = Array.isArray(data) ? data : Object.values(data ?? {});
727
+ return list.map((w) => new Webhook2(this.client, w));
728
+ }
729
+ };
730
+ }
731
+ });
732
+
733
+ // src/structures/User.ts
734
+ var import_types8, User;
735
+ var init_User = __esm({
736
+ "src/structures/User.ts"() {
737
+ "use strict";
738
+ init_Base();
739
+ import_types8 = require("@fluxerjs/types");
740
+ init_Constants();
741
+ User = class extends Base {
742
+ client;
743
+ id;
744
+ username;
745
+ discriminator;
746
+ globalName;
747
+ avatar;
748
+ bot;
749
+ /** @param data - API user from message author, GET /users/{id}, or GET /users/@me */
750
+ constructor(client, data) {
751
+ super();
752
+ this.client = client;
753
+ this.id = data.id;
754
+ this.username = data.username;
755
+ this.discriminator = data.discriminator;
756
+ this.globalName = data.global_name ?? null;
757
+ this.avatar = data.avatar ?? null;
758
+ this.bot = !!data.bot;
759
+ }
760
+ /** Update mutable fields from fresh API data. Used by getOrCreateUser cache. */
761
+ _patch(data) {
762
+ this.username = data.username;
763
+ this.discriminator = data.discriminator;
764
+ this.globalName = data.global_name ?? null;
765
+ this.avatar = data.avatar ?? null;
766
+ }
767
+ /**
768
+ * Get the URL for this user's avatar.
769
+ * @param options - Optional `size` and `extension` (default: `png`)
770
+ */
771
+ avatarURL(options) {
772
+ if (!this.avatar) return null;
773
+ const ext = options?.extension ?? "png";
774
+ const size = options?.size ? `?size=${options.size}` : "";
775
+ return `${CDN_URL}/avatars/${this.id}/${this.avatar}.${ext}${size}`;
776
+ }
777
+ /** Get the avatar URL, or the default avatar if none set. */
778
+ displayAvatarURL(options) {
779
+ return this.avatarURL(options) ?? `${CDN_URL}/avatars/0/0.png`;
780
+ }
781
+ /** Returns a mention string (e.g. `<@123456>`). */
782
+ toString() {
783
+ return `<@${this.id}>`;
784
+ }
785
+ /**
786
+ * Create or get a DM channel with this user.
787
+ * Returns the DM channel; use {@link DMChannel.send} to send messages.
788
+ */
789
+ async createDM() {
790
+ const { DMChannel: DMChannelClass } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
791
+ const data = await this.client.rest.post(import_types8.Routes.userMeChannels(), {
792
+ body: { recipient_id: this.id },
793
+ auth: true
794
+ });
795
+ return new DMChannelClass(this.client, data);
796
+ }
797
+ /**
798
+ * Send a DM to this user.
799
+ * Convenience method that creates the DM channel and sends the message.
800
+ */
801
+ async send(options) {
802
+ const dm = await this.createDM();
803
+ return dm.send(options);
804
+ }
805
+ };
806
+ }
807
+ });
808
+
809
+ // src/structures/MessageReaction.ts
810
+ var MessageReaction_exports = {};
811
+ __export(MessageReaction_exports, {
812
+ MessageReaction: () => MessageReaction
813
+ });
814
+ var MessageReaction;
815
+ var init_MessageReaction = __esm({
816
+ "src/structures/MessageReaction.ts"() {
817
+ "use strict";
818
+ init_Base();
819
+ MessageReaction = class extends Base {
820
+ client;
821
+ messageId;
822
+ channelId;
823
+ guildId;
824
+ emoji;
825
+ /** Raw gateway payload for low-level access. */
826
+ _data;
827
+ constructor(client, data) {
828
+ super();
829
+ this.client = client;
830
+ this._data = data;
831
+ this.messageId = data.message_id;
832
+ this.channelId = data.channel_id;
833
+ this.guildId = data.guild_id ?? null;
834
+ this.emoji = data.emoji;
835
+ }
836
+ /** Emoji as a string: unicode or "name:id" for custom. */
837
+ get emojiIdentifier() {
838
+ return this.emoji.id ? `${this.emoji.name}:${this.emoji.id}` : this.emoji.name;
839
+ }
840
+ /** Guild where this reaction was added. Resolved from cache; null for DMs or if not cached. */
841
+ get guild() {
842
+ return this.guildId ? this.client.guilds.get(this.guildId) ?? null : null;
843
+ }
844
+ /**
845
+ * Fetch the message this reaction belongs to.
846
+ * Use when you need to edit, delete, or otherwise interact with the message.
847
+ */
848
+ async fetchMessage() {
849
+ return this.client.channels.fetchMessage(this.channelId, this.messageId);
850
+ }
851
+ };
852
+ }
853
+ });
854
+
855
+ // src/client/ClientUser.ts
415
856
  var ClientUser_exports = {};
416
857
  __export(ClientUser_exports, {
417
858
  ClientUser: () => ClientUser
@@ -436,20 +877,25 @@ __export(index_exports, {
436
877
  Base: () => Base,
437
878
  CategoryChannel: () => CategoryChannel,
438
879
  Channel: () => Channel,
880
+ ChannelManager: () => ChannelManager,
439
881
  Client: () => Client,
440
882
  ClientUser: () => ClientUser,
883
+ DMChannel: () => DMChannel,
441
884
  EmbedBuilder: () => import_builders2.EmbedBuilder,
442
885
  ErrorCodes: () => ErrorCodes,
443
886
  Events: () => Events,
444
887
  FluxerError: () => FluxerError,
445
- GatewayOpcodes: () => import_types6.GatewayOpcodes,
888
+ GatewayOpcodes: () => import_types10.GatewayOpcodes,
446
889
  Guild: () => Guild,
447
890
  GuildChannel: () => GuildChannel,
448
891
  GuildMember: () => GuildMember,
449
892
  LinkChannel: () => LinkChannel,
450
893
  Message: () => Message,
894
+ MessageManager: () => MessageManager,
451
895
  MessagePayload: () => import_builders2.MessagePayload,
452
- Routes: () => import_types6.Routes,
896
+ MessageReaction: () => MessageReaction,
897
+ Role: () => Role,
898
+ Routes: () => import_types10.Routes,
453
899
  TextChannel: () => TextChannel,
454
900
  User: () => User,
455
901
  VoiceChannel: () => VoiceChannel,
@@ -461,8 +907,120 @@ module.exports = __toCommonJS(index_exports);
461
907
  var import_events = require("events");
462
908
  var import_rest = require("@fluxerjs/rest");
463
909
  var import_ws = require("@fluxerjs/ws");
464
- var import_types5 = require("@fluxerjs/types");
465
- var import_collection3 = require("@fluxerjs/collection");
910
+ var import_types9 = require("@fluxerjs/types");
911
+ var import_collection5 = require("@fluxerjs/collection");
912
+
913
+ // src/client/ChannelManager.ts
914
+ var import_collection2 = require("@fluxerjs/collection");
915
+ var import_types4 = require("@fluxerjs/types");
916
+ var ChannelManager = class extends import_collection2.Collection {
917
+ constructor(client) {
918
+ super();
919
+ this.client = client;
920
+ }
921
+ /**
922
+ * Fetch a channel by ID from the API (or return from cache if present).
923
+ * @param channelId - Snowflake of the channel
924
+ * @returns The channel, or null if not found
925
+ * @example
926
+ * const channel = await client.channels.fetch(channelId);
927
+ * if (channel?.isSendable()) await channel.send('Hello!');
928
+ */
929
+ async fetch(channelId) {
930
+ const cached = this.get(channelId);
931
+ if (cached) return cached;
932
+ try {
933
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
934
+ const data = await this.client.rest.get(
935
+ import_types4.Routes.channel(channelId)
936
+ );
937
+ const channel = Channel2.fromOrCreate(this.client, data);
938
+ if (channel) this.set(channel.id, channel);
939
+ return channel;
940
+ } catch {
941
+ return null;
942
+ }
943
+ }
944
+ /**
945
+ * Fetch a message by ID from the API.
946
+ * @param channelId - Snowflake of the channel
947
+ * @param messageId - Snowflake of the message
948
+ * @returns The message, or null if not found
949
+ * @deprecated Use channel.messages.fetch(messageId). Prefer (await client.channels.fetch(channelId))?.messages?.fetch(messageId).
950
+ * @example
951
+ * const channel = await client.channels.fetch(channelId);
952
+ * const message = await channel?.messages?.fetch(messageId);
953
+ */
954
+ async fetchMessage(channelId, messageId) {
955
+ try {
956
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
957
+ const data = await this.client.rest.get(
958
+ import_types4.Routes.channelMessage(channelId, messageId)
959
+ );
960
+ return new Message2(this.client, data);
961
+ } catch {
962
+ return null;
963
+ }
964
+ }
965
+ /**
966
+ * Send a message to a channel by ID. Works even when the channel is not cached.
967
+ * Skips the fetch when you only need to send.
968
+ * @param channelId - Snowflake of the channel (text channel or DM)
969
+ * @param payload - Text content or object with content and/or embeds
970
+ * @returns The created message
971
+ * @example
972
+ * await client.channels.send(logChannelId, 'User joined!');
973
+ * await client.channels.send(channelId, { embeds: [embed.toJSON()] });
974
+ */
975
+ async send(channelId, payload) {
976
+ const body = typeof payload === "string" ? { content: payload } : payload;
977
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
978
+ const data = await this.client.rest.post(import_types4.Routes.channelMessages(channelId), { body });
979
+ return new Message2(this.client, data);
980
+ }
981
+ };
982
+
983
+ // src/client/GuildManager.ts
984
+ var import_collection4 = require("@fluxerjs/collection");
985
+ var import_types7 = require("@fluxerjs/types");
986
+ var GuildManager = class extends import_collection4.Collection {
987
+ constructor(client) {
988
+ super();
989
+ this.client = client;
990
+ }
991
+ /**
992
+ * Fetch a guild by ID from the API (or return from cache if present).
993
+ * @param guildId - Snowflake of the guild
994
+ * @returns The guild, or null if not found
995
+ * @example
996
+ * const guild = await client.guilds.fetch(guildId);
997
+ * if (guild) console.log(guild.name);
998
+ */
999
+ async fetch(guildId) {
1000
+ const cached = this.get(guildId);
1001
+ if (cached) return cached;
1002
+ try {
1003
+ const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1004
+ const data = await this.client.rest.get(
1005
+ import_types7.Routes.guild(guildId)
1006
+ );
1007
+ const guild = new Guild2(this.client, data);
1008
+ this.set(guild.id, guild);
1009
+ return guild;
1010
+ } catch {
1011
+ return null;
1012
+ }
1013
+ }
1014
+ };
1015
+
1016
+ // src/errors/FluxerError.ts
1017
+ var FluxerError = class _FluxerError extends Error {
1018
+ constructor(message) {
1019
+ super(message);
1020
+ this.name = "FluxerError";
1021
+ Object.setPrototypeOf(this, _FluxerError.prototype);
1022
+ }
1023
+ };
466
1024
 
467
1025
  // src/util/Events.ts
468
1026
  var Events = {
@@ -470,6 +1028,7 @@ var Events = {
470
1028
  MessageCreate: "messageCreate",
471
1029
  MessageUpdate: "messageUpdate",
472
1030
  MessageDelete: "messageDelete",
1031
+ MessageDeleteBulk: "messageDeleteBulk",
473
1032
  MessageReactionAdd: "messageReactionAdd",
474
1033
  MessageReactionRemove: "messageReactionRemove",
475
1034
  MessageReactionRemoveAll: "messageReactionRemoveAll",
@@ -478,24 +1037,285 @@ var Events = {
478
1037
  GuildCreate: "guildCreate",
479
1038
  GuildUpdate: "guildUpdate",
480
1039
  GuildDelete: "guildDelete",
481
- ChannelCreate: "channelCreate",
482
- ChannelUpdate: "channelUpdate",
483
- ChannelDelete: "channelDelete",
1040
+ GuildBanAdd: "guildBanAdd",
1041
+ GuildBanRemove: "guildBanRemove",
1042
+ GuildEmojisUpdate: "guildEmojisUpdate",
1043
+ GuildStickersUpdate: "guildStickersUpdate",
1044
+ GuildIntegrationsUpdate: "guildIntegrationsUpdate",
484
1045
  GuildMemberAdd: "guildMemberAdd",
485
1046
  GuildMemberUpdate: "guildMemberUpdate",
486
1047
  GuildMemberRemove: "guildMemberRemove",
1048
+ GuildRoleCreate: "guildRoleCreate",
1049
+ GuildRoleUpdate: "guildRoleUpdate",
1050
+ GuildRoleDelete: "guildRoleDelete",
1051
+ GuildScheduledEventCreate: "guildScheduledEventCreate",
1052
+ GuildScheduledEventUpdate: "guildScheduledEventUpdate",
1053
+ GuildScheduledEventDelete: "guildScheduledEventDelete",
1054
+ ChannelCreate: "channelCreate",
1055
+ ChannelUpdate: "channelUpdate",
1056
+ ChannelDelete: "channelDelete",
1057
+ ChannelPinsUpdate: "channelPinsUpdate",
1058
+ InviteCreate: "inviteCreate",
1059
+ InviteDelete: "inviteDelete",
1060
+ TypingStart: "typingStart",
1061
+ UserUpdate: "userUpdate",
1062
+ PresenceUpdate: "presenceUpdate",
487
1063
  VoiceStateUpdate: "voiceStateUpdate",
488
1064
  VoiceServerUpdate: "voiceServerUpdate",
489
1065
  VoiceStatesSync: "voiceStatesSync",
1066
+ WebhooksUpdate: "webhooksUpdate",
1067
+ Resumed: "resumed",
490
1068
  Error: "error",
491
1069
  Debug: "debug"
492
1070
  };
493
1071
 
1072
+ // src/client/Client.ts
1073
+ var import_util2 = require("@fluxerjs/util");
1074
+ init_User();
1075
+
1076
+ // src/client/EventHandlerRegistry.ts
1077
+ var handlers = /* @__PURE__ */ new Map();
1078
+ handlers.set("MESSAGE_CREATE", async (client, d) => {
1079
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1080
+ client.emit(Events.MessageCreate, new Message2(client, d));
1081
+ });
1082
+ handlers.set("MESSAGE_UPDATE", async (client, d) => {
1083
+ const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
1084
+ client.emit(Events.MessageUpdate, null, new Message2(client, d));
1085
+ });
1086
+ handlers.set("MESSAGE_DELETE", async (client, d) => {
1087
+ const data = d;
1088
+ const channel = client.channels.get(data.channel_id) ?? null;
1089
+ client.emit(Events.MessageDelete, {
1090
+ id: data.id,
1091
+ channelId: data.channel_id,
1092
+ channel
1093
+ });
1094
+ });
1095
+ handlers.set("MESSAGE_REACTION_ADD", async (client, d) => {
1096
+ const data = d;
1097
+ const { MessageReaction: MessageReaction2 } = await Promise.resolve().then(() => (init_MessageReaction(), MessageReaction_exports));
1098
+ const reaction = new MessageReaction2(client, data);
1099
+ const user = client.getOrCreateUser({
1100
+ id: data.user_id,
1101
+ username: "Unknown",
1102
+ discriminator: "0"
1103
+ });
1104
+ client.emit(Events.MessageReactionAdd, reaction, user);
1105
+ });
1106
+ handlers.set("MESSAGE_REACTION_REMOVE", async (client, d) => {
1107
+ const data = d;
1108
+ const { MessageReaction: MessageReaction2 } = await Promise.resolve().then(() => (init_MessageReaction(), MessageReaction_exports));
1109
+ const reaction = new MessageReaction2(client, data);
1110
+ const user = client.getOrCreateUser({
1111
+ id: data.user_id,
1112
+ username: "Unknown",
1113
+ discriminator: "0"
1114
+ });
1115
+ client.emit(Events.MessageReactionRemove, reaction, user);
1116
+ });
1117
+ handlers.set("MESSAGE_REACTION_REMOVE_ALL", async (client, d) => {
1118
+ client.emit(Events.MessageReactionRemoveAll, d);
1119
+ });
1120
+ handlers.set("MESSAGE_REACTION_REMOVE_EMOJI", async (client, d) => {
1121
+ client.emit(
1122
+ Events.MessageReactionRemoveEmoji,
1123
+ d
1124
+ );
1125
+ });
1126
+ handlers.set("GUILD_CREATE", async (client, d) => {
1127
+ const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1128
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1129
+ const guild = new Guild2(client, d);
1130
+ client.guilds.set(guild.id, guild);
1131
+ const g = d;
1132
+ for (const ch of g.channels ?? []) {
1133
+ const channel = Channel2.from(client, ch);
1134
+ if (channel) client.channels.set(channel.id, channel);
1135
+ }
1136
+ client.emit(Events.GuildCreate, guild);
1137
+ if (g.voice_states?.length) {
1138
+ client.emit(Events.VoiceStatesSync, { guildId: guild.id, voiceStates: g.voice_states });
1139
+ }
1140
+ });
1141
+ handlers.set("GUILD_UPDATE", async (client, d) => {
1142
+ const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1143
+ const g = d;
1144
+ const old = client.guilds.get(g.id);
1145
+ const updated = new Guild2(client, g);
1146
+ client.guilds.set(updated.id, updated);
1147
+ client.emit(Events.GuildUpdate, old ?? updated, updated);
1148
+ });
1149
+ handlers.set("GUILD_DELETE", async (client, d) => {
1150
+ const g = d;
1151
+ const guild = client.guilds.get(g.id);
1152
+ if (guild) {
1153
+ client.guilds.delete(g.id);
1154
+ client.emit(Events.GuildDelete, guild);
1155
+ }
1156
+ });
1157
+ handlers.set("CHANNEL_CREATE", async (client, d) => {
1158
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1159
+ const ch = Channel2.from(client, d);
1160
+ if (ch) {
1161
+ client.channels.set(ch.id, ch);
1162
+ client.emit(Events.ChannelCreate, ch);
1163
+ }
1164
+ });
1165
+ handlers.set("CHANNEL_UPDATE", async (client, d) => {
1166
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1167
+ const ch = d;
1168
+ const oldCh = client.channels.get(ch.id);
1169
+ const newCh = Channel2.from(client, ch);
1170
+ if (newCh) {
1171
+ client.channels.set(newCh.id, newCh);
1172
+ client.emit(Events.ChannelUpdate, oldCh ?? newCh, newCh);
1173
+ }
1174
+ });
1175
+ handlers.set("CHANNEL_DELETE", async (client, d) => {
1176
+ const ch = d;
1177
+ const channel = client.channels.get(ch.id);
1178
+ if (channel) {
1179
+ client.channels.delete(ch.id);
1180
+ client.emit(Events.ChannelDelete, channel);
1181
+ }
1182
+ });
1183
+ handlers.set("GUILD_MEMBER_ADD", async (client, d) => {
1184
+ const { GuildMember: GuildMember2 } = await Promise.resolve().then(() => (init_GuildMember(), GuildMember_exports));
1185
+ const data = d;
1186
+ const guild = client.guilds.get(data.guild_id);
1187
+ if (guild) {
1188
+ const member = new GuildMember2(client, data, guild);
1189
+ guild.members.set(member.id, member);
1190
+ client.emit(Events.GuildMemberAdd, member);
1191
+ }
1192
+ });
1193
+ handlers.set("GUILD_MEMBER_UPDATE", async (client, d) => {
1194
+ const { GuildMember: GuildMember2 } = await Promise.resolve().then(() => (init_GuildMember(), GuildMember_exports));
1195
+ const data = d;
1196
+ const guild = client.guilds.get(data.guild_id);
1197
+ if (guild) {
1198
+ const oldM = guild.members.get(data.user.id);
1199
+ const newM = new GuildMember2(client, data, guild);
1200
+ guild.members.set(newM.id, newM);
1201
+ client.emit(Events.GuildMemberUpdate, oldM ?? newM, newM);
1202
+ }
1203
+ });
1204
+ handlers.set("GUILD_MEMBER_REMOVE", async (client, d) => {
1205
+ const data = d;
1206
+ const guild = client.guilds.get(data.guild_id);
1207
+ if (guild) {
1208
+ const member = guild.members.get(data.user.id);
1209
+ if (member) {
1210
+ guild.members.delete(data.user.id);
1211
+ client.emit(Events.GuildMemberRemove, member);
1212
+ }
1213
+ }
1214
+ });
1215
+ handlers.set("INTERACTION_CREATE", async (client, d) => {
1216
+ client.emit(Events.InteractionCreate, d);
1217
+ });
1218
+ handlers.set("VOICE_STATE_UPDATE", async (client, d) => {
1219
+ client.emit(Events.VoiceStateUpdate, d);
1220
+ });
1221
+ handlers.set("VOICE_SERVER_UPDATE", async (client, d) => {
1222
+ client.emit(Events.VoiceServerUpdate, d);
1223
+ });
1224
+ handlers.set("MESSAGE_DELETE_BULK", async (client, d) => {
1225
+ client.emit(Events.MessageDeleteBulk, d);
1226
+ });
1227
+ handlers.set("GUILD_BAN_ADD", async (client, d) => {
1228
+ client.emit(Events.GuildBanAdd, d);
1229
+ });
1230
+ handlers.set("GUILD_BAN_REMOVE", async (client, d) => {
1231
+ client.emit(Events.GuildBanRemove, d);
1232
+ });
1233
+ handlers.set("GUILD_EMOJIS_UPDATE", async (client, d) => {
1234
+ client.emit(Events.GuildEmojisUpdate, d);
1235
+ });
1236
+ handlers.set("GUILD_STICKERS_UPDATE", async (client, d) => {
1237
+ client.emit(Events.GuildStickersUpdate, d);
1238
+ });
1239
+ handlers.set("GUILD_INTEGRATIONS_UPDATE", async (client, d) => {
1240
+ client.emit(Events.GuildIntegrationsUpdate, d);
1241
+ });
1242
+ handlers.set("GUILD_ROLE_CREATE", async (client, d) => {
1243
+ const data = d;
1244
+ const guild = client.guilds.get(data.guild_id);
1245
+ if (guild) {
1246
+ const { Role: Role2 } = await Promise.resolve().then(() => (init_Role(), Role_exports));
1247
+ guild.roles.set(data.role.id, new Role2(client, data.role, guild.id));
1248
+ }
1249
+ client.emit(Events.GuildRoleCreate, data);
1250
+ });
1251
+ handlers.set("GUILD_ROLE_UPDATE", async (client, d) => {
1252
+ const data = d;
1253
+ const guild = client.guilds.get(data.guild_id);
1254
+ if (guild) {
1255
+ const { Role: Role2 } = await Promise.resolve().then(() => (init_Role(), Role_exports));
1256
+ guild.roles.set(data.role.id, new Role2(client, data.role, guild.id));
1257
+ }
1258
+ client.emit(Events.GuildRoleUpdate, data);
1259
+ });
1260
+ handlers.set("GUILD_ROLE_DELETE", async (client, d) => {
1261
+ const data = d;
1262
+ const guild = client.guilds.get(data.guild_id);
1263
+ if (guild) guild.roles.delete(data.role_id);
1264
+ client.emit(Events.GuildRoleDelete, data);
1265
+ });
1266
+ handlers.set("GUILD_SCHEDULED_EVENT_CREATE", async (client, d) => {
1267
+ client.emit(Events.GuildScheduledEventCreate, d);
1268
+ });
1269
+ handlers.set("GUILD_SCHEDULED_EVENT_UPDATE", async (client, d) => {
1270
+ client.emit(Events.GuildScheduledEventUpdate, d);
1271
+ });
1272
+ handlers.set("GUILD_SCHEDULED_EVENT_DELETE", async (client, d) => {
1273
+ client.emit(Events.GuildScheduledEventDelete, d);
1274
+ });
1275
+ handlers.set("CHANNEL_PINS_UPDATE", async (client, d) => {
1276
+ client.emit(Events.ChannelPinsUpdate, d);
1277
+ });
1278
+ handlers.set("INVITE_CREATE", async (client, d) => {
1279
+ client.emit(Events.InviteCreate, d);
1280
+ });
1281
+ handlers.set("INVITE_DELETE", async (client, d) => {
1282
+ client.emit(Events.InviteDelete, d);
1283
+ });
1284
+ handlers.set("TYPING_START", async (client, d) => {
1285
+ client.emit(Events.TypingStart, d);
1286
+ });
1287
+ handlers.set("USER_UPDATE", async (client, d) => {
1288
+ const data = d;
1289
+ if (client.user?.id === data.id) {
1290
+ client.user._patch(data);
1291
+ }
1292
+ client.emit(Events.UserUpdate, data);
1293
+ });
1294
+ handlers.set("PRESENCE_UPDATE", async (client, d) => {
1295
+ client.emit(Events.PresenceUpdate, d);
1296
+ });
1297
+ handlers.set("WEBHOOKS_UPDATE", async (client, d) => {
1298
+ client.emit(Events.WebhooksUpdate, d);
1299
+ });
1300
+ handlers.set("RESUMED", async (client) => {
1301
+ client.emit(Events.Resumed);
1302
+ });
1303
+ var eventHandlers = handlers;
1304
+
494
1305
  // src/client/Client.ts
495
1306
  var Client = class extends import_events.EventEmitter {
1307
+ /** @param options - Token, REST config, WebSocket, presence, etc. */
496
1308
  constructor(options = {}) {
497
1309
  super();
498
1310
  this.options = options;
1311
+ Object.defineProperty(this.channels, "cache", {
1312
+ get: () => this.channels,
1313
+ configurable: true
1314
+ });
1315
+ Object.defineProperty(this.guilds, "cache", {
1316
+ get: () => this.guilds,
1317
+ configurable: true
1318
+ });
499
1319
  this.rest = new import_rest.REST({
500
1320
  api: options.rest?.api ?? "https://api.fluxer.app",
501
1321
  version: options.rest?.version ?? "1",
@@ -503,17 +1323,87 @@ var Client = class extends import_events.EventEmitter {
503
1323
  });
504
1324
  }
505
1325
  rest;
506
- guilds = new import_collection3.Collection();
507
- channels = new import_collection3.Collection();
508
- users = new import_collection3.Collection();
1326
+ guilds = new GuildManager(this);
1327
+ channels = new ChannelManager(this);
1328
+ users = new import_collection5.Collection();
509
1329
  user = null;
510
1330
  readyAt = null;
511
1331
  _ws = null;
1332
+ /**
1333
+ * Resolve an emoji argument to the API format (unicode or "name:id").
1334
+ * Supports: <:name:id>, :name:, name:id, { name, id }, unicode.
1335
+ * When id is missing (e.g. :name:), fetches guild emojis if guildId provided.
1336
+ * @param emoji - Emoji string or object
1337
+ * @param guildId - Guild ID for resolving custom emoji by name (required when id is missing)
1338
+ * @returns API-formatted string for reactions
1339
+ */
1340
+ async resolveEmoji(emoji, guildId) {
1341
+ if (typeof emoji === "object" && emoji.id) {
1342
+ return (0, import_util2.formatEmoji)({ name: emoji.name, id: emoji.id, animated: emoji.animated });
1343
+ }
1344
+ const parsed = (0, import_util2.parseEmoji)(typeof emoji === "string" ? emoji : `:${emoji.name}:`);
1345
+ if (!parsed) throw new Error("Invalid emoji");
1346
+ if (parsed.id) return (0, import_util2.formatEmoji)(parsed);
1347
+ if (guildId) {
1348
+ const emojis = await this.rest.get(import_types9.Routes.guildEmojis(guildId));
1349
+ const list = Array.isArray(emojis) ? emojis : Object.values(emojis ?? {});
1350
+ const found = list.find((e) => e.name && e.name.toLowerCase() === parsed.name.toLowerCase());
1351
+ if (found) return (0, import_util2.formatEmoji)({ ...parsed, id: found.id, animated: found.animated });
1352
+ throw new Error(
1353
+ `Custom emoji ":${parsed.name}:" not found in guild. Use name:id or <:name:id> format.`
1354
+ );
1355
+ }
1356
+ if (/^\w+$/.test(parsed.name)) {
1357
+ throw new Error(
1358
+ `Custom emoji ":${parsed.name}:" requires guild context. Use message.react() in a guild channel, or pass guildId to client.resolveEmoji().`
1359
+ );
1360
+ }
1361
+ return encodeURIComponent(parsed.name);
1362
+ }
1363
+ /**
1364
+ * Fetch a message by channel and message ID. Use when you have IDs (e.g. from a DB).
1365
+ * @param channelId - Snowflake of the channel
1366
+ * @param messageId - Snowflake of the message
1367
+ * @returns The message, or null if not found
1368
+ * @deprecated Use channel.messages.fetch(messageId). For IDs-only: (await client.channels.fetch(channelId))?.messages?.fetch(messageId)
1369
+ * @example
1370
+ * const channel = await client.channels.fetch(channelId);
1371
+ * const message = await channel?.messages?.fetch(messageId);
1372
+ */
1373
+ async fetchMessage(channelId, messageId) {
1374
+ return this.channels.fetchMessage(channelId, messageId);
1375
+ }
1376
+ /**
1377
+ * Send a message to any channel by ID. Shorthand for client.channels.send().
1378
+ * Works even when the channel is not cached.
1379
+ */
1380
+ async sendToChannel(channelId, payload) {
1381
+ return this.channels.send(channelId, payload);
1382
+ }
1383
+ /**
1384
+ * Get or create a User from API data. Caches in client.users.
1385
+ * Updates existing user's username, avatar, etc. when fresh data is provided.
1386
+ */
1387
+ getOrCreateUser(data) {
1388
+ const existing = this.users.get(data.id);
1389
+ if (existing) {
1390
+ existing._patch(data);
1391
+ return existing;
1392
+ }
1393
+ const user = new User(this, data);
1394
+ this.users.set(user.id, user);
1395
+ return user;
1396
+ }
1397
+ /** WebSocket manager. Throws if not logged in. */
512
1398
  get ws() {
513
1399
  if (!this._ws) throw new Error("Client is not logged in");
514
1400
  return this._ws;
515
1401
  }
516
- /** Send a payload to the gateway (e.g. Voice State Update). Uses shard 0 when single-shard. */
1402
+ /**
1403
+ * Send a payload to the gateway (e.g. Voice State Update).
1404
+ * @param shardId - Shard ID (use 0 for single-shard)
1405
+ * @param payload - Gateway payload to send
1406
+ */
517
1407
  sendToGateway(shardId, payload) {
518
1408
  this.ws.send(shardId, payload);
519
1409
  }
@@ -521,158 +1411,27 @@ var Client = class extends import_events.EventEmitter {
521
1411
  if (payload.op !== 0 || !payload.t) return;
522
1412
  const { t: event, d } = payload;
523
1413
  try {
524
- switch (event) {
525
- case "MESSAGE_CREATE": {
526
- const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
527
- this.emit(Events.MessageCreate, new Message2(this, d));
528
- break;
529
- }
530
- case "MESSAGE_UPDATE": {
531
- const { Message: Message2 } = await Promise.resolve().then(() => (init_Message(), Message_exports));
532
- this.emit(Events.MessageUpdate, null, new Message2(this, d));
533
- break;
534
- }
535
- case "MESSAGE_DELETE":
536
- this.emit(Events.MessageDelete, { id: d.id, channelId: d.channel_id });
537
- break;
538
- case "MESSAGE_REACTION_ADD":
539
- this.emit(Events.MessageReactionAdd, d);
540
- break;
541
- case "MESSAGE_REACTION_REMOVE":
542
- this.emit(Events.MessageReactionRemove, d);
543
- break;
544
- case "MESSAGE_REACTION_REMOVE_ALL":
545
- this.emit(Events.MessageReactionRemoveAll, d);
546
- break;
547
- case "MESSAGE_REACTION_REMOVE_EMOJI":
548
- this.emit(Events.MessageReactionRemoveEmoji, d);
549
- break;
550
- case "GUILD_CREATE": {
551
- const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
552
- const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
553
- const guild = new Guild2(this, d);
554
- this.guilds.set(guild.id, guild);
555
- const g = d;
556
- for (const ch of g.channels ?? []) {
557
- const channel = Channel2.from(this, ch);
558
- if (channel) this.channels.set(channel.id, channel);
559
- }
560
- this.emit(Events.GuildCreate, guild);
561
- if (g.voice_states?.length) {
562
- this.emit(Events.VoiceStatesSync, { guildId: guild.id, voiceStates: g.voice_states });
563
- }
564
- break;
565
- }
566
- case "GUILD_UPDATE": {
567
- const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
568
- const g = d;
569
- const old = this.guilds.get(g.id);
570
- const updated = new Guild2(this, g);
571
- this.guilds.set(updated.id, updated);
572
- this.emit(Events.GuildUpdate, old ?? updated, updated);
573
- break;
574
- }
575
- case "GUILD_DELETE": {
576
- const g = d;
577
- const guild = this.guilds.get(g.id);
578
- if (guild) {
579
- this.guilds.delete(g.id);
580
- this.emit(Events.GuildDelete, guild);
581
- }
582
- break;
583
- }
584
- case "CHANNEL_CREATE": {
585
- const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
586
- const ch = Channel2.from(this, d);
587
- if (ch) {
588
- this.channels.set(ch.id, ch);
589
- this.emit(Events.ChannelCreate, ch);
590
- }
591
- break;
592
- }
593
- case "CHANNEL_UPDATE": {
594
- const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
595
- const ch = d;
596
- const oldCh = this.channels.get(ch.id);
597
- const newCh = Channel2.from(this, ch);
598
- if (newCh) {
599
- this.channels.set(newCh.id, newCh);
600
- this.emit(Events.ChannelUpdate, oldCh ?? newCh, newCh);
601
- }
602
- break;
603
- }
604
- case "CHANNEL_DELETE": {
605
- const ch = d;
606
- const channel = this.channels.get(ch.id);
607
- if (channel) {
608
- this.channels.delete(ch.id);
609
- this.emit(Events.ChannelDelete, channel);
610
- }
611
- break;
612
- }
613
- case "GUILD_MEMBER_ADD": {
614
- const { GuildMember: GuildMember2 } = await Promise.resolve().then(() => (init_GuildMember(), GuildMember_exports));
615
- const data = d;
616
- const guild = this.guilds.get(data.guild_id);
617
- if (guild) {
618
- const member = new GuildMember2(this, data, guild);
619
- guild.members.set(member.id, member);
620
- this.emit(Events.GuildMemberAdd, member);
621
- }
622
- break;
623
- }
624
- case "GUILD_MEMBER_UPDATE": {
625
- const { GuildMember: GuildMember2 } = await Promise.resolve().then(() => (init_GuildMember(), GuildMember_exports));
626
- const data = d;
627
- const guild = this.guilds.get(data.guild_id);
628
- if (guild) {
629
- const oldM = guild.members.get(data.user.id);
630
- const newM = new GuildMember2(this, data, guild);
631
- guild.members.set(newM.id, newM);
632
- this.emit(Events.GuildMemberUpdate, oldM ?? newM, newM);
633
- }
634
- break;
635
- }
636
- case "GUILD_MEMBER_REMOVE": {
637
- const data = d;
638
- const guild = this.guilds.get(data.guild_id);
639
- if (guild) {
640
- const member = guild.members.get(data.user.id);
641
- if (member) {
642
- guild.members.delete(data.user.id);
643
- this.emit(Events.GuildMemberRemove, member);
644
- }
645
- }
646
- break;
647
- }
648
- case "INTERACTION_CREATE": {
649
- this.emit(Events.InteractionCreate, d);
650
- break;
651
- }
652
- case "VOICE_STATE_UPDATE": {
653
- this.emit(Events.VoiceStateUpdate, d);
654
- break;
655
- }
656
- case "VOICE_SERVER_UPDATE": {
657
- this.emit(Events.VoiceServerUpdate, d);
658
- break;
659
- }
660
- default:
661
- break;
662
- }
1414
+ const handler = eventHandlers.get(event);
1415
+ if (handler) await handler(this, d);
663
1416
  } catch (err) {
664
1417
  this.emit(Events.Error, err instanceof Error ? err : new Error(String(err)));
665
1418
  }
666
1419
  }
1420
+ /**
1421
+ * Connect to the Fluxer gateway and authenticate.
1422
+ * @param token - Bot token (e.g. from FLUXER_BOT_TOKEN)
1423
+ */
667
1424
  async login(token) {
1425
+ if (this._ws) {
1426
+ throw new FluxerError("Client is already logged in. Call destroy() first.");
1427
+ }
668
1428
  this.rest.setToken(token);
669
1429
  let intents = this.options.intents ?? 0;
670
1430
  if (intents !== 0) {
671
1431
  if (typeof process !== "undefined" && process.emitWarning) {
672
- process.emitWarning(
673
- "Fluxer does not support intents yet. Value has been set to 0.",
674
- { type: "FluxerIntents" }
675
- );
1432
+ process.emitWarning("Fluxer does not support intents yet. Value has been set to 0.", {
1433
+ type: "FluxerIntents"
1434
+ });
676
1435
  } else {
677
1436
  console.warn("Fluxer does not support intents yet. Value has been set to 0.");
678
1437
  }
@@ -687,33 +1446,44 @@ var Client = class extends import_events.EventEmitter {
687
1446
  WebSocket: this.options.WebSocket
688
1447
  });
689
1448
  this._ws.on("dispatch", ({ payload }) => {
690
- this.handleDispatch(payload);
1449
+ this.handleDispatch(payload).catch(
1450
+ (err) => this.emit(Events.Error, err instanceof Error ? err : new Error(String(err)))
1451
+ );
691
1452
  });
692
- this._ws.on("ready", async ({ data }) => {
693
- const { ClientUser: ClientUser2 } = await Promise.resolve().then(() => (init_ClientUser(), ClientUser_exports));
694
- const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
695
- const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
696
- this.user = new ClientUser2(this, data.user);
697
- for (const g of data.guilds ?? []) {
698
- const guild = new Guild2(this, g);
699
- this.guilds.set(guild.id, guild);
700
- const withCh = g;
701
- for (const ch of withCh.channels ?? []) {
702
- const channel = Channel2.from(this, ch);
703
- if (channel) this.channels.set(channel.id, channel);
704
- }
705
- if (withCh.voice_states?.length) {
706
- this.emit(Events.VoiceStatesSync, { guildId: guild.id, voiceStates: withCh.voice_states });
1453
+ this._ws.on(
1454
+ "ready",
1455
+ async ({
1456
+ data
1457
+ }) => {
1458
+ const { ClientUser: ClientUser2 } = await Promise.resolve().then(() => (init_ClientUser(), ClientUser_exports));
1459
+ const { Guild: Guild2 } = await Promise.resolve().then(() => (init_Guild(), Guild_exports));
1460
+ const { Channel: Channel2 } = await Promise.resolve().then(() => (init_Channel(), Channel_exports));
1461
+ this.user = new ClientUser2(this, data.user);
1462
+ for (const g of data.guilds ?? []) {
1463
+ const guild = new Guild2(this, g);
1464
+ this.guilds.set(guild.id, guild);
1465
+ const withCh = g;
1466
+ for (const ch of withCh.channels ?? []) {
1467
+ const channel = Channel2.from(this, ch);
1468
+ if (channel) this.channels.set(channel.id, channel);
1469
+ }
1470
+ if (withCh.voice_states?.length) {
1471
+ this.emit(Events.VoiceStatesSync, {
1472
+ guildId: guild.id,
1473
+ voiceStates: withCh.voice_states
1474
+ });
1475
+ }
707
1476
  }
1477
+ this.readyAt = /* @__PURE__ */ new Date();
1478
+ this.emit(Events.Ready);
708
1479
  }
709
- this.readyAt = /* @__PURE__ */ new Date();
710
- this.emit(Events.Ready);
711
- });
1480
+ );
712
1481
  this._ws.on("error", ({ error }) => this.emit(Events.Error, error));
713
1482
  this._ws.on("debug", (msg) => this.emit(Events.Debug, msg));
714
1483
  await this._ws.connect();
715
1484
  return token;
716
1485
  }
1486
+ /** Disconnect from the gateway and clear cached data. */
717
1487
  async destroy() {
718
1488
  if (this._ws) {
719
1489
  this._ws.destroy();
@@ -726,32 +1496,27 @@ var Client = class extends import_events.EventEmitter {
726
1496
  this.channels.clear();
727
1497
  this.users.clear();
728
1498
  }
1499
+ /** Returns true if the client has received Ready and `user` is set. */
729
1500
  isReady() {
730
1501
  return this.readyAt !== null && this.user !== null;
731
1502
  }
732
1503
  static get Routes() {
733
- return import_types5.Routes;
1504
+ return import_types9.Routes;
734
1505
  }
735
1506
  };
736
1507
 
737
1508
  // src/index.ts
1509
+ init_MessageManager();
738
1510
  init_ClientUser();
739
1511
  init_Base();
740
1512
  init_User();
741
1513
  init_Guild();
742
1514
  init_Channel();
743
1515
  init_Message();
1516
+ init_MessageReaction();
744
1517
  init_Webhook();
745
1518
  init_GuildMember();
746
-
747
- // src/errors/FluxerError.ts
748
- var FluxerError = class _FluxerError extends Error {
749
- constructor(message) {
750
- super(message);
751
- this.name = "FluxerError";
752
- Object.setPrototypeOf(this, _FluxerError.prototype);
753
- }
754
- };
1519
+ init_Role();
755
1520
 
756
1521
  // src/errors/ErrorCodes.ts
757
1522
  var ErrorCodes = {
@@ -761,15 +1526,17 @@ var ErrorCodes = {
761
1526
 
762
1527
  // src/index.ts
763
1528
  var import_builders2 = require("@fluxerjs/builders");
764
- var import_types6 = require("@fluxerjs/types");
1529
+ var import_types10 = require("@fluxerjs/types");
765
1530
  // Annotate the CommonJS export names for ESM import in node:
766
1531
  0 && (module.exports = {
767
1532
  AttachmentBuilder,
768
1533
  Base,
769
1534
  CategoryChannel,
770
1535
  Channel,
1536
+ ChannelManager,
771
1537
  Client,
772
1538
  ClientUser,
1539
+ DMChannel,
773
1540
  EmbedBuilder,
774
1541
  ErrorCodes,
775
1542
  Events,
@@ -780,7 +1547,10 @@ var import_types6 = require("@fluxerjs/types");
780
1547
  GuildMember,
781
1548
  LinkChannel,
782
1549
  Message,
1550
+ MessageManager,
783
1551
  MessagePayload,
1552
+ MessageReaction,
1553
+ Role,
784
1554
  Routes,
785
1555
  TextChannel,
786
1556
  User,