@fluxerjs/core 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -86,6 +86,7 @@ function buildSendBody(options) {
86
86
  filename: f.filename ?? f.name
87
87
  }));
88
88
  }
89
+ if (body.flags !== void 0) result.flags = body.flags;
89
90
  return result;
90
91
  }
91
92
 
@@ -99,7 +100,7 @@ var Base = class {
99
100
 
100
101
  // src/structures/Message.ts
101
102
  import { Collection as Collection2 } from "@fluxerjs/collection";
102
- import { MessageType, Routes } from "@fluxerjs/types";
103
+ import { MessageType, MessageFlags, Routes } from "@fluxerjs/types";
103
104
  import { EmbedBuilder as EmbedBuilder2 } from "@fluxerjs/builders";
104
105
 
105
106
  // src/util/ReactionCollector.ts
@@ -237,7 +238,10 @@ var Message = class _Message extends Base {
237
238
  mentionRoles;
238
239
  /** Client-side nonce for acknowledgment. Null if not provided. */
239
240
  nonce;
240
- /** Channel where this message was sent. Resolved from cache; null if not cached (e.g. DM channel not in cache). */
241
+ /**
242
+ * Channel where this message was sent. Resolved from cache; null if not cached.
243
+ * Messages can only exist in text-based channels (text, DM, announcement), so this always has send() when non-null.
244
+ */
241
245
  get channel() {
242
246
  return this.client.channels.get(this.channelId) ?? null;
243
247
  }
@@ -299,12 +303,8 @@ var Message = class _Message extends Base {
299
303
  * await message.send({ content: 'File', files: [{ name: 'data.txt', data }] });
300
304
  */
301
305
  async send(options) {
302
- const opts = typeof options === "string" ? { content: options } : options;
303
- const body = buildSendBody(options);
304
- const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
305
- const postOptions = files?.length ? { body, files } : { body };
306
- const data = await this.client.rest.post(Routes.channelMessages(this.channelId), postOptions);
307
- return new _Message(this.client, data);
306
+ const payload = await _Message._createMessageBody(options);
307
+ return this._send(payload);
308
308
  }
309
309
  /**
310
310
  * Send a message to a specific channel. Use for logging, forwarding, or sending to another channel in the guild.
@@ -319,25 +319,46 @@ var Message = class _Message extends Base {
319
319
  }
320
320
  /**
321
321
  * Reply to this message (shows as a reply in the client).
322
- * @param options - Text content or object with content, embeds, and/or files
322
+ * @param options - Text content or object with content, embeds, and/or reply options (ping, replyTo)
323
323
  * @example
324
324
  * await message.reply('Pong!');
325
325
  * await message.reply({ embeds: [embed] });
326
+ * await message.reply('No ping!', { ping: false });
327
+ * await message.reply({ content: 'Reply to other', replyTo: otherMessage });
326
328
  */
327
- async reply(options) {
329
+ async reply(options, replyOptions) {
328
330
  const opts = typeof options === "string" ? { content: options } : options;
329
- const base = buildSendBody(options);
330
- const body = {
331
- ...base,
332
- message_reference: {
333
- channel_id: this.channelId,
334
- message_id: this.id,
335
- guild_id: this.guildId ?? void 0
331
+ const mergedReply = replyOptions ?? (opts.ping !== void 0 || opts.replyTo !== void 0) ? { ping: opts.ping, replyTo: opts.replyTo } : void 0;
332
+ const refMessage = mergedReply?.replyTo ?? this;
333
+ const ref = refMessage instanceof _Message ? { channel_id: refMessage.channelId, message_id: refMessage.id, guild_id: refMessage.guildId ?? void 0 } : { channel_id: refMessage.channelId, message_id: refMessage.messageId, guild_id: void 0 };
334
+ const payload = await _Message._createMessageBody(opts, ref, mergedReply?.ping !== false);
335
+ return this._send(payload);
336
+ }
337
+ /** Exposed for testing purposes, use Message.reply() or send() for normal use */
338
+ static async _createMessageBody(content, referenced_message, ping) {
339
+ if (typeof content === "string") {
340
+ if (content.length === 0) {
341
+ throw new RangeError("Cannot send an empty message");
336
342
  }
337
- };
338
- const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
339
- const postOptions = files?.length ? { body, files } : { body };
340
- const data = await this.client.rest.post(Routes.channelMessages(this.channelId), postOptions);
343
+ content = { content };
344
+ }
345
+ const base = buildSendBody(content);
346
+ const files = content.files?.length ? await resolveMessageFiles(content.files) : void 0;
347
+ const body = { ...base };
348
+ if (referenced_message) {
349
+ body.message_reference = referenced_message;
350
+ if (ping === false) {
351
+ body.flags = (body.flags ?? 0) | MessageFlags.SuppressNotifications;
352
+ }
353
+ }
354
+ return { files, body };
355
+ }
356
+ async _send(payload) {
357
+ const data = await this.client.rest.post(
358
+ Routes.channelMessages(this.channelId),
359
+ payload
360
+ );
361
+ this.client._addMessageToCache(this.channelId, data);
341
362
  return new _Message(this.client, data);
342
363
  }
343
364
  /**
@@ -474,8 +495,18 @@ var MessageManager = class {
474
495
  this.client = client;
475
496
  this.channelId = channelId;
476
497
  }
498
+ /**
499
+ * Get a message from cache. Returns undefined if not cached or caching is disabled.
500
+ * Requires options.cache.messages > 0.
501
+ * @param messageId - Snowflake of the message
502
+ */
503
+ get(messageId) {
504
+ const data = this.client._getMessageCache(this.channelId)?.get(messageId);
505
+ return data ? new Message(this.client, data) : void 0;
506
+ }
477
507
  /**
478
508
  * Fetch a message by ID from this channel.
509
+ * When message caching is enabled, the fetched message is added to the cache.
479
510
  * @param messageId - Snowflake of the message
480
511
  * @returns The message
481
512
  * @throws FluxerError with MESSAGE_NOT_FOUND if the message does not exist
@@ -485,6 +516,7 @@ var MessageManager = class {
485
516
  const data = await this.client.rest.get(
486
517
  Routes2.channelMessage(this.channelId, messageId)
487
518
  );
519
+ this.client._addMessageToCache(this.channelId, data);
488
520
  return new Message(this.client, data);
489
521
  } catch (err) {
490
522
  if (err instanceof RateLimitError) throw err;
@@ -553,6 +585,7 @@ var MessageCollector = class extends EventEmitter2 {
553
585
 
554
586
  // src/structures/Channel.ts
555
587
  import { ChannelType, Routes as Routes5 } from "@fluxerjs/types";
588
+ import { PermissionFlags } from "@fluxerjs/util";
556
589
  import { emitDeprecationWarning } from "@fluxerjs/util";
557
590
 
558
591
  // src/structures/Webhook.ts
@@ -688,7 +721,7 @@ var Webhook = class _Webhook extends Base {
688
721
  );
689
722
  }
690
723
  const opts = typeof options === "string" ? { content: options } : options;
691
- const body = buildSendBody(options);
724
+ const body = buildSendBody(opts);
692
725
  if (opts.username !== void 0) body.username = opts.username;
693
726
  if (opts.avatar_url !== void 0) body.avatar_url = opts.avatar_url;
694
727
  if (opts.tts !== void 0) body.tts = opts.tts;
@@ -789,7 +822,7 @@ var Invite = class extends Base {
789
822
  // src/structures/Channel.ts
790
823
  var Channel = class _Channel extends Base {
791
824
  /** Whether this channel has a send method (TextChannel, DMChannel). */
792
- isSendable() {
825
+ isTextBased() {
793
826
  return "send" in this;
794
827
  }
795
828
  /** Whether this channel is a DM or Group DM. */
@@ -800,6 +833,9 @@ var Channel = class _Channel extends Base {
800
833
  isVoice() {
801
834
  return "bitrate" in this;
802
835
  }
836
+ isLink() {
837
+ return "url" in this;
838
+ }
803
839
  /** Create a DM channel from API data (type DM or GroupDM). */
804
840
  static createDM(client, data) {
805
841
  return new DMChannel(client, data);
@@ -863,6 +899,15 @@ var Channel = class _Channel extends Base {
863
899
  async sendTyping() {
864
900
  await this.client.rest.post(Routes5.channelTyping(this.id), { auth: true });
865
901
  }
902
+ /**
903
+ * Whether the bot can send messages in this channel.
904
+ * For DMs: always true (when the channel exists).
905
+ * For guild channels: checks ViewChannel and SendMessages permissions via guild.members.me.
906
+ */
907
+ canSendMessage() {
908
+ if (this.isDM()) return true;
909
+ return false;
910
+ }
866
911
  };
867
912
  var GuildChannel = class extends Channel {
868
913
  guildId;
@@ -946,6 +991,32 @@ var GuildChannel = class extends Channel {
946
991
  if (idx >= 0) this.permissionOverwrites[idx] = entry;
947
992
  else this.permissionOverwrites.push(entry);
948
993
  }
994
+ /**
995
+ * Whether the bot can send messages in this channel.
996
+ * Checks ViewChannel and SendMessages via guild.members.me permissions.
997
+ * Returns false if guild or bot member not cached.
998
+ */
999
+ canSendMessage() {
1000
+ const guild = this.client.guilds.get(this.guildId);
1001
+ if (!guild) return false;
1002
+ const me = guild.members.me;
1003
+ if (!me) return false;
1004
+ const perms = me.permissionsIn(this);
1005
+ return perms.has(PermissionFlags.ViewChannel) && perms.has(PermissionFlags.SendMessages);
1006
+ }
1007
+ /**
1008
+ * Send a message to this guild channel.
1009
+ * Works for text and announcement channels. Voice/category/link channels will fail at the API.
1010
+ */
1011
+ async send(options) {
1012
+ const opts = typeof options === "string" ? { content: options } : options;
1013
+ const body = buildSendBody(options);
1014
+ const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
1015
+ const postOptions = files?.length ? { body, files } : { body };
1016
+ const data = await this.client.rest.post(Routes5.channelMessages(this.id), postOptions);
1017
+ this.client._addMessageToCache(this.id, data);
1018
+ return new Message(this.client, data);
1019
+ }
949
1020
  /**
950
1021
  * Remove a permission overwrite. DELETE /channels/{id}/permissions/{overwriteId}.
951
1022
  */
@@ -1009,6 +1080,7 @@ var TextChannel = class extends GuildChannel {
1009
1080
  const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
1010
1081
  const postOptions = files?.length ? { body, files } : { body };
1011
1082
  const data = await this.client.rest.post(Routes5.channelMessages(this.id), postOptions);
1083
+ this.client._addMessageToCache(this.id, data);
1012
1084
  return new Message(this.client, data);
1013
1085
  }
1014
1086
  /** Message manager for this channel. Use channel.messages.fetch(messageId). */
@@ -1100,6 +1172,7 @@ var DMChannel = class extends Channel {
1100
1172
  const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
1101
1173
  const postOptions = files?.length ? { body, files } : { body };
1102
1174
  const data = await this.client.rest.post(Routes5.channelMessages(this.id), postOptions);
1175
+ this.client._addMessageToCache(this.id, data);
1103
1176
  return new Message(this.client, data);
1104
1177
  }
1105
1178
  /** Message manager for this channel. Use channel.messages.fetch(messageId). */
@@ -1183,7 +1256,7 @@ var ChannelManager = class extends Collection4 {
1183
1256
  * @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
1184
1257
  * @example
1185
1258
  * const channel = await client.channels.resolve(message.channelId);
1186
- * if (channel?.isSendable()) await channel.send('Hello!');
1259
+ * if (channel?.isTextBased()) await channel.send('Hello!');
1187
1260
  */
1188
1261
  async resolve(channelId) {
1189
1262
  return this.get(channelId) ?? this.fetch(channelId);
@@ -1195,7 +1268,7 @@ var ChannelManager = class extends Collection4 {
1195
1268
  * @throws FluxerError with CHANNEL_NOT_FOUND if the channel does not exist
1196
1269
  * @example
1197
1270
  * const channel = await client.channels.fetch(channelId);
1198
- * if (channel?.isSendable()) await channel.send('Hello!');
1271
+ * if (channel?.isTextBased()) await channel.send('Hello!');
1199
1272
  */
1200
1273
  async fetch(channelId) {
1201
1274
  const cached = this.get(channelId);
@@ -1274,6 +1347,7 @@ var ChannelManager = class extends Collection4 {
1274
1347
  const files = opts.files?.length ? await resolveMessageFiles(opts.files) : void 0;
1275
1348
  const postOptions = files?.length ? { body, files } : { body };
1276
1349
  const data = await this.client.rest.post(Routes6.channelMessages(channelId), postOptions);
1350
+ this.client._addMessageToCache(channelId, data);
1277
1351
  return new Message(this.client, data);
1278
1352
  }
1279
1353
  };
@@ -1295,12 +1369,12 @@ import { Collection as Collection6 } from "@fluxerjs/collection";
1295
1369
  import { Routes as Routes9 } from "@fluxerjs/types";
1296
1370
 
1297
1371
  // src/structures/GuildMember.ts
1298
- import { PermissionFlagsMap } from "@fluxerjs/util";
1372
+ import { BitField } from "@fluxerjs/util";
1299
1373
  import { Routes as Routes8 } from "@fluxerjs/types";
1300
1374
 
1301
1375
  // src/util/permissions.ts
1302
1376
  import { OverwriteType } from "@fluxerjs/types";
1303
- import { ALL_PERMISSIONS_BIGINT } from "@fluxerjs/util";
1377
+ import { ALL_PERMISSIONS_BIGINT, PermissionFlags as PermissionFlags2 } from "@fluxerjs/util";
1304
1378
  function computePermissions(basePermissions, overwrites, memberRoles, memberId, isOwner) {
1305
1379
  if (isOwner) return ALL_PERMISSIONS_BIGINT;
1306
1380
  let perms = basePermissions;
@@ -1311,12 +1385,7 @@ function computePermissions(basePermissions, overwrites, memberRoles, memberId,
1311
1385
  const deny = BigInt(overwrite.deny || "0");
1312
1386
  perms = perms & ~deny | allow;
1313
1387
  }
1314
- return perms;
1315
- }
1316
- function hasPermission(bitfield, permission) {
1317
- const Administrator = 1n << 3n;
1318
- if ((bitfield & Administrator) !== 0n) return true;
1319
- return (bitfield & permission) === permission;
1388
+ return (perms & PermissionFlags2.Administrator) !== 0n ? ALL_PERMISSIONS_BIGINT : perms;
1320
1389
  }
1321
1390
 
1322
1391
  // src/structures/GuildMemberRoleManager.ts
@@ -1518,13 +1587,7 @@ var GuildMember = class extends Base {
1518
1587
  const ownerId = this.guild.ownerId;
1519
1588
  const isOwner = ownerId != null && ownerId !== "" && String(ownerId) === String(this.id);
1520
1589
  const perms = computePermissions(base, [], [], this.id, isOwner);
1521
- return {
1522
- has(permission) {
1523
- const perm = typeof permission === "number" ? permission : PermissionFlagsMap[String(permission)];
1524
- if (perm === void 0) return false;
1525
- return hasPermission(perms, BigInt(perm));
1526
- }
1527
- };
1590
+ return new BitField(perms);
1528
1591
  }
1529
1592
  /**
1530
1593
  * Compute the member's effective permissions in a guild channel.
@@ -1546,22 +1609,16 @@ var GuildMember = class extends Base {
1546
1609
  this.id,
1547
1610
  isOwner
1548
1611
  );
1549
- return {
1550
- has(permission) {
1551
- const perm = typeof permission === "number" ? permission : PermissionFlagsMap[String(permission)];
1552
- if (perm === void 0) return false;
1553
- return hasPermission(perms, BigInt(perm));
1554
- }
1555
- };
1612
+ return new BitField(perms);
1556
1613
  }
1557
1614
  _computeBasePermissions() {
1558
1615
  let base = 0n;
1559
1616
  const everyone = this.guild.roles.get(this.guild.id);
1560
- if (everyone) base |= BigInt(everyone.permissions);
1617
+ if (everyone) base |= everyone.permissions.bitfield;
1561
1618
  for (const roleId of this.roles.roleIds) {
1562
1619
  if (roleId === this.guild.id) continue;
1563
1620
  const role = this.guild.roles.get(roleId);
1564
- if (role) base |= BigInt(role.permissions);
1621
+ if (role) base |= role.permissions.bitfield;
1565
1622
  }
1566
1623
  return base;
1567
1624
  }
@@ -1646,8 +1703,10 @@ var GuildMemberManager = class extends Collection6 {
1646
1703
  // src/structures/Role.ts
1647
1704
  import { Routes as Routes10 } from "@fluxerjs/types";
1648
1705
  import {
1649
- PermissionFlags,
1650
- resolvePermissionsToBitfield
1706
+ PermissionFlags as PermissionFlags4,
1707
+ resolvePermissionsToBitfield,
1708
+ ALL_PERMISSIONS_BIGINT as ALL_PERMISSIONS_BIGINT2,
1709
+ PermissionsBitField
1651
1710
  } from "@fluxerjs/util";
1652
1711
  var Role = class extends Base {
1653
1712
  client;
@@ -1656,7 +1715,7 @@ var Role = class extends Base {
1656
1715
  name;
1657
1716
  color;
1658
1717
  position;
1659
- permissions;
1718
+ _permissions;
1660
1719
  hoist;
1661
1720
  mentionable;
1662
1721
  unicodeEmoji;
@@ -1673,18 +1732,24 @@ var Role = class extends Base {
1673
1732
  this.name = data.name;
1674
1733
  this.color = data.color;
1675
1734
  this.position = data.position;
1676
- this.permissions = data.permissions;
1735
+ this._permissions = data.permissions;
1677
1736
  this.hoist = !!data.hoist;
1678
1737
  this.mentionable = !!data.mentionable;
1679
1738
  this.unicodeEmoji = data.unicode_emoji ?? null;
1680
1739
  this.hoistPosition = data.hoist_position ?? null;
1681
1740
  }
1741
+ get permissions() {
1742
+ const bits = BigInt(this._permissions);
1743
+ return new PermissionsBitField(
1744
+ (bits & PermissionFlags4.Administrator) !== 0n ? ALL_PERMISSIONS_BIGINT2 : bits
1745
+ );
1746
+ }
1682
1747
  /** Update mutable fields from fresh API data. Used by edit and gateway events. */
1683
1748
  _patch(data) {
1684
1749
  if (data.name !== void 0) this.name = data.name;
1685
1750
  if (data.color !== void 0) this.color = data.color;
1686
1751
  if (data.position !== void 0) this.position = data.position;
1687
- if (data.permissions !== void 0) this.permissions = data.permissions;
1752
+ if (data.permissions !== void 0) this._permissions = data.permissions;
1688
1753
  if (data.hoist !== void 0) this.hoist = !!data.hoist;
1689
1754
  if (data.mentionable !== void 0) this.mentionable = !!data.mentionable;
1690
1755
  if (data.unicode_emoji !== void 0) this.unicodeEmoji = data.unicode_emoji ?? null;
@@ -1694,24 +1759,6 @@ var Role = class extends Base {
1694
1759
  toString() {
1695
1760
  return `<@&${this.id}>`;
1696
1761
  }
1697
- /**
1698
- * Check if this role has a permission. Administrator grants all permissions.
1699
- * @param permission - Permission flag, name, or resolvable
1700
- * @returns true if the role has the permission
1701
- * @example
1702
- * if (role.has(PermissionFlags.BanMembers)) { ... }
1703
- * if (role.has('ManageChannels')) { ... }
1704
- */
1705
- has(permission) {
1706
- const perm = typeof permission === "number" ? permission : PermissionFlags[permission];
1707
- if (perm === void 0) return false;
1708
- const permNum = Number(perm);
1709
- const rolePerms = BigInt(this.permissions);
1710
- const permBig = BigInt(permNum);
1711
- if (permBig < 0) return false;
1712
- if ((rolePerms & BigInt(PermissionFlags.Administrator)) !== 0n) return true;
1713
- return (rolePerms & permBig) === permBig;
1714
- }
1715
1762
  /**
1716
1763
  * Edit this role.
1717
1764
  * Requires Manage Roles permission.
@@ -2756,13 +2803,28 @@ handlers.set("MESSAGE_CREATE", async (client, d) => {
2756
2803
  guild.members.set(member.id, member);
2757
2804
  }
2758
2805
  }
2806
+ client._addMessageToCache(data.channel_id, data);
2759
2807
  client.emit(Events.MessageCreate, new Message(client, data));
2760
2808
  });
2761
2809
  handlers.set("MESSAGE_UPDATE", async (client, d) => {
2762
- client.emit(Events.MessageUpdate, null, new Message(client, d));
2810
+ const partial = d;
2811
+ const cache = client._getMessageCache(partial.channel_id);
2812
+ let oldMessage = null;
2813
+ let mergedData = partial;
2814
+ if (cache) {
2815
+ const oldData = cache.get(partial.id);
2816
+ if (oldData) {
2817
+ oldMessage = new Message(client, oldData);
2818
+ mergedData = { ...oldData, ...partial };
2819
+ }
2820
+ cache.set(partial.id, mergedData);
2821
+ }
2822
+ const newMessage = new Message(client, mergedData);
2823
+ client.emit(Events.MessageUpdate, oldMessage, newMessage);
2763
2824
  });
2764
2825
  handlers.set("MESSAGE_DELETE", async (client, d) => {
2765
2826
  const data = d;
2827
+ client._removeMessageFromCache(data.channel_id, data.id);
2766
2828
  const channel = client.channels.get(data.channel_id) ?? null;
2767
2829
  client.emit(Events.MessageDelete, {
2768
2830
  id: data.id,
@@ -2825,6 +2887,7 @@ handlers.set("GUILD_CREATE", async (client, d) => {
2825
2887
  if (g.voice_states?.length) {
2826
2888
  client.emit(Events.VoiceStatesSync, { guildId: guild.id, voiceStates: g.voice_states });
2827
2889
  }
2890
+ client._onGuildReceived(guild.id);
2828
2891
  });
2829
2892
  handlers.set("GUILD_UPDATE", async (client, d) => {
2830
2893
  const guildData = normalizeGuildPayload(d);
@@ -2931,7 +2994,11 @@ handlers.set("VOICE_SERVER_UPDATE", async (client, d) => {
2931
2994
  client.emit(Events.VoiceServerUpdate, d);
2932
2995
  });
2933
2996
  handlers.set("MESSAGE_DELETE_BULK", async (client, d) => {
2934
- client.emit(Events.MessageDeleteBulk, d);
2997
+ const data = d;
2998
+ for (const id of data.ids ?? []) {
2999
+ client._removeMessageFromCache(data.channel_id, id);
3000
+ }
3001
+ client.emit(Events.MessageDeleteBulk, data);
2935
3002
  });
2936
3003
  handlers.set("GUILD_BAN_ADD", async (client, d) => {
2937
3004
  const data = d;
@@ -2957,7 +3024,11 @@ handlers.set("GUILD_EMOJIS_UPDATE", async (client, d) => {
2957
3024
  if (!e.id || e.name == null) continue;
2958
3025
  guild.emojis.set(
2959
3026
  e.id,
2960
- new GuildEmoji(client, { id: e.id, name: e.name, animated: e.animated ?? false, guild_id: guild.id }, guild.id)
3027
+ new GuildEmoji(
3028
+ client,
3029
+ { id: e.id, name: e.name, animated: e.animated ?? false, guild_id: guild.id },
3030
+ guild.id
3031
+ )
2961
3032
  );
2962
3033
  }
2963
3034
  }
@@ -3083,6 +3154,10 @@ var Client = class extends EventEmitter3 {
3083
3154
  /** Timestamp when the client became ready. Null until READY is received. */
3084
3155
  readyAt = null;
3085
3156
  _ws = null;
3157
+ /** When waitForGuilds, set of guild IDs we're waiting for GUILD_CREATE on. Null when not waiting. */
3158
+ _pendingGuildIds = null;
3159
+ /** Per-channel message cache (channelId -> messageId -> APIMessage). Used when options.cache.messages > 0. */
3160
+ _messageCaches = null;
3086
3161
  /**
3087
3162
  * Resolve an emoji argument to the API format (unicode or "name:id").
3088
3163
  * Supports: <:name:id>, :name:, name:id, { name, id }, unicode.
@@ -3171,10 +3246,52 @@ var Client = class extends EventEmitter3 {
3171
3246
  /**
3172
3247
  * Send a message to any channel by ID. Shorthand for client.channels.send().
3173
3248
  * Works even when the channel is not cached.
3249
+ * @deprecated Use client.channels.send(channelId, payload).
3174
3250
  */
3175
3251
  async sendToChannel(channelId, payload) {
3252
+ emitDeprecationWarning3(
3253
+ "Client.sendToChannel()",
3254
+ "Use client.channels.send(channelId, payload)."
3255
+ );
3176
3256
  return this.channels.send(channelId, payload);
3177
3257
  }
3258
+ /**
3259
+ * Get the message cache for a channel. Returns null if message caching is disabled.
3260
+ * Used by MessageManager.get() and event handlers.
3261
+ * @internal
3262
+ */
3263
+ _getMessageCache(channelId) {
3264
+ const limit = this.options.cache?.messages ?? 0;
3265
+ if (limit <= 0) return null;
3266
+ if (!this._messageCaches) this._messageCaches = /* @__PURE__ */ new Map();
3267
+ let cache = this._messageCaches.get(channelId);
3268
+ if (!cache) {
3269
+ cache = /* @__PURE__ */ new Map();
3270
+ this._messageCaches.set(channelId, cache);
3271
+ }
3272
+ return cache;
3273
+ }
3274
+ /**
3275
+ * Add a message to the channel cache. Evicts oldest (FIFO) when over limit.
3276
+ * @internal
3277
+ */
3278
+ _addMessageToCache(channelId, data) {
3279
+ const cache = this._getMessageCache(channelId);
3280
+ if (!cache) return;
3281
+ const limit = this.options.cache?.messages ?? 0;
3282
+ if (limit > 0 && cache.size >= limit && !cache.has(data.id)) {
3283
+ const firstKey = cache.keys().next().value;
3284
+ if (firstKey !== void 0) cache.delete(firstKey);
3285
+ }
3286
+ cache.set(data.id, { ...data });
3287
+ }
3288
+ /**
3289
+ * Remove a message from the channel cache.
3290
+ * @internal
3291
+ */
3292
+ _removeMessageFromCache(channelId, messageId) {
3293
+ this._messageCaches?.get(channelId)?.delete(messageId);
3294
+ }
3178
3295
  /**
3179
3296
  * Get or create a User from API data. Caches in client.users.
3180
3297
  * Updates existing user's username, avatar, etc. when fresh data is provided.
@@ -3255,7 +3372,13 @@ var Client = class extends EventEmitter3 {
3255
3372
  data
3256
3373
  }) => {
3257
3374
  this.user = new ClientUser(this, data.user);
3375
+ const waitForGuilds = this.options.waitForGuilds === true;
3376
+ const pending = waitForGuilds ? /* @__PURE__ */ new Set() : null;
3258
3377
  for (const g of data.guilds ?? []) {
3378
+ if (g.unavailable === true) {
3379
+ if (pending !== null && g.id) pending.add(g.id);
3380
+ continue;
3381
+ }
3259
3382
  const guildData = normalizeGuildPayload(g);
3260
3383
  if (!guildData) continue;
3261
3384
  const guild = new Guild(this, guildData);
@@ -3275,8 +3398,11 @@ var Client = class extends EventEmitter3 {
3275
3398
  });
3276
3399
  }
3277
3400
  }
3278
- this.readyAt = /* @__PURE__ */ new Date();
3279
- this.emit(Events.Ready);
3401
+ if (pending !== null && pending.size > 0) {
3402
+ this._pendingGuildIds = pending;
3403
+ return;
3404
+ }
3405
+ this._finalizeReady();
3280
3406
  }
3281
3407
  );
3282
3408
  this._ws.on("error", ({ error }) => this.emit(Events.Error, error));
@@ -3284,6 +3410,25 @@ var Client = class extends EventEmitter3 {
3284
3410
  await this._ws.connect();
3285
3411
  return token;
3286
3412
  }
3413
+ /**
3414
+ * Called when all guilds have been received (or immediately if not waiting).
3415
+ * Sets readyAt, emits Ready, clears pending state.
3416
+ */
3417
+ _finalizeReady() {
3418
+ this._pendingGuildIds = null;
3419
+ this.readyAt = /* @__PURE__ */ new Date();
3420
+ this.emit(Events.Ready);
3421
+ }
3422
+ /**
3423
+ * Called by GUILD_CREATE handler when waitForGuilds is enabled.
3424
+ * Removes guild from pending set; when empty, finalizes ready.
3425
+ */
3426
+ _onGuildReceived(guildId) {
3427
+ const pending = this._pendingGuildIds;
3428
+ if (pending === null) return;
3429
+ pending.delete(guildId);
3430
+ if (pending.size === 0) this._finalizeReady();
3431
+ }
3287
3432
  /** Disconnect from the gateway and clear cached data. */
3288
3433
  async destroy() {
3289
3434
  if (this._ws) {
@@ -3293,6 +3438,7 @@ var Client = class extends EventEmitter3 {
3293
3438
  this.rest.setToken(null);
3294
3439
  this.user = null;
3295
3440
  this.readyAt = null;
3441
+ this._pendingGuildIds = null;
3296
3442
  this.guilds.clear();
3297
3443
  this.channels.clear();
3298
3444
  this.users.clear();
@@ -3325,8 +3471,8 @@ import { EmbedBuilder as EmbedBuilder3, MessagePayload, AttachmentBuilder } from
3325
3471
  import { Routes as Routes21, GatewayOpcodes, MessageAttachmentFlags } from "@fluxerjs/types";
3326
3472
  import { resolveTenorToImageUrl, parseUserMention, parsePrefixCommand } from "@fluxerjs/util";
3327
3473
  import {
3328
- PermissionsBitField,
3329
- PermissionFlags as PermissionFlags2,
3474
+ PermissionsBitField as PermissionsBitField2,
3475
+ PermissionFlags as PermissionFlags5,
3330
3476
  resolvePermissionsToBitfield as resolvePermissionsToBitfield3,
3331
3477
  UserFlagsBitField,
3332
3478
  UserFlagsBits
@@ -3362,8 +3508,8 @@ export {
3362
3508
  MessageManager,
3363
3509
  MessagePayload,
3364
3510
  MessageReaction,
3365
- PermissionFlags2 as PermissionFlags,
3366
- PermissionsBitField,
3511
+ PermissionFlags5 as PermissionFlags,
3512
+ PermissionsBitField2 as PermissionsBitField,
3367
3513
  ReactionCollector,
3368
3514
  Role,
3369
3515
  Routes21 as Routes,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.1",
6
+ "version": "1.2.3",
7
7
  "description": "A fully-featured SDK for Fluxer bots",
8
8
  "repository": {
9
9
  "type": "git",
@@ -34,12 +34,12 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@fluxerjs/rest": "1.2.1",
38
- "@fluxerjs/ws": "1.2.1",
39
- "@fluxerjs/builders": "1.2.1",
40
- "@fluxerjs/collection": "1.2.1",
41
- "@fluxerjs/util": "1.2.1",
42
- "@fluxerjs/types": "1.2.1"
37
+ "@fluxerjs/types": "1.2.3",
38
+ "@fluxerjs/rest": "1.2.3",
39
+ "@fluxerjs/collection": "1.2.3",
40
+ "@fluxerjs/builders": "1.2.3",
41
+ "@fluxerjs/ws": "1.2.3",
42
+ "@fluxerjs/util": "1.2.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^20.0.0",
@@ -55,6 +55,6 @@
55
55
  "lint": "eslint src --max-warnings 0 --config ../../eslint.config.js",
56
56
  "lint:fix": "eslint src --fix --config ../../eslint.config.js",
57
57
  "test": "vitest run --passWithNoTests",
58
- "test:coverage": "vitest run --coverage --passWithNoTests"
58
+ "test:coverage": "vitest run --coverage"
59
59
  }
60
60
  }