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: user-installables #1532

Open
wants to merge 55 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
1ce9f24
Add user commands
retrouser955 Aug 25, 2024
e4de85c
assign contexts to application command constructor
retrouser955 Aug 25, 2024
6473441
add context type to commandinteraction
retrouser955 Aug 25, 2024
431381d
Add context constants
retrouser955 Aug 25, 2024
743ac7e
Format: ApplicationCommandContextType
retrouser955 Aug 25, 2024
d58b66c
add testing for user installable
retrouser955 Aug 25, 2024
b5f05a3
Fix: user installables not registering properly
retrouser955 Aug 25, 2024
7247cbf
Add integration_types to ApplicationCommandCreateOptions type
retrouser955 Aug 25, 2024
8c0f31b
make the example more verbose
retrouser955 Aug 25, 2024
d15710d
add a warning to the example
retrouser955 Aug 25, 2024
0295152
chore: lint
retrouser955 Aug 25, 2024
f1f4733
Update typings to be optional
retrouser955 Aug 25, 2024
df8a692
Update index.d.ts
retrouser955 Aug 26, 2024
bdf0330
chore: lint
retrouser955 Aug 26, 2024
5b500ea
Merge branch 'dev' of https://github.com/retrouser955/eris into dev
retrouser955 Aug 26, 2024
b052b0f
alphabetical order
retrouser955 Aug 26, 2024
c0c23d9
remove spacing
retrouser955 Aug 26, 2024
1a81f4b
fix: spacing yet again
retrouser955 Aug 26, 2024
8e566bf
Fix: Missing guild prop && Chore: lint
retrouser955 Aug 26, 2024
19ac5e6
remove magic number
retrouser955 Aug 26, 2024
0c8a24a
update types
retrouser955 Aug 26, 2024
2afdb14
sort properties
retrouser955 Aug 27, 2024
ac00f71
Use a Guild instance
retrouser955 Aug 27, 2024
1cc3695
enhance user command example
Aug 27, 2024
a5fd831
fix: Guild not in cache on user-installables
retrouser955 Aug 29, 2024
62c2f9a
wrote guild assignment more efficiently
retrouser955 Aug 29, 2024
5d5a5ff
chore: lint
retrouser955 Aug 29, 2024
5bc39b4
Merge branch 'dev' of https://github.com/abalabahaha/eris into 1532
bsian03 Aug 31, 2024
adfa5af
lint
bsian03 Aug 31, 2024
03badc8
fix(typings): Type mismatch between discord and lib
retrouser955 Aug 31, 2024
164dab6
Merge branch 'dev' of https://github.com/retrouser955/eris into dev
retrouser955 Aug 31, 2024
f212f2f
chore(add types)
retrouser955 Sep 25, 2024
fd9abb1
chore(format)
retrouser955 Sep 25, 2024
89689e8
Merge branch 'abalabahaha:dev' into dev
retrouser955 Sep 26, 2024
ed04a28
no constructor for autocomplete
retrouser955 Oct 6, 2024
89ea9d1
assign only data.guild
retrouser955 Oct 9, 2024
7fd0d80
revert
retrouser955 Oct 15, 2024
e661cda
Merge branch 'erisdev' into 1532
bsian03 Oct 19, 2024
0533eaf
add authorizingIntegrationOwners
retrouser955 Nov 10, 2024
d2e05fb
Merge branch 'dev' of https://github.com/retrouser955/eris into dev
retrouser955 Nov 10, 2024
1fa31d3
chore(lint) && integration_types_configuration
retrouser955 Nov 11, 2024
f22ad92
add install params and custom install url
retrouser955 Nov 11, 2024
49fbb92
Merge branch 'dev' into dev
retrouser955 Nov 30, 2024
391188a
chore(lint): dts
retrouser955 Dec 13, 2024
3cb938f
deprecate `<Message>.interaction`
retrouser955 Dec 13, 2024
545fb50
feat(interaction metadata): Init types
retrouser955 Dec 13, 2024
dce393c
chore(lint)
retrouser955 Dec 13, 2024
3bb0f7c
update types
retrouser955 Dec 13, 2024
155f669
updated message interaction metadata
retrouser955 Dec 17, 2024
9f47191
docs: add jsdoc
retrouser955 Dec 18, 2024
aefd654
docs(jsdoc): Complete types
retrouser955 Dec 18, 2024
8fd9f8a
chore: lint
retrouser955 Dec 18, 2024
498393d
fix(docs): readd user to interactionMetadata
retrouser955 Dec 18, 2024
239bf73
fix(interactionMetadata): parse model submit correctly
retrouser955 Dec 19, 2024
a7c1777
fix(interactionMetadata): Parse modelsubmit correct
retrouser955 Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions examples/userCommand.js
retrouser955 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const Eris = require("eris");

const Constants = Eris.Constants;

const bot = new Eris.Client("Bot token", {
intents: [],
});

bot.on("ready", async () => {
console.log("connected!");
bot.createCommand({
type: Constants.ApplicationCommandTypes.CHAT_INPUT,
name: "ping",
description: "Reply with pong",
options: [],
/* The following 2 lines set the state of the application command as both user installable and guild installable */
contexts: [
Constants.ApplicationCommandContextType.GUILD,
Constants.ApplicationCommandContextType.BOT_DM,
Constants.ApplicationCommandContextType.PRIVATE,
],
integrationTypes: [
Constants.ApplicationCommandIntegrationTypes.GUILD_INSTALL,
Constants.ApplicationCommandIntegrationTypes.USER_INSTALL,
],
});
});

bot.on("interactionCreate", (interaction) => {
if (interaction instanceof Eris.CommandInteraction) {
if (interaction.data.name === "ping") {
let where = "";
/**
* <CommandInteraction>.context includes information of where the command was executed.
* if <CommandInteraction>.context matches ...
* Constants.ApplicationCommandContextType.GUILD it means it is executed from a server
* Constants.ApplicationCommandContextType.BOT_DM it means it is executed from the bot's DM
* Constants.ApplicationCommandContextType.PRIVATE it means it is executed as a user installable
*
* Incase of BOT_DM and PRIVATE, the CommandInteraction will lose all its guild related properties such as the guild information and member data
*/
const context = interaction.context;

// Check where the command is sent from.
// Keep in mind, context will be the same for commands that are ran as user-installables and guild-invited bots unless user-installables are ran in DMs or are sent in the bot's DMs
if (context === Constants.ApplicationCommandContextType.GUILD) {
where = "as a server interaction.";
} else if (context === Constants.ApplicationCommandContextType.BOT_DM) {
where = "as a bot DM interaction.";
} else if (context === Constants.ApplicationCommandContextType.PRIVATE) {
where = "as a private interaction (user installable).";
} else {
where = "as a server interaction.";
}

interaction.createMessage(`🏓 pong ${where}`);
}
}
});

bot.connect();
55 changes: 55 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ declare namespace Eris {
// TYPES

// Application Commands
type ApplicationCommandContextTypes = (typeof Constants["ApplicationCommandContextType"][keyof typeof Constants["ApplicationCommandContextType"]]);
type ApplicationCommandIntegrationTypes = (typeof Constants["ApplicationCommandIntegrationTypes"][keyof typeof Constants["ApplicationCommandIntegrationTypes"]]);
type ApplicationCommandOptions = ApplicationCommandOptionsSubCommand | ApplicationCommandOptionsSubCommandGroup | ApplicationCommandOptionsWithValue;
type ApplicationCommandOptionsBoolean = ApplicationCommandOption<Constants["ApplicationCommandOptionTypes"]["BOOLEAN"]>;
type ApplicationCommandOptionsChannel = ApplicationCommandOption<Constants["ApplicationCommandOptionTypes"]["CHANNEL"]>;
Expand Down Expand Up @@ -168,6 +170,7 @@ declare namespace Eris {
type SelectMenuNonResolvedTypes = Constants["ComponentTypes"][keyof Pick<Constants["ComponentTypes"], "STRING_SELECT">];
type SelectMenuResolvedTypes = Constants["ComponentTypes"][keyof Pick<Constants["ComponentTypes"], "USER_SELECT" | "ROLE_SELECT" | "MENTIONABLE_SELECT" | "CHANNEL_SELECT">];
type SelectMenuTypes = SelectMenuNonResolvedTypes | SelectMenuResolvedTypes;
type MessageInteractionMetadata = MessageComponentInteractionMetadata | ApplicationCommandInteractionMetadata | ModelSubmitInteractionMetadata;

// Permission
type PermissionType = Constants["PermissionOverwriteTypes"][keyof Constants["PermissionOverwriteTypes"]];
Expand Down Expand Up @@ -228,7 +231,9 @@ declare namespace Eris {
}
/** Generic T is `true` if creating Guild scoped commands, and `false` if not */
interface ApplicationCommandCreateOptions<T extends boolean, U = ApplicationCommandTypes> extends ApplicationCommandEditOptions<T, U> {
contexts?: ApplicationCommandContextTypes[];
description: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : "" | void;
integrationTypes?: ApplicationCommandIntegrationTypes[];
name: string;
type?: U;
retrouser955 marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down Expand Up @@ -1357,6 +1362,10 @@ declare namespace Eris {
target_id?: string;
options: InteractionDataOptions[];
}
interface AuthorizingIntegrationOwners {
guildInstall?: string;
userInstall: string;
}
interface CommandInteractionData {
id: string;
name: string;
Expand Down Expand Up @@ -1605,6 +1614,26 @@ declare namespace Eris {
type: InteractionTypes;
user: User;
}
interface BaseMessageMetadata {
authorizingIntegrationOwners: AuthorizingIntegrationOwners;
id: string;
originalResponseMessageID?: string;
type: InteractionTypes;
user: User;
}
interface ApplicationCommandInteractionMetadata extends BaseMessageMetadata {
targetMessageID?: string;
targetUser?: User;
type: Constants["InteractionTypes"]["APPLICATION_COMMAND"];
}
interface MessageComponentInteractionMetadata extends BaseMessageMetadata {
interactedMessageID: string;
type: Constants["InteractionTypes"]["MESSAGE_COMPONENT"];
}
interface ModelSubmitInteractionMetadata extends BaseMessageMetadata {
triggeringInteractionMetadata: MessageComponentInteractionMetadata | ApplicationCommandInteractionMetadata;
type: Constants["InteractionTypes"]["MODAL_SUBMIT"];
}
interface MessageReference extends MessageReferenceBase {
channelID: string;
}
Expand Down Expand Up @@ -2002,12 +2031,23 @@ declare namespace Eris {
[key: string]: unknown;
}
interface OAuthApplicationInfo {
approximate_guild_count?: number;
approximate_user_install_count?: number;
bot?: PartialUser;
bot_public: boolean;
bot_require_code_grant: boolean;
custom_install_url?: string;
description: string;
icon: string | null;
id: string;
integration_types_config: {
0?: OAuthApplicationIntegrationTypeConfiguration;
1?: OAuthApplicationIntegrationTypeConfiguration;
}; // TODO: Configure types for this properly
install_params?: {
scopes: string[];
permissions: string;
};
name: string;
owner: PartialUser;
privacy_policy_url?: string;
Expand All @@ -2017,6 +2057,12 @@ declare namespace Eris {
terms_of_service_url?: string;
verify_key: string;
}
interface OAuthApplicationIntegrationTypeConfiguration {
oauth2_install_params?: {
scopes: string[];
permissions: string;
};
}
interface OAuthTeamInfo {
icon: string | null;
id: string;
Expand Down Expand Up @@ -2050,13 +2096,15 @@ declare namespace Eris {
/** Generic T is `true` if a Guild scoped command, and `false` if not */
export class ApplicationCommand<T extends boolean, U = ApplicationCommandTypes> extends Base {
applicationID: string;
contexts?: ApplicationCommandContextTypes[] | null;
defaultMemberPermissions: Permission;
/** @deprecated */
defaultPermission?: boolean | null;
retrouser955 marked this conversation as resolved.
Show resolved Hide resolved
description: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : "";
descriptionLocalizations?: U extends "CHAT_INPUT" ? Record<LocaleStrings, string> | null : null;
dmPermission?: boolean;
guild: T extends true ? PossiblyUncachedGuild : never;
integrationTypes?: ApplicationCommandIntegrationTypes[];
name: string;
nameLocalizations?: Record<LocaleStrings, string> | null;
nsfw?: boolean;
Expand Down Expand Up @@ -2933,6 +2981,7 @@ declare namespace Eris {
export class AutocompleteInteraction<T extends PossiblyUncachedTextableChannel = TextableChannel> extends Interaction {
appPermissions?: Permission;
channel: T;
context: ApplicationCommandContextTypes;
data: AutocompleteInteractionData;
guildID: T extends AnyGuildChannel ? string : undefined;
member: T extends AnyGuildChannel ? Member : undefined;
Expand All @@ -2945,6 +2994,7 @@ declare namespace Eris {
export class Interaction extends Base {
acknowledged: boolean;
applicationID: string;
authorizingIntegrationOwners: AuthorizingIntegrationOwners;
id: string;
token: string;
type: number;
Expand All @@ -2961,6 +3011,7 @@ declare namespace Eris {
export class CommandInteraction<T extends PossiblyUncachedTextableChannel = TextableChannel> extends Interaction {
appPermissions?: Permission;
channel: T;
context: ApplicationCommandContextTypes;
data: CommandInteractionData;
guildID: T extends AnyGuildChannel ? string : undefined;
member: T extends AnyGuildChannel ? Member : undefined;
Expand All @@ -2981,6 +3032,7 @@ declare namespace Eris {
export class ComponentInteraction<T extends PossiblyUncachedTextableChannel = TextableChannel> extends Interaction {
appPermissions?: Permission;
channel: T;
context: ApplicationCommandContextTypes;
data: ComponentInteractionButtonData | ComponentInteractionSelectMenuData;
guildID: T extends AnyGuildChannel ? string : undefined;
member: T extends AnyGuildChannel ? Member : undefined;
Expand Down Expand Up @@ -3120,7 +3172,9 @@ declare namespace Eris {
flags: number;
guildID: T extends GuildTextableWithThreads ? string : undefined;
id: string;
/** @deprecated */
interaction: MessageInteraction | null;
interactionMetadata: MessageInteractionMetadata | null;
jumpLink: string;
member: T extends GuildTextableWithThreads ? Member : null;
mentionEveryone: boolean;
Expand Down Expand Up @@ -3161,6 +3215,7 @@ declare namespace Eris {

export class ModalSubmitInteraction<T extends PossiblyUncachedTextableChannel = TextableChannel> extends Interaction {
channel: T;
context: ApplicationCommandContextTypes;
data: ModalSubmitInteractionData;
guildID: T extends AnyGuildChannel ? string : undefined;
member: T extends AnyGuildChannel ? Member : undefined;
Expand Down
3 changes: 3 additions & 0 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -650,11 +650,13 @@ class Client extends EventEmitter {
/**
* Create a global application command
* @arg {Object} command A command object
* @arg {Number[]?} [command.contexts] Configure if the command is a user-installable GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types)
* @arg {BigInt | Number | String | Permission?} command.defaultMemberPermissions The default permissions the user must have to use the command
* @arg {Boolean} [command.defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions`
* @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands
* @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description
* @arg {Boolean?} [command.dmPermission=true] Whether the command is available in DMs with the app
* @arg {Number[]?} [command.integrationTypes] Configure the installation state of the slash command GUILD_INSTALL=0 USER_INSTALL=1 [more info](https://discord.com/developers/docs/resources/application#application-object-application-integration-types)
* @arg {String} command.name The command name
* @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name
* @arg {Boolean} [command.nsfw=false] Whether the command is age-restricted
Expand All @@ -680,6 +682,7 @@ class Client extends EventEmitter {
command.default_permission = command.defaultPermission;
}
command.dm_permission = command.dmPermission;
command.integration_types = command.integrationTypes;
return this.requestHandler.request("POST", Endpoints.COMMANDS(this.application.id), true, command).then((command) => new ApplicationCommand(command, this));
}

Expand Down
17 changes: 13 additions & 4 deletions lib/Constants.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ export default interface Constants {
CUSTOM: 4;
COMPETING: 5;
};
ApplicationCommandContextType: {
GUILD: 0;
BOT_DM: 1;
PRIVATE: 2;
};
ApplicationCommandIntegrationTypes: {
GUILD_INSTALL: 0;
USER_INSTALL: 1;
};
ApplicationCommandOptionTypes: {
SUB_COMMAND: 1;
SUB_COMMAND_GROUP: 2;
Expand Down Expand Up @@ -578,11 +587,11 @@ export default interface Constants {
sendVoiceMessages: 70368744177664n;
setVoiceChannelStatus: 281474976710656n;
sendPolls: 562949953421312n;
allGuild: 29697484783806n;
allText: 633854226857041n;
allVoice: 954930478188305n;
all: 985162418487295n;
useExternalApps: 1125899906842624n;
allGuild: 1155597391626430n;
allText: 1759754133699665n;
allVoice: 2080830385030929n;
all: 2111062325329919n;
};
PollLayoutTypes: {
DEFAULT: 1;
Expand Down
11 changes: 11 additions & 0 deletions lib/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ module.exports.ActivityTypes = {
COMPETING: 5,
};

module.exports.ApplicationCommandContextType = {
GUILD: 0,
BOT_DM: 1,
PRIVATE: 2,
};

module.exports.ApplicationCommandIntegrationTypes = {
GUILD_INSTALL: 0,
USER_INSTALL: 1,
};

module.exports.ApplicationCommandOptionTypes = {
SUB_COMMAND: 1,
SUB_COMMAND_GROUP: 2,
Expand Down
8 changes: 8 additions & 0 deletions lib/structures/ApplicationCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ const Permission = require("./Permission");
/**
* Represents an application command
* @prop {String} applicationID The ID of the application that this command belongs to
* @prop {Number[]?} contexts The context of the command (if the command is a user-installable or a guild-installable)
* @prop {Permission?} defaultMemberPermissions The permissions required by default for this command to be usable
* @prop {Boolean?} defaultPermission [DEPRECATED] Indicates whether the command is enabled by default when the application is added to a guild
* @prop {String} description The description of the command (empty for user & message commands)
* @prop {Object?} descriptionLocalizations A map of [locales](https://discord.com/developers/docs/reference#locales) to descriptions for that locale
* @prop {Boolean?} dmPermission If this command can be used in direct messages (global commands only)
* @prop {Guild?} guild The guild associated with this command (guild commands only)
* @prop {String} id The ID of the application command
* @prop {Number[]?} integrationTypes The installation state of the slash command GUILD_INSTALL=0 USER_INSTALL=1 [more info](https://discord.com/developers/docs/resources/application#application-object-application-integration-types)
* @prop {String} name The name of the command
* @prop {Object?} nameLocalizations A map of [locales](https://discord.com/developers/docs/reference#locales) to names for that locale
* @prop {Boolean?} nsfw Indicates whether the command is age-restricted
Expand Down Expand Up @@ -56,6 +58,12 @@ class ApplicationCommand extends Base {
if (data.nsfw !== undefined) {
this.nsfw = data.nsfw;
}
if (data.contexts !== undefined) {
this.contexts = data.contexts;
}
if (data.integration_types !== undefined) {
this.integrationTypes = data.integration_types;
}
}

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/structures/AutocompleteInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

const Interaction = require("./Interaction");
const Permission = require("./Permission");
const Guild = require("./Guild");
const { InteractionResponseTypes } = require("../Constants");

/**
* Represents an application command autocomplete interaction. See Interaction for more properties.
* @extends Interaction
* @prop {Permission?} appPermissions The permissions the app or bot has within the channel the interaction was sent from
* @prop {DMChannel | TextChannel | NewsChannel} channel The channel the interaction was created in. Can be partial with only the id if the channel is not cached
* @prop {Number} context Where the interaction is sent from GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types)
* @prop {Object} data The data attached to the interaction
* @prop {String} data.id The ID of the Application Command
* @prop {String} data.name The command name
Expand All @@ -32,6 +34,7 @@ class AutocompleteInteraction extends Interaction {
id: data.channel_id,
};

this.context = data.context;
this.data = data.data;

if (data.guild_id !== undefined) {
Expand All @@ -43,7 +46,7 @@ class AutocompleteInteraction extends Interaction {
if (this.channel.guild) {
this.member = this.channel.guild.members.update(data.member, this.channel.guild);
} else {
const guild = this._client.guilds.get(data.guild_id);
const guild = this._client.guilds.get(data.guild_id) || new Guild(data.guild, this._client);
retrouser955 marked this conversation as resolved.
Show resolved Hide resolved
this.member = guild.members.update(data.member, guild);
}
}
Expand Down
5 changes: 4 additions & 1 deletion lib/structures/CommandInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const Channel = require("./Channel");
const Message = require("./Message");
const Collection = require("../util/Collection");
const Permission = require("./Permission");
const Guild = require("./Guild");

const { InteractionResponseTypes } = require("../Constants");

Expand All @@ -16,6 +17,7 @@ const { InteractionResponseTypes } = require("../Constants");
* @extends Interaction
* @prop {Permission?} appPermissions The permissions the app or bot has within the channel the interaction was sent from
* @prop {DMChannel | TextChannel | NewsChannel} channel The channel the interaction was created in. Can be partial with only the id if the channel is not cached
* @prop {Number} context Where the interaction is sent from GUILD=0 BOT_DM=1 PRIVATE=2 [more info](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types)
* @prop {Object} data The data attached to the interaction
* @prop {String} data.id The ID of the Application Command
* @prop {String} data.name The command name
Expand Down Expand Up @@ -44,6 +46,7 @@ class CommandInteraction extends Interaction {
};

this.data = JSON.parse(JSON.stringify(data.data));
this.context = data.context;

if (data.data.resolved !== undefined) {
// Users
Expand Down Expand Up @@ -104,7 +107,7 @@ class CommandInteraction extends Interaction {
if (this.channel.guild) {
this.member = this.channel.guild.members.update(data.member, this.channel.guild);
} else {
const guild = this._client.guilds.get(data.guild_id);
const guild = this._client.guilds.get(data.guild_id) || new Guild(data.guild, this._client);
this.member = guild.members.update(data.member, guild);
}
}
Expand Down
Loading