@somehiddenkey/discord-command-utils 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -144,6 +144,30 @@ The same exists for:
|
|
|
144
144
|
- Selection menu : `OnSelectMenu`
|
|
145
145
|
- Message : `OnMessageCommand`
|
|
146
146
|
- Modal submission : `OnModal`
|
|
147
|
+
|
|
148
|
+
A slash command with a specific subcommand can be defined as follows:
|
|
149
|
+
```js
|
|
150
|
+
Interaction.OnSlashCommand(
|
|
151
|
+
{name: "ping", subcommand: "webping"},
|
|
152
|
+
InteractionScope.Main,
|
|
153
|
+
async (interaction) => await interaction.reply("pong")
|
|
154
|
+
);
|
|
155
|
+
```
|
|
156
|
+
### Callback arguments
|
|
157
|
+
#### Message arguments
|
|
158
|
+
Message commands start with a prefix, after which the proceeding arguments are the arguments. E.g. the message `!ping "google.com"` would trigger `async (interaction, website) => //...`
|
|
159
|
+
|
|
160
|
+
#### Interaction IDs
|
|
161
|
+
You can burry hidden information in the custom ID of a selection menu, button and modal submission by concatting them to them to the custom ID. The helper function `Interaction.makeCustomId()` exists for this. This is handy to e.g. hide session IDs as not to have to recompute them. Example:
|
|
162
|
+
```js
|
|
163
|
+
new ButtonBuilder().setCustomId(Interaction.makeCustomId("ping", session_id))
|
|
164
|
+
|
|
165
|
+
Interaction.OnButton(
|
|
166
|
+
"ping", InteractionScope.Main,
|
|
167
|
+
async (interaction, session_id) => await interaction.reply(`pong from ${session_id}`)
|
|
168
|
+
);
|
|
169
|
+
```
|
|
170
|
+
|
|
147
171
|
### Call Interaction
|
|
148
172
|
All interactions get stored in a so-called `InteractionContainer` that still needs to be called:
|
|
149
173
|
```js
|
|
@@ -164,7 +188,7 @@ For slash commands to work, you need to register them to a bot. There are two ki
|
|
|
164
188
|
|
|
165
189
|
You can add a set of slash commands through the following abstraction:
|
|
166
190
|
```js
|
|
167
|
-
const rest = interactionContainer.Rest
|
|
191
|
+
const rest = await interactionContainer.Rest
|
|
168
192
|
.add([firstSlashCommand, secondSlashCommand], localConfig.guilds.tutor)
|
|
169
193
|
.add([firstSlashCommand, secondSlashCommand], localConfig.guilds.community)
|
|
170
194
|
.add([somePublicSlashCommand])
|
package/package.json
CHANGED
package/src/Configs/Fetchable.js
CHANGED
|
@@ -60,13 +60,13 @@ export default class GlobalConfig {
|
|
|
60
60
|
*/
|
|
61
61
|
get_scope(guild_id) {
|
|
62
62
|
switch(guild_id) {
|
|
63
|
-
case this.main:
|
|
63
|
+
case this.main.guild.id:
|
|
64
64
|
return InteractionScope.Main;
|
|
65
|
-
case this.community:
|
|
65
|
+
case this.community.guild.id:
|
|
66
66
|
return InteractionScope.Community;
|
|
67
|
-
case this.tutor:
|
|
67
|
+
case this.tutor.guild.id:
|
|
68
68
|
return InteractionScope.Tutor;
|
|
69
|
-
case this.staff:
|
|
69
|
+
case this.staff.guild.id:
|
|
70
70
|
return InteractionScope.Staff;
|
|
71
71
|
default:
|
|
72
72
|
return InteractionScope.OtherGuild;
|
|
@@ -15,11 +15,11 @@ const {
|
|
|
15
15
|
/**
|
|
16
16
|
* @typedef { (interaction: DiscordBaseInteraction<CacheType>) => Promise<any> } InteractionFunction
|
|
17
17
|
* @typedef { (message: OmitPartialGroupDMChannel<Message<boolean>>) => Promise<any> } InteractionMsgFunction
|
|
18
|
-
* @typedef { string } InteractionID
|
|
18
|
+
* @typedef { {name: string, subcommand: string | null} | string } InteractionID
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
export default class Interaction {
|
|
22
|
-
/** @type {
|
|
22
|
+
/** @type {InteractionID} */
|
|
23
23
|
id;
|
|
24
24
|
/** @type {InteractionType} */
|
|
25
25
|
type;
|
|
@@ -94,4 +94,13 @@ export default class Interaction {
|
|
|
94
94
|
static OnModal(custom_id, scope, target) {
|
|
95
95
|
return Interaction.On(custom_id, InteractionType.Modal, scope, target)
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Hides arbitrary number of arguments in the custom ID
|
|
100
|
+
* @param {...any} args
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
static makeCustomId(...args) {
|
|
104
|
+
return args.map(arg => String(arg)).join("?");
|
|
105
|
+
}
|
|
97
106
|
}
|
|
@@ -5,22 +5,20 @@ import LocalConfig from "../Configs/LocalConfig.js";
|
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import CommandError from "./CommandError.js";
|
|
7
7
|
import RestCommands from './RestCommands.js';
|
|
8
|
-
import
|
|
9
|
-
const {
|
|
10
|
-
Interaction: DiscordBaseInteraction,
|
|
11
|
-
OmitPartialGroupDMChannel,
|
|
12
|
-
Message,
|
|
13
|
-
CacheType
|
|
14
|
-
} = pkg;
|
|
8
|
+
import { ChannelType } from 'discord.js';
|
|
15
9
|
|
|
16
10
|
/**
|
|
11
|
+
* @typedef { import("discord.js").Interaction } DiscordBaseInteraction
|
|
12
|
+
* @typedef { import("discord.js").OmitPartialGroupDMChannel } OmitPartialGroupDMChannel
|
|
13
|
+
* @typedef { import("discord.js").Message } Message
|
|
14
|
+
* @typedef { import("discord.js").CacheType } CacheType
|
|
17
15
|
* @typedef { (interaction: DiscordBaseInteraction<CacheType>) => Promise<any> } InteractionFunction
|
|
18
16
|
* @typedef { (message: OmitPartialGroupDMChannel<Message<boolean>>) => Promise<any> } InteractionMsgFunction
|
|
19
|
-
* @typedef {
|
|
17
|
+
* @typedef { import("./Interaction.js").InteractionID } InteractionID
|
|
20
18
|
*/
|
|
21
19
|
|
|
22
20
|
export default class InteractionContainer {
|
|
23
|
-
/** @type {Map<
|
|
21
|
+
/** @type {Map<string, Interaction>} */
|
|
24
22
|
static #interaction_container = new Map();
|
|
25
23
|
|
|
26
24
|
/** @type {GlobalConfig} */
|
|
@@ -29,42 +27,55 @@ export default class InteractionContainer {
|
|
|
29
27
|
#local_config;
|
|
30
28
|
|
|
31
29
|
/** @type {RestCommands} */
|
|
32
|
-
Rest
|
|
30
|
+
Rest;
|
|
33
31
|
|
|
34
32
|
/**
|
|
35
33
|
* @param {InteractionType} type
|
|
36
34
|
* @param {InteractionScope} scope
|
|
37
|
-
* @param {
|
|
38
|
-
* @returns {
|
|
35
|
+
* @param {InteractionID} id
|
|
36
|
+
* @returns {string} interction ID
|
|
39
37
|
*/
|
|
40
38
|
static #make_key(type, scope, id) {
|
|
41
|
-
return `${type}__${scope}__${id}`;
|
|
39
|
+
return `${type}__${scope}__${id.subcommand ? `${id.name}>${id.subcommand}` : (id.name || id)}`;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
/**
|
|
45
|
-
* @param {InteractionType} type
|
|
46
|
-
* @param {InteractionScope} scope
|
|
47
|
-
* @param {string} id
|
|
48
43
|
* @param {Interaction} base_interaction
|
|
49
44
|
*/
|
|
50
45
|
static add(base_interaction) {
|
|
51
46
|
const key = InteractionContainer.#make_key(base_interaction.type, base_interaction.scope, base_interaction.id);
|
|
47
|
+
consola.debug(`Registered interaction with key ${key}`);
|
|
52
48
|
InteractionContainer.#interaction_container.set(key, base_interaction);
|
|
53
49
|
}
|
|
54
50
|
|
|
55
51
|
/**
|
|
56
52
|
* @param {InteractionType} type
|
|
57
53
|
* @param {InteractionScope} scope
|
|
58
|
-
* @param {
|
|
54
|
+
* @param {InteractionID} id
|
|
59
55
|
* @returns {Interaction?}
|
|
60
56
|
*/
|
|
61
57
|
static get(type, scope, id) {
|
|
62
|
-
|
|
58
|
+
const key = InteractionContainer.#make_key(type, scope, id);
|
|
59
|
+
consola.debug(`Fetching interaction with key ${key}`);
|
|
60
|
+
return InteractionContainer.#interaction_container.get(key);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static dump() {
|
|
64
|
+
return Array.from(InteractionContainer.#interaction_container.entries()).map(([key, interaction]) => {
|
|
65
|
+
return {
|
|
66
|
+
key,
|
|
67
|
+
type: interaction.type,
|
|
68
|
+
scope: interaction.scope,
|
|
69
|
+
id: interaction.id,
|
|
70
|
+
function: interaction.interaction_command_function.name
|
|
71
|
+
}
|
|
72
|
+
});
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
constructor(local_config, global_config) {
|
|
66
76
|
this.#local_config = local_config;
|
|
67
77
|
this.#global_config = global_config;
|
|
78
|
+
this.Rest = new RestCommands(global_config, local_config);
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
/**
|
|
@@ -82,10 +93,10 @@ export default class InteractionContainer {
|
|
|
82
93
|
(interaction.channel.type === ChannelType.DM) ?
|
|
83
94
|
InteractionScope.DM : this.#global_config.get_scope(interaction.guild.id);
|
|
84
95
|
|
|
85
|
-
const customId = interaction.content.slice(this.#local_config.prefix?.length)
|
|
96
|
+
const [customId, ...command_arguments] = interaction.content.slice(this.#local_config.prefix?.length)?.split("?")
|
|
86
97
|
|
|
87
98
|
return InteractionContainer
|
|
88
|
-
.#call(InteractionType.Message, scope,
|
|
99
|
+
.#call(interaction, InteractionType.Message, scope, customId, command_arguments)
|
|
89
100
|
.catch(error => {
|
|
90
101
|
if (error instanceof CommandError)
|
|
91
102
|
return error.error_as_message(interaction)
|
|
@@ -107,8 +118,10 @@ export default class InteractionContainer {
|
|
|
107
118
|
const scope =
|
|
108
119
|
(interaction.channel.type === ChannelType.DM) ?
|
|
109
120
|
InteractionScope.DM : this.#global_config.get_scope(interaction.guild.id);
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
console.log(scope)
|
|
122
|
+
|
|
123
|
+
const [customId, ...command_arguments] = (interaction.commandName || interaction.customId)?.split("?")
|
|
124
|
+
console.log(customId)
|
|
112
125
|
|
|
113
126
|
var type;
|
|
114
127
|
if (interaction.isChatInputCommand())
|
|
@@ -121,9 +134,10 @@ export default class InteractionContainer {
|
|
|
121
134
|
type = InteractionType.Modal;
|
|
122
135
|
else
|
|
123
136
|
return;
|
|
137
|
+
console.log(type)
|
|
124
138
|
|
|
125
139
|
return InteractionContainer
|
|
126
|
-
.#call(type, scope, customId, interaction)
|
|
140
|
+
.#call(interaction, type, scope, {name: customId, subcommand: interaction.options?.getSubcommand()}, command_arguments)
|
|
127
141
|
.catch(error => {
|
|
128
142
|
if (error instanceof CommandError)
|
|
129
143
|
return error.error_as_command(interaction)
|
|
@@ -138,18 +152,19 @@ export default class InteractionContainer {
|
|
|
138
152
|
|
|
139
153
|
/**
|
|
140
154
|
*
|
|
155
|
+
* @param {DiscordBaseInteraction} interaction
|
|
141
156
|
* @param {InteractionType} type
|
|
142
157
|
* @param {InteractionScope} scope
|
|
143
|
-
* @param {InteractionID
|
|
144
|
-
* @param {
|
|
158
|
+
* @param {InteractionID} customId
|
|
159
|
+
* @param {string[]} command_arguments
|
|
145
160
|
* @returns {Promise<any>}
|
|
146
161
|
* @throws {CommandError}
|
|
147
162
|
*/
|
|
148
|
-
static async #call(type, scope, customId,
|
|
149
|
-
const interaction_command = InteractionContainer.get(type, scope, customId
|
|
163
|
+
static async #call(interaction, type, scope, customId, command_arguments = []) {
|
|
164
|
+
const interaction_command = InteractionContainer.get(type, scope, customId);
|
|
150
165
|
if(!interaction_command)
|
|
151
|
-
return consola.error(`No interaction found
|
|
166
|
+
return consola.error(`No interaction found:\n${JSON.stringify({customId, type, scope, command_arguments: command_arguments.join(',')}, null, 2)}`);
|
|
152
167
|
|
|
153
|
-
return await interaction_command.interaction_command_function.apply(null, [interaction, ...
|
|
168
|
+
return await interaction_command.interaction_command_function.apply(null, [interaction, ...command_arguments]);
|
|
154
169
|
}
|
|
155
170
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import GlobalConfig from "../Configs/GlobalConfig/GlobalConfig.js";
|
|
2
2
|
import LocalConfig from "../Configs/LocalConfig.js";
|
|
3
3
|
import { REST, Routes } from "discord.js";
|
|
4
|
+
import { InteractionScope } from "../Utils/enums.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import("discord.js").SlashCommandSubcommandsOnlyBuilder} SlashCommandSubcommandsOnlyBuilder
|
|
8
|
+
*/
|
|
4
9
|
|
|
5
10
|
export default class RestCommands {
|
|
6
11
|
#commands = new Map();
|
|
@@ -20,6 +25,12 @@ export default class RestCommands {
|
|
|
20
25
|
this.#local_config = local_config;
|
|
21
26
|
}
|
|
22
27
|
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param {SlashCommandSubcommandsOnlyBuilder[]} commands
|
|
31
|
+
* @param {InteractionScope} scope
|
|
32
|
+
* @returns {RestCommands}
|
|
33
|
+
*/
|
|
23
34
|
add(commands, scope) {
|
|
24
35
|
if(!!scope)
|
|
25
36
|
this.#commands.set(scope, commands);
|
|
@@ -29,6 +40,10 @@ export default class RestCommands {
|
|
|
29
40
|
return this;
|
|
30
41
|
}
|
|
31
42
|
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} secret_token
|
|
45
|
+
* @returns {Promise<REST>}
|
|
46
|
+
*/
|
|
32
47
|
async register(secret_token){
|
|
33
48
|
const rest = new REST(this.#local_config.restOptions).setToken(secret_token);
|
|
34
49
|
|