Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support with_response on interaction callbacks #167

Merged
merged 11 commits into from
Oct 26, 2024
65 changes: 50 additions & 15 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ declare namespace Dysnomia {
type InteractionDataOptionsUser = InteractionDataOption<"USER", string>;
type InteractionDataOptionsWithOptions = InteractionDataOptionsSubCommand | InteractionDataOptionsSubCommandGroup;
type InteractionDataOptionsWithValue = InteractionDataOptionsString | InteractionDataOptionsInteger | InteractionDataOptionsBoolean | InteractionDataOptionsUser | InteractionDataOptionsChannel | InteractionDataOptionsRole | InteractionDataOptionsMentionable | InteractionDataOptionsNumber;
type InteractionResponse = InteractionResponseAutocomplete | InteractionResponseDeferred | InteractionResponseMessage;
type InteractionResponse = InteractionResponseAutocomplete | InteractionResponseDeferred | InteractionResponseLaunchActivity | InteractionResponseMessage | InteractionResponsePong | InteractionResponsePremiumRequired;
type InteractionResponseTypes = Constants["InteractionResponseTypes"][keyof Constants["InteractionResponseTypes"]];
type InteractionTypes = Constants["InteractionTypes"][keyof Constants["InteractionTypes"]];

// Invite
Expand Down Expand Up @@ -1241,6 +1242,25 @@ declare namespace Dysnomia {
values: string[];
resolved?: InteractionResolvedData;
}
interface InteractionCallbackResponse<T extends InteractionResponseTypes = InteractionResponseTypes> {
interaction: {
id: string;
type: InteractionTypes;
activity_instance_id?: string;
response_message_id?: string;
response_message_loading?: boolean;
response_message_ephemeral?: boolean;
};
resource: T extends Constants["InteractionResponseTypes"]["CHANNEL_MESSAGE_WITH_SOURCE" | "UPDATE_MESSAGE" | "LAUNCH_ACTIVITY"]
? {
type: T;
activity_instance: T extends Constants["InteractionResponseTypes"]["LAUNCH_ACTIVITY"] ? ActivityInstance : never;
message: T extends Constants["InteractionResponseTypes"]["CHANNEL_MESSAGE_WITH_SOURCE" | "UPDATE_MESSAGE"] ? {
id: string;
[key: string]: unknown;
} : never;
} : never;
}
interface InteractionDataOptionsBase<T extends ApplicationCommandOptionsTypes, V = unknown> {
focused?: T extends ApplicationCommandOptionsTypesWithAutocomplete ? boolean : never;
name: string;
Expand All @@ -1265,28 +1285,38 @@ declare namespace Dysnomia {
roles?: Collection<Role>;
users?: Collection<User>;
}
interface InteractionResponseAutocomplete {
interface InteractionResponseAutocomplete extends InteractionResponseBase {
data: ApplicationCommandOptionsChoice[];
type: Constants["InteractionResponseTypes"]["APPLICATION_COMMAND_AUTOCOMPLETE_RESULT"];
}
interface InteractionResponseDeferred {
interface InteractionResponseBase {
withResponse?: boolean;
}
interface InteractionResponseDeferred extends InteractionResponseBase {
data?: {
flags?: number;
};
type: Constants["InteractionResponseTypes"]["DEFERRED_UPDATE_MESSAGE" | "DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE"];
}
interface InteractionResponseMessage {
interface InteractionResponseLaunchActivity extends InteractionResponseBase {
type: Constants["InteractionResponseTypes"]["LAUNCH_ACTIVITY"];
}
interface InteractionResponseMessage extends InteractionResponseBase {
data: RawInteractionContent;
type: Constants["InteractionResponseTypes"]["CHANNEL_MESSAGE_WITH_SOURCE" | "UPDATE_MESSAGE" | "PREMIUM_REQUIRED"];
}
interface InteractionResponseModal {
interface InteractionResponseModal extends InteractionResponseBase {
data: InteractionModalContent;
type: Constants["InteractionResponseTypes"]["MODAL"];

}
interface InteractionResponsePong {
interface InteractionResponsePong extends InteractionResponseBase {
type: Constants["InteractionResponseTypes"]["PONG"];
}
/** @deprecated */
interface InteractionResponsePremiumRequired extends InteractionResponseBase {
type: Constants["InteractionResponseTypes"]["PREMIUM_REQUIRED"];
}

interface RawInteractionContent extends Pick<WebhookPayload, "content" | "embeds" | "tts" | "flags" | "components"> {
allowed_mentions?: {
Expand Down Expand Up @@ -1927,6 +1957,11 @@ declare namespace Dysnomia {
scopes: string[];
permissions: string;
}

// Activities
interface ActivityInstance {
id: string;
}
/* eslint-disable @stylistic/key-spacing, @stylistic/no-multi-spaces */
interface Constants {
GATEWAY_VERSION: 10;
Expand Down Expand Up @@ -2855,7 +2890,7 @@ declare namespace Dysnomia {
createGuildScheduledEvent<T extends GuildScheduledEventEntityTypes>(guildID: string, event: GuildScheduledEventOptions<T>, reason?: string): Promise<GuildScheduledEvent<T>>;
createGuildSticker(guildID: string, options: CreateStickerOptions, reason?: string): Promise<Sticker>;
createGuildTemplate(guildID: string, name: string, description?: string | null): Promise<GuildTemplate>;
createInteractionResponse(interactionID: string, interactionToken: string, options: InteractionResponse, file?: FileContent | FileContent[]): Promise<void>;
createInteractionResponse<T extends InteractionResponse>(interactionID: string, interactionToken: string, options: T, file?: FileContent | FileContent[]): Promise<T["withResponse"] extends true ? InteractionCallbackResponse<T["type"]> : void>;
createMessage(channelID: string, content: MessageContent<"hasNonce">): Promise<Message>;
createRole(guildID: string, options?: RoleOptions, reason?: string): Promise<Role>;
createRole(guildID: string, options?: Role, reason?: string): Promise<Role>;
Expand Down Expand Up @@ -3082,15 +3117,15 @@ declare namespace Dysnomia {
user?: User;
acknowledge(flags?: number): Promise<void>;
createFollowup(content: string | InteractionContent): Promise<Message>;
createMessage(content: string | InteractionContent): Promise<void>;
createMessage(content: string | InteractionContent): Promise<Message>;
createModal(content: InteractionModalContent): Promise<void>;
defer(flags?: number): Promise<void>;
deleteMessage(messageID: string): Promise<void>;
deleteOriginalMessage(): Promise<void>;
editMessage(messageID: string, content: string | InteractionContentEdit): Promise<Message>;
editOriginalMessage(content: string | InteractionContentEdit): Promise<Message>;
getOriginalMessage(): Promise<Message>;
launchActivity(): Promise<void>;
launchActivity(): Promise<ActivityInstance>;
/** @deprecated */
requirePremium(): Promise<void>;
}
Expand All @@ -3106,17 +3141,17 @@ declare namespace Dysnomia {
user?: User;
acknowledge(): Promise<void>;
createFollowup(content: string | InteractionContent): Promise<Message>;
createMessage(content: string | InteractionContent): Promise<void>;
createMessage(content: string | InteractionContent): Promise<Message>;
createModal(content: InteractionModalContent): Promise<void>;
defer(flags?: number): Promise<void>;
deferUpdate(): Promise<void>;
deleteMessage(messageID: string): Promise<void>;
deleteOriginalMessage(): Promise<void>;
editMessage(messageID: string, content: string | InteractionContentEdit): Promise<Message>;
editOriginalMessage(content: string | InteractionContentEdit): Promise<Message>;
editParent(content: InteractionContentEdit): Promise<void>;
editParent(content: InteractionContentEdit): Promise<Message>;
getOriginalMessage(): Promise<Message>;
launchActivity(): Promise<void>;
launchActivity(): Promise<ActivityInstance>;
/** @deprecated */
requirePremium(): Promise<void>;
}
Expand Down Expand Up @@ -3649,16 +3684,16 @@ declare namespace Dysnomia {
user?: User;
acknowledge(): Promise<void>;
createFollowup(content: string | InteractionContent): Promise<Message>;
createMessage(content: string | InteractionContent): Promise<void>;
createMessage(content: string | InteractionContent): Promise<Message>;
defer(flags?: number): Promise<void>;
deferUpdate(): Promise<void>;
deleteMessage(messageID: string): Promise<void>;
deleteOriginalMessage(): Promise<void>;
editMessage(messageID: string, content: string | InteractionContentEdit): Promise<Message>;
editOriginalMessage(content: string | InteractionContentEdit): Promise<Message>;
editParent(content: InteractionContentEdit): Promise<void>;
editParent(content: InteractionContentEdit): Promise<Message>;
getOriginalMessage(): Promise<Message>;
launchActivity(): Promise<void>;
launchActivity(): Promise<ActivityInstance>;
/** @deprecated */
requirePremium(): Promise<void>;
}
Expand Down
11 changes: 9 additions & 2 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,14 +794,21 @@ class Client extends EventEmitter {
* @param {Object} options The options object.
* @param {Object} [options.data] The data to send with the response. **WARNING: This call expects raw API data and does not transform it in any way.**
* @param {Number} options.type The response type to send. See [the official Discord API documentation entry](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) for valid types
* @param {Boolean} [options.withResponse=false] Whether to respond with an interaction callback response or not
* @param {Object | Array<Object>} [file] A file object (or an Array of them)
* @param {String} [file.fieldName] The multipart field name
* @param {Buffer} file.file A buffer containing file data
* @param {String} file.name What to name the file
* @returns {Promise}
* @returns {Promise<Object?>} If `options.withResponse` is true, returns an [interaction callback response object](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object).
*/
createInteractionResponse(interactionID, interactionToken, options, file) {
return this.requestHandler.request("POST", Endpoints.INTERACTION_RESPOND(interactionID, interactionToken), true, options, file, "/interactions/:id/:token/callback");
let qs = "";
const withResponse = !!options.withResponse;
options.withResponse = undefined;
if(withResponse) {
qs += "&with_response=true";
}
return this.requestHandler.request("POST", Endpoints.INTERACTION_RESPOND(interactionID, interactionToken) + (qs ? "?" + qs : ""), true, options, file, "/interactions/:id/:token/callback");
}

/**
Expand Down
20 changes: 14 additions & 6 deletions lib/structures/CommandInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class CommandInteraction extends Interaction {
* @param {Number} [content.flags] A number representing the flags to apply. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for a list
* @param {Object} [content.poll] A poll object. See [Discord's Documentation](https://discord.com/developers/docs/resources/poll#poll-create-request-object-poll-create-request-object-structure) for object structure
* @param {Boolean} [content.tts] Set the message TTS flag
* @returns {Promise}
* @returns {Promise<Message>}
*/
createMessage(content) {
if(this.acknowledged === true) {
Expand All @@ -176,8 +176,12 @@ class CommandInteraction extends Interaction {

return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE,
data: content
}, files).then(() => this.update());
data: content,
withResponse: true
}, files).then((resp) => {
this.update();
return new Message(resp.resource.message, this.#client);
});
}

/**
Expand Down Expand Up @@ -320,12 +324,16 @@ class CommandInteraction extends Interaction {

/**
* Responds to the interaction with launching an activity
* @returns {Promise}
* @returns {Promise<Object>} Returns [the created activity instance](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-activity-instance-resource)
*/
launchActivity() {
return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.LAUNCH_ACTIVITY
}).then(() => this.update());
type: InteractionResponseTypes.LAUNCH_ACTIVITY,
withResponse: true
}).then((resp) => {
this.update();
return resp.resource.activity_instance;
});
}

/**
Expand Down
30 changes: 21 additions & 9 deletions lib/structures/ComponentInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class ComponentInteraction extends Interaction {
* @param {Number} [content.flags] A number representing the flags to apply. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for a list
* @param {Object} [content.poll] A poll object. See [Discord's Documentation](https://discord.com/developers/docs/resources/poll#poll-create-request-object-poll-create-request-object-structure) for object structure
* @param {Boolean} [content.tts] Set the message TTS flag
* @returns {Promise}
* @returns {Promise<Message>}
*/
createMessage(content) {
if(this.acknowledged === true) {
Expand All @@ -165,8 +165,12 @@ class ComponentInteraction extends Interaction {

return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE,
data: content
}, files).then(() => this.update());
data: content,
withResponse: true
}, files).then((resp) => {
this.update();
return new Message(resp.resource.message, this.#client);
});
}

/**
Expand Down Expand Up @@ -329,7 +333,7 @@ class ComponentInteraction extends Interaction {
* @param {Array<Object>} [content.embeds] An array of embed objects. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
* @param {Number} [content.flags] A number representing the flags to apply. See [Discord's Documentation](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for a list
* @param {Boolean} [content.tts] Set the message TTS flag
* @returns {Promise}
* @returns {Promise<Message>}
*/
editParent(content) {
if(this.acknowledged === true) {
Expand All @@ -353,8 +357,12 @@ class ComponentInteraction extends Interaction {

return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.UPDATE_MESSAGE,
data: content
}, files).then(() => this.update());
data: content,
withResponse: true
}, files).then((resp) => {
this.update();
return new Message(resp.resource.message, this.#client);
});
}

/**
Expand All @@ -371,12 +379,16 @@ class ComponentInteraction extends Interaction {

/**
* Responds to the interaction with launching an activity
* @returns {Promise}
* @returns {Promise<Object>} Returns [the created activity instance](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-activity-instance-resource)
*/
launchActivity() {
return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.LAUNCH_ACTIVITY
}).then(() => this.update());
type: InteractionResponseTypes.LAUNCH_ACTIVITY,
withResponse: true
}).then((resp) => {
this.update();
return resp.resource.activity_instance;
});
}

/**
Expand Down
28 changes: 20 additions & 8 deletions lib/structures/ModalSubmitInteraction.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use strict";

const Interaction = require("./Interaction");
const Message = require("./Message");

const {InteractionResponseTypes} = require("../Constants");
const emitDeprecation = require("../util/emitDeprecation");
Expand Down Expand Up @@ -86,7 +87,7 @@ class ModalSubmitInteraction extends Interaction {
* @param {Boolean} [content.flags] 64 for Ephemeral
* @param {Object} [content.poll] A poll object. See [Discord's Documentation](https://discord.com/developers/docs/resources/poll#poll-create-request-object-poll-create-request-object-structure) for object structure
* @param {Boolean} [content.tts] Set the message TTS flag
* @returns {Promise}
* @returns {Promise<Message>}
*/
async createMessage(content) {
if(this.acknowledged === true) {
Expand All @@ -110,8 +111,12 @@ class ModalSubmitInteraction extends Interaction {

return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.CHANNEL_MESSAGE_WITH_SOURCE,
data: content
}, files).then(() => this.update());
data: content,
withResponse: true
}, files).then((resp) => {
this.update();
return new Message(resp.resource.message, this.#client);
});
}

/**
Expand Down Expand Up @@ -258,7 +263,7 @@ class ModalSubmitInteraction extends Interaction {
* @param {Array<Object>} [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
* @param {Boolean} [content.flags] 64 for Ephemeral
* @param {Boolean} [content.tts] Set the message TTS flag
* @returns {Promise}
* @returns {Promise<Message>}
*/
async editParent(content) {
if(this.acknowledged === true) {
Expand All @@ -283,7 +288,10 @@ class ModalSubmitInteraction extends Interaction {
return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.UPDATE_MESSAGE,
data: content
}, files).then(() => this.update());
}, files).then((resp) => {
this.update();
return new Message(resp.resource.message, this.#client);
});
}

/**
Expand All @@ -300,12 +308,16 @@ class ModalSubmitInteraction extends Interaction {

/**
* Responds to the interaction with launching an activity
* @returns {Promise}
* @returns {Promise<Object>} Returns [the created activity instance](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-activity-instance-resource)
*/
launchActivity() {
return this.#client.createInteractionResponse.call(this.#client, this.id, this.token, {
type: InteractionResponseTypes.LAUNCH_ACTIVITY
}).then(() => this.update());
type: InteractionResponseTypes.LAUNCH_ACTIVITY,
withResponse: true
}).then((resp) => {
this.update();
return resp.resource.activity_instance;
});
}

/**
Expand Down