lunibee 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +2 -0
  2. package/dist/builders/components.d.ts +30 -2
  3. package/dist/builders/embed.d.ts +7 -7
  4. package/dist/builders/index.d.ts +1 -1
  5. package/dist/builders/index.js +110 -18
  6. package/dist/builders/index.js.map +6 -6
  7. package/dist/collection/index.d.ts +68 -1
  8. package/dist/collection/index.js +334 -1
  9. package/dist/collection/index.js.map +3 -3
  10. package/dist/core/collector.d.ts +13 -0
  11. package/dist/core/events.d.ts +29 -3
  12. package/dist/core/index.d.ts +25 -9
  13. package/dist/core/index.js +1745 -134
  14. package/dist/core/index.js.map +30 -26
  15. package/dist/core/permissions.d.ts +48 -0
  16. package/dist/index.js +2102 -362
  17. package/dist/index.js.map +36 -34
  18. package/dist/managers/advanced.d.ts +116 -0
  19. package/dist/managers/base.d.ts +13 -2
  20. package/dist/managers/emoji.d.ts +1 -1
  21. package/dist/managers/guild-resources.d.ts +22 -2
  22. package/dist/managers/guild.d.ts +31 -3
  23. package/dist/managers/index.d.ts +38 -6
  24. package/dist/managers/index.js +996 -160
  25. package/dist/managers/index.js.map +20 -18
  26. package/dist/managers/member.d.ts +13 -12
  27. package/dist/managers/message.d.ts +21 -4
  28. package/dist/rest/errors.d.ts +33 -0
  29. package/dist/rest/index.d.ts +1 -1
  30. package/dist/rest/index.js +52 -6
  31. package/dist/rest/index.js.map +5 -5
  32. package/dist/rest/routes.d.ts +20 -0
  33. package/dist/sharding/index.d.ts +29 -0
  34. package/dist/sharding/index.js +99 -15
  35. package/dist/sharding/index.js.map +8 -8
  36. package/dist/structures/base.d.ts +38 -0
  37. package/dist/structures/index.d.ts +29 -0
  38. package/dist/structures/index.js +208 -46
  39. package/dist/structures/index.js.map +13 -12
  40. package/dist/structures/interactions.d.ts +51 -1
  41. package/dist/structures/options.d.ts +2 -0
  42. package/dist/structures/resources.d.ts +29 -3
  43. package/dist/types/gateway.d.ts +16 -0
  44. package/dist/types/index.d.ts +67 -1
  45. package/dist/types/index.js.map +3 -3
  46. package/dist/utils/args.d.ts +79 -0
  47. package/dist/utils/index.d.ts +1 -0
  48. package/dist/utils/index.js +190 -2
  49. package/dist/utils/index.js.map +5 -4
  50. package/dist/voice/index.d.ts +3 -0
  51. package/dist/voice/index.js +15 -1
  52. package/dist/voice/index.js.map +3 -3
  53. package/dist/ws/index.js +13 -5
  54. package/dist/ws/index.js.map +6 -6
  55. package/package.json +14 -14
@@ -24,7 +24,32 @@ export interface ResourceContext {
24
24
  removeAllReactions?(channelId: string, messageId: string): Promise<void>;
25
25
  pinMessage?(channelId: string, messageId: string): Promise<void>;
26
26
  unpinMessage?(channelId: string, messageId: string): Promise<void>;
27
+ /**
28
+ * A member's permissions from cached state: guild-level, or channel-level
29
+ * (with overwrites) when a channel ID is given. Null when the guild (or
30
+ * channel) is not cached.
31
+ */
32
+ memberPermissions?(guildId: string, memberId: string, roleIds: readonly string[], channelId?: string): import("@lunibee/core").PermissionSet | null;
33
+ kickMember?(guildId: string, userId: string, reason?: string): Promise<void>;
34
+ banMember?(guildId: string, userId: string, options?: {
35
+ reason?: string;
36
+ deleteMessageSeconds?: number;
37
+ }): Promise<void>;
38
+ editMember?(guildId: string, userId: string, options: Record<string, unknown>, reason?: string): Promise<import("./resources.js").GuildMember>;
39
+ addMemberRole?(guildId: string, userId: string, roleId: string, reason?: string): Promise<void>;
40
+ removeMemberRole?(guildId: string, userId: string, roleId: string, reason?: string): Promise<void>;
41
+ /** Collects the client's interactions (a `Collector` over `interactionCreate`). Used by message and interaction collectors. */
42
+ collectInteractions?(options: import("@lunibee/core").CollectorOptions<import("./interactions.js").Interaction>): import("@lunibee/core").Collector<string, import("./interactions.js").Interaction>;
43
+ editPermissionOverwrite?(channelId: string, targetId: string, changes: PermissionOverwriteChanges, options?: {
44
+ type?: 0 | 1;
45
+ reason?: string;
46
+ }): Promise<void>;
27
47
  }
48
+ /**
49
+ * Permissions to change on one overwrite: `true` allows, `false` denies,
50
+ * `null` goes back to inheriting. Permissions not named keep their state.
51
+ */
52
+ export type PermissionOverwriteChanges = Partial<Record<import("@lunibee/core").PermissionName, boolean | null>>;
28
53
  export declare const CDN_BASE = "https://cdn.discordapp.com";
29
54
  /** Options for CDN image URLs. */
30
55
  export interface ImageURLOptions {
@@ -67,6 +92,8 @@ export declare class Channel extends BaseStructure {
67
92
  nsfw?: boolean;
68
93
  rateLimitPerUser?: number;
69
94
  position?: number;
95
+ /** Role and member permission overwrites (empty for DMs and when not provided). */
96
+ permissionOverwrites: import("@lunibee/types").APIOverwrite[];
70
97
  /** Creates a channel structure from Discord channel data. */
71
98
  constructor(data: import("@lunibee/types").APIChannel, context?: ResourceContext);
72
99
  /** Sends a message to this channel. @param options Message payload. @returns The created message. @throws {Error} If the channel is not attached to a client. */
@@ -84,6 +111,17 @@ export declare class Channel extends BaseStructure {
84
111
  /** Changes only this channel's topic. @param topic New topic, or null to clear it. @returns The updated channel. @throws {Error} If the channel is not attached to a client. */
85
112
  editTopic(topic: string | null): Promise<Channel>;
86
113
  /** Moves this channel to another parent category. @param parentId Parent category ID, or null to remove the parent. @returns The updated channel. @throws {Error} If the channel is not attached to a client. */
114
+ /**
115
+ * Changes some permissions of one overwrite and keeps the rest: `true`
116
+ * allows, `false` denies, `null` resets to inherited.
117
+ * @param targetId Role or member ID. @param changes Permissions to change.
118
+ * @param options `type` (0 role, 1 member) when the overwrite does not exist yet; audit-log `reason`.
119
+ * @example channel.editPermissionOverwrite(userId, { ViewChannel: true, SendMessages: false }, { type: 1 })
120
+ */
121
+ editPermissionOverwrite(targetId: string, changes: PermissionOverwriteChanges, options?: {
122
+ type?: 0 | 1;
123
+ reason?: string;
124
+ }): Promise<void>;
87
125
  editParent(parentId: string | null): Promise<Channel>;
88
126
  /** Deletes this channel. @returns A promise fulfilled when Discord confirms deletion. @throws {Error} If the channel is not attached to a client. */
89
127
  delete(): Promise<void>;
@@ -1,5 +1,13 @@
1
1
  /** Resource structures for Discord messages and related entities. */
2
2
  import { BaseStructure, Channel, User } from "./base.js";
3
+ import { GuildMember } from "./resources.js";
4
+ import type { ComponentInteraction } from "./interactions.js";
5
+ import type { Collector, CollectorOptions } from "@lunibee/core";
6
+ /** Options for {@link Message.createComponentCollector}. */
7
+ export interface ComponentCollectorOptions extends CollectorOptions<ComponentInteraction> {
8
+ /** Only this component type (2 button, 3 string select...). */
9
+ componentType?: number;
10
+ }
3
11
  import type { ResourceContext } from "./base.js";
4
12
  export { BaseStructure, Channel, Guild, User } from "./base.js";
5
13
  export type { ResourceContext } from "./base.js";
@@ -23,6 +31,12 @@ export declare class Message extends BaseStructure {
23
31
  readonly reference?: import("@lunibee/types").APIMessageReference;
24
32
  readonly components: import("@lunibee/types").APIMessageComponent[];
25
33
  readonly referencedMessage?: Message | null;
34
+ /**
35
+ * The author as a guild member, on guild messages from the Gateway. Its
36
+ * `permissions` are computed from the cached roles; `member.kick()` and the
37
+ * other member actions work when the message came from a client.
38
+ */
39
+ member: GuildMember | null;
26
40
  /** Creates a message structure from Discord message data. */
27
41
  constructor(data: import("@lunibee/types").APIMessage, context?: ResourceContext);
28
42
  /** Whether embeds are suppressed on this message. */
@@ -49,6 +63,21 @@ export declare class Message extends BaseStructure {
49
63
  })): Promise<Message>;
50
64
  /** Crossposts this message. @returns The resulting message. @throws {Error} If the message is not attached to a client. */
51
65
  crosspost(): Promise<Message>;
66
+ /**
67
+ * Collects clicks and picks on this message's components. It always ends:
68
+ * after `time`, or 15 minutes (an interaction token's life) when neither
69
+ * `time` nor `idle` is set.
70
+ * @example
71
+ * const collector = message.createComponentCollector({ filter: (i) => i.user?.id === authorId, time: 60_000 });
72
+ * collector.on("collect", (i) => i.update({ content: `Picked ${i.customId}` }));
73
+ */
74
+ createComponentCollector(options?: ComponentCollectorOptions): Collector<string, ComponentInteraction>;
75
+ /**
76
+ * Waits for the next click or pick on this message's components.
77
+ * @param options `time` (default 15 minutes), `filter`, `componentType`.
78
+ * @returns The interaction. @throws {Error} When time runs out first.
79
+ */
80
+ awaitComponent(options?: Omit<ComponentCollectorOptions, "max">): Promise<ComponentInteraction>;
52
81
  /** Adds a reaction to this message. @param emoji Emoji identifier. @returns A promise fulfilled when the reaction is added. @throws {Error} If reactions are unavailable. */
53
82
  react(emoji: string): Promise<void>;
54
83
  /** Removes a reaction from this message. @param emoji Emoji identifier. @param userId User whose reaction should be removed; omit to remove the current user's reaction. @returns A promise fulfilled when the reaction is removed. @throws {Error} If reaction operations are unavailable. */
@@ -71,6 +71,7 @@ class Channel extends BaseStructure {
71
71
  nsfw;
72
72
  rateLimitPerUser;
73
73
  position;
74
+ permissionOverwrites;
74
75
  #context;
75
76
  constructor(data, context) {
76
77
  super(data.id);
@@ -84,6 +85,7 @@ class Channel extends BaseStructure {
84
85
  this.nsfw = data.nsfw;
85
86
  this.rateLimitPerUser = data.rate_limit_per_user;
86
87
  this.position = data.position;
88
+ this.permissionOverwrites = (data.permission_overwrites ?? []).map((overwrite) => ({ ...overwrite }));
87
89
  this.#context = context;
88
90
  }
89
91
  sendMessage(options) {
@@ -107,6 +109,11 @@ class Channel extends BaseStructure {
107
109
  editTopic(topic) {
108
110
  return this.edit({ topic });
109
111
  }
112
+ editPermissionOverwrite(targetId, changes, options) {
113
+ if (!this.#context?.editPermissionOverwrite)
114
+ throw new Error("This channel is not attached to a client.");
115
+ return this.#context.editPermissionOverwrite(this.id, targetId, changes, options);
116
+ }
110
117
  editParent(parentId) {
111
118
  return this.edit({ parent_id: parentId });
112
119
  }
@@ -241,6 +248,7 @@ class Guild extends BaseStructure {
241
248
  return this.name;
242
249
  }
243
250
  }
251
+
244
252
  // packages/core/src/permissions.ts
245
253
  var Permission = {
246
254
  createInstantInvite: 1n << 0n,
@@ -290,8 +298,11 @@ var Permission = {
290
298
  createEvents: 1n << 44n,
291
299
  useExternalSounds: 1n << 45n,
292
300
  sendVoiceMessages: 1n << 46n,
301
+ setVoiceChannelStatus: 1n << 48n,
293
302
  sendPolls: 1n << 49n,
294
- useExternalApps: 1n << 50n
303
+ useExternalApps: 1n << 50n,
304
+ pinMessages: 1n << 51n,
305
+ bypassSlowmode: 1n << 52n
295
306
  };
296
307
  var Permissions = {
297
308
  CreateInstantInvite: 1n << 0n,
@@ -341,8 +352,11 @@ var Permissions = {
341
352
  CreateEvents: 1n << 44n,
342
353
  UseExternalSounds: 1n << 45n,
343
354
  SendVoiceMessages: 1n << 46n,
355
+ SetVoiceChannelStatus: 1n << 48n,
344
356
  SendPolls: 1n << 49n,
345
- UseExternalApps: 1n << 50n
357
+ UseExternalApps: 1n << 50n,
358
+ PinMessages: 1n << 51n,
359
+ BypassSlowmode: 1n << 52n
346
360
  };
347
361
  var PermissionFlagsBits = {
348
362
  CreateInstantInvite: 1n << 0n,
@@ -392,8 +406,11 @@ var PermissionFlagsBits = {
392
406
  CreateEvents: 1n << 44n,
393
407
  UseExternalSounds: 1n << 45n,
394
408
  SendVoiceMessages: 1n << 46n,
409
+ SetVoiceChannelStatus: 1n << 48n,
395
410
  SendPolls: 1n << 49n,
396
- UseExternalApps: 1n << 50n
411
+ UseExternalApps: 1n << 50n,
412
+ PinMessages: 1n << 51n,
413
+ BypassSlowmode: 1n << 52n
397
414
  };
398
415
 
399
416
  class PermissionSet {
@@ -595,6 +612,10 @@ class PermissionSet {
595
612
  }
596
613
  return false;
597
614
  }
615
+ missing(...permissions) {
616
+ const wanted = new PermissionSet(permissions);
617
+ return wanted.toArray().filter((name) => !this.has(name));
618
+ }
598
619
  add(...permissions) {
599
620
  let bitfield = this.bitfield;
600
621
  for (let i = 0;i < permissions.length; i++) {
@@ -620,6 +641,11 @@ class PermissionSet {
620
641
  toArray() {
621
642
  return Object.keys(Permission).filter((permission) => this.has(permission));
622
643
  }
644
+ static #renamed = {
645
+ ManageEmojisAndStickers: "ManageGuildExpressions",
646
+ ManageEmojis: "ManageGuildExpressions",
647
+ UseSlashCommands: "UseApplicationCommands"
648
+ };
623
649
  #resolve(permission) {
624
650
  if (typeof permission === "bigint")
625
651
  return permission;
@@ -634,6 +660,9 @@ class PermissionSet {
634
660
  return bit;
635
661
  }
636
662
  if (typeof permission === "string") {
663
+ const renamed = PermissionSet.#renamed[permission];
664
+ if (renamed)
665
+ return this.#resolve(renamed);
637
666
  if (permission in Permission)
638
667
  return Permission[permission];
639
668
  if (permission in Permissions)
@@ -656,6 +685,7 @@ class PermissionsBitField extends PermissionSet {
656
685
  super(value);
657
686
  }
658
687
  }
688
+ var ALL_PERMISSIONS = Object.values(Permission).reduce((bits, bit) => bits | bit, 0n);
659
689
  // packages/types/src/gateway.ts
660
690
  var GatewayIntentBits = {
661
691
  Guilds: 1 << 0,
@@ -721,7 +751,7 @@ var InteractionResponseType = {
721
751
  // packages/rest/package.json
722
752
  var package_default = {
723
753
  name: "@lunibee/rest",
724
- version: "0.1.8",
754
+ version: "0.2.1",
725
755
  type: "module",
726
756
  module: "./src/index.ts",
727
757
  types: "./src/index.ts",
@@ -743,6 +773,9 @@ var REQUEST_DATA_KEYS = new Set([
743
773
  "auth",
744
774
  "signal"
745
775
  ]);
776
+ // packages/managers/src/index.ts
777
+ var BULK_DELETE_MAX_AGE_MS = 14 * 24 * 60 * 60000;
778
+
746
779
  // packages/ws/src/close-codes.ts
747
780
  var GatewayCloseCodes = {
748
781
  UnknownError: 4000,
@@ -789,13 +822,15 @@ class GuildMember {
789
822
  roleIds;
790
823
  pending;
791
824
  joinedAt;
792
- permissions;
793
825
  avatarHash;
794
826
  premiumSince;
795
827
  timedOutUntil;
796
828
  flags;
797
- constructor(data) {
829
+ #suppliedPermissions;
830
+ #context;
831
+ constructor(data, context) {
798
832
  this.user = new User(data.user);
833
+ this.#context = context;
799
834
  if (!/^\d{1,20}$/.test(data.guild_id))
800
835
  throw new TypeError("Member guild_id must be a valid snowflake.");
801
836
  this.guildId = data.guild_id;
@@ -805,12 +840,53 @@ class GuildMember {
805
840
  this.joinedAt = data.joined_at ? new Date(data.joined_at) : undefined;
806
841
  if (this.joinedAt?.toString() === "Invalid Date")
807
842
  throw new RangeError("Member joined_at must be a valid date.");
808
- this.permissions = new PermissionsBitField(data.permissions ?? 0n);
843
+ if (data.permissions !== undefined)
844
+ this.#suppliedPermissions = new PermissionsBitField(data.permissions);
809
845
  this.avatarHash = data.avatar ?? null;
810
846
  this.premiumSince = toDateOrNull(data.premium_since);
811
847
  this.timedOutUntil = toDateOrNull(data.communication_disabled_until);
812
848
  this.flags = data.flags ?? 0;
813
849
  }
850
+ get permissions() {
851
+ if (this.#suppliedPermissions)
852
+ return this.#suppliedPermissions;
853
+ return new PermissionsBitField(this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds) ?? 0n);
854
+ }
855
+ permissionsIn(channelId) {
856
+ const computed = this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds, channelId);
857
+ return computed ? new PermissionsBitField(computed) : null;
858
+ }
859
+ #action(name) {
860
+ const action = this.#context?.[name];
861
+ if (!action)
862
+ throw new Error("This member is not attached to a client.");
863
+ return action;
864
+ }
865
+ kick(reason) {
866
+ return this.#action("kickMember")(this.guildId, this.user.id, reason);
867
+ }
868
+ ban(options = {}) {
869
+ return this.#action("banMember")(this.guildId, this.user.id, options);
870
+ }
871
+ edit(options, reason) {
872
+ return this.#action("editMember")(this.guildId, this.user.id, options, reason);
873
+ }
874
+ timeout(milliseconds, reason) {
875
+ if (milliseconds !== null && !(milliseconds > 0))
876
+ throw new RangeError("Timeout length must be a positive number of milliseconds.");
877
+ return this.edit({
878
+ communication_disabled_until: milliseconds === null ? null : new Date(Date.now() + milliseconds).toISOString()
879
+ }, reason);
880
+ }
881
+ setNickname(nickname, reason) {
882
+ return this.edit({ nick: nickname }, reason);
883
+ }
884
+ addRole(roleId, reason) {
885
+ return this.#action("addMemberRole")(this.guildId, this.user.id, roleId, reason);
886
+ }
887
+ removeRole(roleId, reason) {
888
+ return this.#action("removeMemberRole")(this.guildId, this.user.id, roleId, reason);
889
+ }
814
890
  get displayName() {
815
891
  return this.nickname ?? this.user.displayName;
816
892
  }
@@ -1344,6 +1420,17 @@ class CommandOptions {
1344
1420
  throw new TypeError(`Resolved mentionable for option "${name}" is missing.`);
1345
1421
  return resolved;
1346
1422
  }
1423
+ getMentionableType(name) {
1424
+ const opt = this.#get(name);
1425
+ if (!opt || opt.type !== OptionType.Mentionable)
1426
+ return null;
1427
+ const id = String(opt.value);
1428
+ if (this.#resolved?.users?.[id])
1429
+ return "user";
1430
+ if (this.#resolved?.roles?.[id])
1431
+ return "role";
1432
+ return null;
1433
+ }
1347
1434
  getAttachment(name, required = false) {
1348
1435
  const opt = this.#get(name);
1349
1436
  if (!opt || opt.type !== OptionType.Attachment) {
@@ -1369,6 +1456,17 @@ var InteractionType = {
1369
1456
  ApplicationCommandAutocomplete: 4,
1370
1457
  ModalSubmit: 5
1371
1458
  };
1459
+ var EPHEMERAL = 64;
1460
+ var TOKEN_LIFETIME_MS = 15 * 60000;
1461
+ var DISCORD_EPOCH = 1420070400000n;
1462
+ function toMessageData(options) {
1463
+ if (typeof options === "string")
1464
+ return { content: options };
1465
+ const { ephemeral, ...data } = options;
1466
+ if (ephemeral)
1467
+ data.flags = (typeof data.flags === "number" ? data.flags : 0) | EPHEMERAL;
1468
+ return data;
1469
+ }
1372
1470
 
1373
1471
  class InteractionResponse {
1374
1472
  type;
@@ -1383,14 +1481,10 @@ class InteractionResponse {
1383
1481
  return this.data === undefined ? { type: this.type } : { type: this.type, data: this.data };
1384
1482
  }
1385
1483
  static message(options) {
1386
- const data = { ...options };
1387
- if (options.ephemeral) {
1388
- data.flags = (typeof options.flags === "number" ? options.flags : 0) | 64;
1389
- }
1390
- return new InteractionResponse(InteractionResponseType.ChannelMessage, data);
1484
+ return new InteractionResponse(InteractionResponseType.ChannelMessage, toMessageData(options));
1391
1485
  }
1392
1486
  static defer(ephemeral = false) {
1393
- return new InteractionResponse(InteractionResponseType.DeferredChannelMessage, ephemeral ? { flags: 64 } : undefined);
1487
+ return new InteractionResponse(InteractionResponseType.DeferredChannelMessage, ephemeral ? { flags: EPHEMERAL } : undefined);
1394
1488
  }
1395
1489
  static pong() {
1396
1490
  return new InteractionResponse(InteractionResponseType.Pong);
@@ -1408,6 +1502,7 @@ class Interaction {
1408
1502
  replied = false;
1409
1503
  deferred = false;
1410
1504
  #client;
1505
+ #acknowledging = false;
1411
1506
  constructor(client, data) {
1412
1507
  if (!data.id || !data.token)
1413
1508
  throw new TypeError("Interaction ID and token are required.");
@@ -1423,6 +1518,20 @@ class Interaction {
1423
1518
  isChatInputCommand() {
1424
1519
  return this.type === InteractionType.ApplicationCommand;
1425
1520
  }
1521
+ awaitModalSubmit(options) {
1522
+ const collect = this.#client.resourceContext?.collectInteractions;
1523
+ if (!collect)
1524
+ return Promise.reject(new Error("This interaction is not attached to a client."));
1525
+ if (!(options.time > 0))
1526
+ return Promise.reject(new RangeError("awaitModalSubmit needs a positive time."));
1527
+ const userId = this.user?.id;
1528
+ const collector = collect({
1529
+ time: options.time,
1530
+ max: 1,
1531
+ filter: async (interaction) => interaction.isModalSubmit() && interaction.user?.id === userId && (options.customId === undefined || interaction.customId === options.customId) && (options.filter ? await options.filter(interaction) : true)
1532
+ });
1533
+ return collector.next();
1534
+ }
1426
1535
  isMessageComponent() {
1427
1536
  return this.type === InteractionType.MessageComponent;
1428
1537
  }
@@ -1457,9 +1566,57 @@ class Interaction {
1457
1566
  return [3, 5, 6, 7, 8].includes(this.#componentType());
1458
1567
  }
1459
1568
  assertUnacknowledged() {
1460
- if (this.replied || this.deferred)
1569
+ if (this.replied || this.deferred || this.#acknowledging)
1461
1570
  throw new Error("Interaction has already been acknowledged.");
1462
1571
  }
1572
+ async acknowledge(response, state) {
1573
+ this.assertUnacknowledged();
1574
+ this.#acknowledging = true;
1575
+ try {
1576
+ const result = await this.postResponse(response);
1577
+ this[state] = true;
1578
+ return result;
1579
+ } finally {
1580
+ this.#acknowledging = false;
1581
+ }
1582
+ }
1583
+ get user() {
1584
+ const raw = this.data.member?.user ?? this.data.user;
1585
+ return raw ? new User(raw) : null;
1586
+ }
1587
+ get member() {
1588
+ const raw = this.data.member;
1589
+ if (!raw || !this.guildId)
1590
+ return null;
1591
+ return new GuildMember({
1592
+ ...raw,
1593
+ guild_id: this.guildId
1594
+ }, this.#client.resourceContext);
1595
+ }
1596
+ get createdTimestamp() {
1597
+ return Number((BigInt(this.id) >> 22n) + DISCORD_EPOCH);
1598
+ }
1599
+ get expiresAt() {
1600
+ return new Date(this.createdTimestamp + TOKEN_LIFETIME_MS);
1601
+ }
1602
+ get isExpired() {
1603
+ return Date.now() >= this.createdTimestamp + TOKEN_LIFETIME_MS;
1604
+ }
1605
+ #webhookMessage(method, messageId, data) {
1606
+ if (!this.#client.interactionWebhookMessage)
1607
+ return Promise.reject(new Error("This interaction client does not support webhook message access."));
1608
+ return this.#client.interactionWebhookMessage(method, this.applicationId, this.token, messageId, data);
1609
+ }
1610
+ fetchReply() {
1611
+ this.assertAcknowledged();
1612
+ return this.#webhookMessage("GET", "@original");
1613
+ }
1614
+ editFollowUp(messageId, options) {
1615
+ return this.#webhookMessage("PATCH", messageId, toMessageData(options));
1616
+ }
1617
+ async deleteFollowUp(messageId) {
1618
+ await this.#webhookMessage("DELETE", messageId);
1619
+ }
1463
1620
  assertAcknowledged() {
1464
1621
  if (!this.replied && !this.deferred)
1465
1622
  throw new Error("Interaction has not been acknowledged.");
@@ -1468,41 +1625,29 @@ class Interaction {
1468
1625
  return this.#client.postInteractionResponse(this.id, this.token, response);
1469
1626
  }
1470
1627
  async reply(options) {
1471
- this.assertUnacknowledged();
1472
- const payload = typeof options === "string" ? { content: options } : options;
1473
- const result = await this.#client.postInteractionResponse(this.id, this.token, InteractionResponse.message(payload));
1474
- this.replied = true;
1475
- return result;
1628
+ return this.acknowledge(InteractionResponse.message(toMessageData(options)), "replied");
1476
1629
  }
1477
- async deferReply(ephemeral = false) {
1478
- this.assertUnacknowledged();
1479
- await this.#client.postInteractionResponse(this.id, this.token, InteractionResponse.defer(ephemeral));
1480
- this.deferred = true;
1630
+ async deferReply(options = false) {
1631
+ const ephemeral = typeof options === "boolean" ? options : options.ephemeral === true;
1632
+ await this.acknowledge(InteractionResponse.defer(ephemeral), "deferred");
1481
1633
  }
1482
1634
  editReply(options) {
1483
1635
  this.assertAcknowledged();
1484
- return this.#client.editInteractionReply(this.token, options);
1636
+ return this.#client.editInteractionReply(this.token, toMessageData(options));
1485
1637
  }
1486
1638
  deleteReply() {
1487
1639
  this.assertAcknowledged();
1488
1640
  return this.#client.deleteInteractionReply(this.token);
1489
1641
  }
1490
1642
  followUp(options) {
1491
- const payload = typeof options === "string" ? { content: options } : options;
1492
- return this.#client.followUpInteraction(this.token, payload);
1643
+ return this.#client.followUpInteraction(this.token, toMessageData(options));
1493
1644
  }
1494
1645
  async update(options) {
1495
- this.assertUnacknowledged();
1496
- const payload = typeof options === "string" ? { content: options } : options;
1497
- const result = await this.#client.postInteractionResponse(this.id, this.token, new InteractionResponse(InteractionResponseType.MessageUpdate, payload));
1498
- this.replied = true;
1499
- return result;
1646
+ return this.acknowledge(new InteractionResponse(InteractionResponseType.MessageUpdate, toMessageData(options)), "replied");
1500
1647
  }
1501
1648
  async showModal(modal) {
1502
- this.assertUnacknowledged();
1503
- const result = await this.#client.postInteractionResponse(this.id, this.token, new InteractionResponse(InteractionResponseType.Modal, modal));
1504
- this.replied = true;
1505
- return result;
1649
+ const data = "toJSON" in modal && typeof modal.toJSON === "function" ? modal.toJSON() : modal;
1650
+ return this.acknowledge(new InteractionResponse(InteractionResponseType.Modal, data), "replied");
1506
1651
  }
1507
1652
  }
1508
1653
 
@@ -1523,6 +1668,9 @@ class ComponentInteraction extends Interaction {
1523
1668
  get customId() {
1524
1669
  return this.data?.data?.custom_id ?? "";
1525
1670
  }
1671
+ get messageId() {
1672
+ return this.data?.message?.id;
1673
+ }
1526
1674
  get componentType() {
1527
1675
  return this.data?.data?.component_type ?? 0;
1528
1676
  }
@@ -1530,16 +1678,10 @@ class ComponentInteraction extends Interaction {
1530
1678
  return this.data?.data?.values ?? [];
1531
1679
  }
1532
1680
  async deferUpdate() {
1533
- this.assertUnacknowledged();
1534
- await this.postResponse(new InteractionResponse(InteractionResponseType.DeferredMessageUpdate));
1535
- this.deferred = true;
1681
+ await this.acknowledge(new InteractionResponse(InteractionResponseType.DeferredMessageUpdate), "deferred");
1536
1682
  }
1537
1683
  async update(options) {
1538
- this.assertUnacknowledged();
1539
- const payload = typeof options === "string" ? { content: options } : options;
1540
- const result = await this.postResponse(new InteractionResponse(InteractionResponseType.MessageUpdate, payload));
1541
- this.replied = true;
1542
- return result;
1684
+ return this.acknowledge(new InteractionResponse(InteractionResponseType.MessageUpdate, toMessageData(options)), "replied");
1543
1685
  }
1544
1686
  }
1545
1687
 
@@ -1582,8 +1724,7 @@ class AutocompleteInteraction extends Interaction {
1582
1724
  if (!Array.isArray(choices))
1583
1725
  throw new TypeError("Autocomplete choices must be an array.");
1584
1726
  const response = new InteractionResponse(InteractionResponseType.Autocomplete, { choices });
1585
- await this.postResponse(response);
1586
- this.replied = true;
1727
+ await this.acknowledge(response, "replied");
1587
1728
  }
1588
1729
  }
1589
1730
  function findFocused(options) {
@@ -1780,6 +1921,7 @@ class AuditLog {
1780
1921
  }
1781
1922
 
1782
1923
  // packages/structures/src/index.ts
1924
+ var COMPONENT_COLLECTOR_DEFAULT_TIME = 15 * 60000;
1783
1925
  var NON_SYSTEM_MESSAGE_TYPES = new Set([0, 19, 20, 23]);
1784
1926
 
1785
1927
  class Message extends BaseStructure {
@@ -1800,6 +1942,7 @@ class Message extends BaseStructure {
1800
1942
  reference;
1801
1943
  components;
1802
1944
  referencedMessage;
1945
+ member;
1803
1946
  #context;
1804
1947
  constructor(data, context) {
1805
1948
  super(data.id);
@@ -1822,6 +1965,11 @@ class Message extends BaseStructure {
1822
1965
  this.reference = data.message_reference;
1823
1966
  this.referencedMessage = data.referenced_message ? new Message(data.referenced_message, context) : data.referenced_message === null ? null : undefined;
1824
1967
  this.#context = context;
1968
+ this.member = data.member && data.guild_id ? new GuildMember({
1969
+ ...data.member,
1970
+ user: data.member.user ?? data.author,
1971
+ guild_id: data.guild_id
1972
+ }, context) : null;
1825
1973
  this.channel = new Channel({ id: data.channel_id, type: 0, guild_id: data.guild_id }, context);
1826
1974
  }
1827
1975
  get embedsSuppressed() {
@@ -1862,6 +2010,20 @@ class Message extends BaseStructure {
1862
2010
  throw new Error("This message is not attached to a client.");
1863
2011
  return this.#context.crosspostMessage(this.channelId, this.id);
1864
2012
  }
2013
+ createComponentCollector(options = {}) {
2014
+ const collect = this.#context?.collectInteractions;
2015
+ if (!collect)
2016
+ throw new Error("This message is not attached to a client.");
2017
+ const { componentType, filter, ...rest } = options;
2018
+ return collect({
2019
+ ...rest,
2020
+ time: rest.time ?? (rest.idle ? undefined : COMPONENT_COLLECTOR_DEFAULT_TIME),
2021
+ filter: async (interaction) => interaction.isMessageComponent() && interaction.messageId === this.id && (componentType === undefined || interaction.componentType === componentType) && (filter ? await filter(interaction) : true)
2022
+ });
2023
+ }
2024
+ awaitComponent(options = {}) {
2025
+ return this.createComponentCollector({ ...options, max: 1 }).next();
2026
+ }
1865
2027
  react(emoji) {
1866
2028
  if (!this.#context?.addReaction)
1867
2029
  throw new Error("This message is not attached to a client.");
@@ -1977,4 +2139,4 @@ export {
1977
2139
  createInteraction
1978
2140
  };
1979
2141
 
1980
- //# debugId=8FB0F7F12180BD8364756E2164756E21
2142
+ //# debugId=56391DD25556C30C64756E2164756E21