From 352b2c3a0932df0167a7595ad04633377c11c422 Mon Sep 17 00:00:00 2001 From: TTtie Date: Thu, 15 Feb 2024 07:09:50 +0000 Subject: [PATCH 1/4] feat: support `nonce`/`enforceNonce` when creating messages --- index.d.ts | 21 ++++++++++++--------- lib/Client.js | 3 +++ lib/structures/Message.js | 7 +++++++ lib/structures/PrivateChannel.js | 2 ++ lib/structures/TextChannel.js | 2 ++ lib/structures/TextVoiceChannel.js | 2 ++ lib/structures/ThreadChannel.js | 2 ++ 7 files changed, 30 insertions(+), 9 deletions(-) diff --git a/index.d.ts b/index.d.ts index a8f3ebe9..8eb3e45b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -151,7 +151,7 @@ declare namespace Dysnomia { type ComponentTypes = Constants["ComponentTypes"][keyof Constants["ComponentTypes"]]; type ImageFormat = Constants["ImageFormats"][number]; type MessageActivityTypes = Constants["MessageActivityTypes"][keyof Constants["MessageActivityTypes"]]; - type MessageContent = string | AdvancedMessageContent; + type MessageContent = string | AdvancedMessageContent; type MFALevel = Constants["MFALevels"][keyof Constants["MFALevels"]]; type PossiblyUncachedMessage = Message | { author?: User | Uncached; channel: TextableChannel | { id: string; guild?: Uncached }; guildID?: string; id: string }; type SelectMenu = BaseSelectMenu | ChannelSelectMenu | StringSelectMenu | UserSelectMenu | RoleSelectMenu | MentionableSelectMenu; @@ -438,7 +438,7 @@ declare namespace Dysnomia { lastMessageID: string; messages: Collection>; addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent): Promise>; + createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; deleteMessage(messageID: string, reason?: string): Promise; editMessage(messageID: string, content: MessageContent): Promise>; getMessage(messageID: string): Promise>; @@ -1344,14 +1344,16 @@ declare namespace Dysnomia { components: TextInput[]; type: Constants["ComponentTypes"]["ACTION_ROW"]; } - interface AdvancedMessageContent { + interface AdvancedMessageContent { allowedMentions?: AllowedMentions; attachments?: AdvancedMessageContentAttachment[]; components?: ActionRow[]; content?: string; + enforceNonce?: T extends "isNonceEnforceable" ? boolean : never; embeds?: EmbedOptions[]; flags?: number; messageReference?: MessageReferenceReply; + nonce?: string | number; stickerIDs?: string[]; tts?: boolean; } @@ -2643,7 +2645,7 @@ declare namespace Dysnomia { createGuildSticker(guildID: string, options: CreateStickerOptions, reason?: string): Promise; createGuildTemplate(guildID: string, name: string, description?: string | null): Promise; createInteractionResponse(interactionID: string, interactionToken: string, options: InteractionResponse, file?: FileContent | FileContent[]): Promise; - createMessage(channelID: string, content: MessageContent): Promise; + createMessage(channelID: string, content: MessageContent<"isNonceEnforceable">): Promise; createRole(guildID: string, options?: RoleOptions, reason?: string): Promise; createRole(guildID: string, options?: Role, reason?: string): Promise; createStageInstance(channelID: string, options: CreateStageInstanceOptions): Promise; @@ -3349,6 +3351,7 @@ declare namespace Dysnomia { mentionEveryone: boolean; mentions: User[]; messageReference: MessageReference | null; + nonce?: string | number; pinned: boolean; position?: number; reactions: { [s: string]: { count: number; me: boolean } }; @@ -3402,7 +3405,7 @@ declare namespace Dysnomia { rateLimitPerUser: 0; type: Constants["ChannelTypes"]["GUILD_ANNOUNCEMENT"]; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; - createMessage(content: MessageContent): Promise>; + createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; crosspostMessage(messageID: string): Promise>; editMessage(messageID: string, content: MessageContent): Promise>; @@ -3461,7 +3464,7 @@ declare namespace Dysnomia { recipient: User; type: PrivateChannelTypes; addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent): Promise>; + createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; deleteMessage(messageID: string, reason?: string): Promise; editMessage(messageID: string, content: MessageContent): Promise>; getMessage(messageID: string): Promise>; @@ -3674,7 +3677,7 @@ declare namespace Dysnomia { constructor(data: BaseData, client: Client, messageLimit: number); addMessageReaction(messageID: string, reaction: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; - createMessage(content: MessageContent): Promise>; + createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; createThread(options: CreateThreadWithoutMessageOptions): Promise; createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; createWebhook(options: Omit, reason?: string): Promise; @@ -3706,7 +3709,7 @@ declare namespace Dysnomia { messages: Collection>; rateLimitPerUser: number; addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent): Promise>; + createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; createWebhook(options: { name: string; avatar?: string | null }, reason?: string): Promise; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; @@ -3738,7 +3741,7 @@ declare namespace Dysnomia { type: GuildThreadChannelTypes; constructor(data: BaseData, client: Client, messageLimit?: number); addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent): Promise>; + createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; edit(options: Pick, reason?: string): Promise; diff --git a/lib/Client.js b/lib/Client.js index db9f0462..8027a10c 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -777,6 +777,7 @@ class Client extends EventEmitter { * @arg {String} [content.attachments[].description] A description for the attachment * @arg {Array} [content.components] An array of components. See [Discord's Documentation](https://discord.com/developers/docs/interactions/message-components#what-is-a-component) for object structure * @arg {String} [content.content] A content string + * @arg {Boolean} [content.enforceNonce] If set and nonce is present, check the message for uniqueness in the past few minutes * @arg {Array} [content.embeds] An array of embed objects. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#embed-object) for object structure * @arg {Number} [content.flags] Message flags. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for a list * @arg {Object} [content.messageReference] The message reference, used when replying to messages @@ -784,6 +785,7 @@ class Client extends EventEmitter { * @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message + * @arg {String | Number} [content.nonce] A value that can be used to check if the message was sent * @arg {Array} [content.stickerIDs] An array of IDs corresponding to stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @returns {Promise} @@ -818,6 +820,7 @@ class Client extends EventEmitter { content.messageReference.failIfNotExists = undefined; } } + content.enforce_nonce = content.enforceNonce; } const {files, attachments} = this._processAttachments(content.attachments); diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 2d617cea..233d333e 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -191,6 +191,13 @@ class Message extends Base { }; } + /** + * A unique user-provided value used to check whether a message was sent. + * Only provided when the message is created over REST + * @type {(String | Number)?} + */ + this.nonce = data.nonce; + switch(this.type) { case MessageTypes.DEFAULT: { break; diff --git a/lib/structures/PrivateChannel.js b/lib/structures/PrivateChannel.js index 6e68217f..220423d4 100644 --- a/lib/structures/PrivateChannel.js +++ b/lib/structures/PrivateChannel.js @@ -60,12 +60,14 @@ class PrivateChannel extends Channel { * @arg {String} [content.attachments[].description] A description for the attachment * @arg {Array} [content.components] An array of components. See [Discord's Documentation](https://discord.com/developers/docs/interactions/message-components#what-is-a-component) for object structure * @arg {String} [content.content] A content string + * @arg {Boolean} [content.enforceNonce] If set and nonce is present, check the message for uniqueness in the past few minutes * @arg {Array} [content.embeds] An array of embed objects. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#embed-object) for object structure * @arg {Object} [content.messageReference] The message reference, used when replying to messages * @arg {String} [content.messageReference.channelID] The channel ID of the referenced message * @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message + * @arg {String | Number} [content.nonce] A value that can be used to check if the message was sent * @arg {Array} [content.stickerIDs] An array of IDs corresponding to stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @returns {Promise} diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index 5e7a4b51..a61727a7 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -119,6 +119,7 @@ class TextChannel extends GuildChannel { * @arg {String} [content.attachments[].description] A description for the attachment * @arg {Array} [content.components] An array of components. See [Discord's Documentation](https://discord.com/developers/docs/interactions/message-components#what-is-a-component) for object structure * @arg {String} [content.content] A content string + * @arg {Boolean} [content.enforceNonce] If set and nonce is present, check the message for uniqueness in the past few minutes * @arg {Array} [content.embeds] An array of embed objects. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#embed-object) for object structure * @arg {Number} [content.flags] Message flags. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for a list * @arg {Object} [content.messageReference] The message reference, used when replying to messages @@ -126,6 +127,7 @@ class TextChannel extends GuildChannel { * @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message + * @arg {String | Number} [content.nonce] A value that can be used to check if the message was sent * @arg {Array} [content.stickerIDs] An array of IDs corresponding to stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @returns {Promise} diff --git a/lib/structures/TextVoiceChannel.js b/lib/structures/TextVoiceChannel.js index 1ae52cfa..bfbf1d34 100644 --- a/lib/structures/TextVoiceChannel.js +++ b/lib/structures/TextVoiceChannel.js @@ -94,12 +94,14 @@ class TextVoiceChannel extends VoiceChannel { * @arg {String} [content.attachments[].description] A description for the attachment * @arg {Array} [content.components] An array of components. See [the official Discord API documentation entry](https://discord.com/developers/docs/interactions/message-components#what-is-a-component) for object structure * @arg {String} content.content A content string + * @arg {Boolean} [content.enforceNonce] If set and nonce is present, check the message for uniqueness in the past few minutes * @arg {Array} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure * @arg {Object} [content.messageReference] The message reference, used when replying to messages * @arg {String} [content.messageReference.channelID] The channel ID of the referenced message * @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message + * @arg {String | Number} [content.nonce] A unique value that can be used to check if the message was sent * @arg {Array} [content.stickerIDs] An array of IDs corresponding to the stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @returns {Promise} diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 69f53b5e..430a89ec 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -112,12 +112,14 @@ class ThreadChannel extends GuildChannel { * @arg {String} [content.attachments[].description] A description for the attachment * @arg {Array} [content.components] An array of components. See [the official Discord API documentation entry](https://discord.com/developers/docs/interactions/message-components#what-is-a-component) for object structure * @arg {String} [content.content] A content string + * @arg {Boolean} [content.enforceNonce] If set and nonce is present, check the message for uniqueness in the past few minutes * @arg {Array} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure * @arg {Object} [content.messageReference] The message reference, used when replying to messages * @arg {String} [content.messageReference.channelID] The channel ID of the referenced message * @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message + * @arg {String | Number} [content.nonce] A value that can be used to check if the message was sent * @arg {Array} [content.stickerIDs] An array of IDs corresponding to stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @returns {Promise} From d011d61f29b9217342e5f200e75cd9f6d4a6201f Mon Sep 17 00:00:00 2001 From: TTtie Date: Thu, 15 Feb 2024 14:22:13 +0000 Subject: [PATCH 2/4] reword Message#nonce docs --- lib/structures/Message.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 233d333e..df33ebe6 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -193,7 +193,9 @@ class Message extends Base { /** * A unique user-provided value used to check whether a message was sent. - * Only provided when the message is created over REST + * Available only when the message is created over REST, or received via + * the messageCreate event. + * * @type {(String | Number)?} */ this.nonce = data.nonce; From b4600a3d984a6c4e8e0e82f9138812ccf72f96f4 Mon Sep 17 00:00:00 2001 From: TTtie Date: Thu, 15 Feb 2024 15:00:22 +0000 Subject: [PATCH 3/4] lint --- lib/structures/Message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/Message.js b/lib/structures/Message.js index df33ebe6..d58b5f05 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -193,7 +193,7 @@ class Message extends Base { /** * A unique user-provided value used to check whether a message was sent. - * Available only when the message is created over REST, or received via + * Available only when the message is created over REST, or received via * the messageCreate event. * * @type {(String | Number)?} From e989214a6bb9cbf5080a4a290cfe635546f48255 Mon Sep 17 00:00:00 2001 From: TTtie Date: Wed, 21 Feb 2024 10:40:00 +0000 Subject: [PATCH 4/4] types: nonces aren't editable --- index.d.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/index.d.ts b/index.d.ts index 8eb3e45b..5be4e007 100644 --- a/index.d.ts +++ b/index.d.ts @@ -151,7 +151,7 @@ declare namespace Dysnomia { type ComponentTypes = Constants["ComponentTypes"][keyof Constants["ComponentTypes"]]; type ImageFormat = Constants["ImageFormats"][number]; type MessageActivityTypes = Constants["MessageActivityTypes"][keyof Constants["MessageActivityTypes"]]; - type MessageContent = string | AdvancedMessageContent; + type MessageContent = string | AdvancedMessageContent; type MFALevel = Constants["MFALevels"][keyof Constants["MFALevels"]]; type PossiblyUncachedMessage = Message | { author?: User | Uncached; channel: TextableChannel | { id: string; guild?: Uncached }; guildID?: string; id: string }; type SelectMenu = BaseSelectMenu | ChannelSelectMenu | StringSelectMenu | UserSelectMenu | RoleSelectMenu | MentionableSelectMenu; @@ -438,7 +438,7 @@ declare namespace Dysnomia { lastMessageID: string; messages: Collection>; addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; + createMessage(content: MessageContent<"hasNonce">): Promise>; deleteMessage(messageID: string, reason?: string): Promise; editMessage(messageID: string, content: MessageContent): Promise>; getMessage(messageID: string): Promise>; @@ -1344,16 +1344,16 @@ declare namespace Dysnomia { components: TextInput[]; type: Constants["ComponentTypes"]["ACTION_ROW"]; } - interface AdvancedMessageContent { + interface AdvancedMessageContent { allowedMentions?: AllowedMentions; attachments?: AdvancedMessageContentAttachment[]; components?: ActionRow[]; content?: string; - enforceNonce?: T extends "isNonceEnforceable" ? boolean : never; + enforceNonce?: T extends "hasNonce" ? boolean : never; embeds?: EmbedOptions[]; flags?: number; messageReference?: MessageReferenceReply; - nonce?: string | number; + nonce?: T extends "hasNonce" ? (string | number) : never; stickerIDs?: string[]; tts?: boolean; } @@ -2645,7 +2645,7 @@ declare namespace Dysnomia { createGuildSticker(guildID: string, options: CreateStickerOptions, reason?: string): Promise; createGuildTemplate(guildID: string, name: string, description?: string | null): Promise; createInteractionResponse(interactionID: string, interactionToken: string, options: InteractionResponse, file?: FileContent | FileContent[]): Promise; - createMessage(channelID: string, content: MessageContent<"isNonceEnforceable">): Promise; + createMessage(channelID: string, content: MessageContent<"hasNonce">): Promise; createRole(guildID: string, options?: RoleOptions, reason?: string): Promise; createRole(guildID: string, options?: Role, reason?: string): Promise; createStageInstance(channelID: string, options: CreateStageInstanceOptions): Promise; @@ -3405,7 +3405,7 @@ declare namespace Dysnomia { rateLimitPerUser: 0; type: Constants["ChannelTypes"]["GUILD_ANNOUNCEMENT"]; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; - createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; + createMessage(content: MessageContent<"hasNonce">): Promise>; createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; crosspostMessage(messageID: string): Promise>; editMessage(messageID: string, content: MessageContent): Promise>; @@ -3464,7 +3464,7 @@ declare namespace Dysnomia { recipient: User; type: PrivateChannelTypes; addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; + createMessage(content: MessageContent<"hasNonce">): Promise>; deleteMessage(messageID: string, reason?: string): Promise; editMessage(messageID: string, content: MessageContent): Promise>; getMessage(messageID: string): Promise>; @@ -3677,7 +3677,7 @@ declare namespace Dysnomia { constructor(data: BaseData, client: Client, messageLimit: number); addMessageReaction(messageID: string, reaction: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; - createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; + createMessage(content: MessageContent<"hasNonce">): Promise>; createThread(options: CreateThreadWithoutMessageOptions): Promise; createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; createWebhook(options: Omit, reason?: string): Promise; @@ -3709,7 +3709,7 @@ declare namespace Dysnomia { messages: Collection>; rateLimitPerUser: number; addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; + createMessage(content: MessageContent<"hasNonce">): Promise>; createWebhook(options: { name: string; avatar?: string | null }, reason?: string): Promise; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; @@ -3741,7 +3741,7 @@ declare namespace Dysnomia { type: GuildThreadChannelTypes; constructor(data: BaseData, client: Client, messageLimit?: number); addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent<"isNonceEnforceable">): Promise>; + createMessage(content: MessageContent<"hasNonce">): Promise>; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; edit(options: Pick, reason?: string): Promise;