@fjrodafo/discord-app 1.0.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.
@@ -0,0 +1,154 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ PermissionFlagsBits,
4
+ EmbedBuilder,
5
+ ButtonBuilder,
6
+ ButtonStyle,
7
+ ActionRowBuilder,
8
+ ComponentType,
9
+ } = require('discord.js');
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const emoji = require('./../../utils/emoji.js');
13
+
14
+ module.exports = {
15
+ category: 'help',
16
+ data: new SlashCommandBuilder()
17
+ .setName('help')
18
+ .setDescription('Shows all available commands!')
19
+ .setDMPermission(false),
20
+ async execute(interaction) {
21
+ // Function to recursively read commands from directories
22
+ function getCommands(dir) {
23
+ const commandFiles = [];
24
+ const files = fs.readdirSync(dir);
25
+
26
+ for (const file of files) {
27
+ const filePath = path.join(dir, file);
28
+ const stat = fs.statSync(filePath);
29
+
30
+ if (stat.isDirectory()) {
31
+ if (file !== 'admin' && file !== 'moderation' && file !== 'context-menu' && file !== 'help') {
32
+ commandFiles.push(...getCommands(filePath));
33
+ }
34
+ if (file === 'admin' && interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
35
+ commandFiles.push(...getCommands(filePath));
36
+ }
37
+ if (file === 'moderation' && interaction.member.permissions.has(PermissionFlagsBits.KickMembers)) {
38
+ commandFiles.push(...getCommands(filePath));
39
+ }
40
+ }
41
+ else if (file.endsWith('.js')) {
42
+ commandFiles.push(filePath);
43
+ }
44
+ }
45
+
46
+ return commandFiles;
47
+ }
48
+
49
+ const commandsDir = path.join(__dirname, './../../commands');
50
+ const commandFiles = getCommands(commandsDir);
51
+
52
+ const commands = commandFiles.map(file => {
53
+ const command = require(file);
54
+ // Get the category (folder name)
55
+ const category = path.basename(path.dirname(file));
56
+
57
+ return {
58
+ name: `${command.data.name} (${category})`,
59
+ description: command.data.description || 'No description',
60
+ };
61
+ });
62
+
63
+ // Divide commands into pages of 5 commands each
64
+ const commandsPerPage = 5;
65
+ const totalPages = Math.ceil(commands.length / commandsPerPage);
66
+
67
+ const pages = [];
68
+ for (let i = 0; i < commands.length; i += commandsPerPage) {
69
+ const page = new EmbedBuilder()
70
+ .setColor(0x5865f2)
71
+ .setTitle('All available commands:')
72
+ .setDescription('You have one minute to turn the page until the buttons are disabled...')
73
+ .setFooter({ text: `Page ${Math.floor(i / commandsPerPage) + 1} of ${totalPages}` });
74
+
75
+ for (let j = i; j < i + commandsPerPage && j < commands.length; j++) {
76
+ page.addFields({ name: '/' + commands[j].name, value: commands[j].description });
77
+ }
78
+
79
+ pages.push(page);
80
+ }
81
+
82
+ let currentPage = 0;
83
+
84
+ // Create the pagination buttons
85
+ const firstPageButton = new ButtonBuilder()
86
+ .setCustomId('first-page-button')
87
+ .setLabel(emoji.arrow_up)
88
+ .setStyle(ButtonStyle.Primary)
89
+ .setDisabled(true);
90
+
91
+ const backButton = new ButtonBuilder()
92
+ .setCustomId('back-button')
93
+ .setLabel(emoji.arrow_left)
94
+ .setStyle(ButtonStyle.Primary)
95
+ .setDisabled(true);
96
+
97
+ const nextButton = new ButtonBuilder()
98
+ .setCustomId('next-button')
99
+ .setLabel(emoji.arrow_right)
100
+ .setStyle(ButtonStyle.Primary)
101
+ .setDisabled(totalPages === 1);
102
+
103
+ const lastPageButton = new ButtonBuilder()
104
+ .setCustomId('last-page-button')
105
+ .setLabel(emoji.arrow_down)
106
+ .setStyle(ButtonStyle.Primary)
107
+ .setDisabled(totalPages === 1);
108
+
109
+ const buttonRow = new ActionRowBuilder().addComponents(firstPageButton, backButton, nextButton, lastPageButton);
110
+
111
+ // Send the initial embed with buttons
112
+ const reply = await interaction.reply({
113
+ embeds: [pages[currentPage]],
114
+ components: [buttonRow],
115
+ ephemeral: true,
116
+ });
117
+
118
+ // Create an event collector for the buttons
119
+ const collector = reply.createMessageComponentCollector({
120
+ componentType: ComponentType.Button,
121
+ filter: (i) => i.user.id === interaction.user.id,
122
+ time: 120_000,
123
+ });
124
+
125
+ collector.on('collect', async (i) => {
126
+ if (i.customId === 'first-page-button') currentPage = 0;
127
+ if (i.customId === 'back-button') currentPage--;
128
+ if (i.customId === 'next-button') currentPage++;
129
+ if (i.customId === 'last-page-button') currentPage = totalPages - 1;
130
+
131
+ // Update button states based on the current page
132
+ firstPageButton.setDisabled(currentPage === 0);
133
+ backButton.setDisabled(currentPage === 0);
134
+ nextButton.setDisabled(currentPage === totalPages - 1);
135
+ lastPageButton.setDisabled(currentPage === totalPages - 1);
136
+
137
+ await i.update({
138
+ embeds: [pages[currentPage]],
139
+ components: [buttonRow],
140
+ });
141
+ });
142
+
143
+ collector.on('end', async () => {
144
+ firstPageButton.setDisabled(true);
145
+ backButton.setDisabled(true);
146
+ nextButton.setDisabled(true);
147
+ lastPageButton.setDisabled(true);
148
+
149
+ await interaction.editReply({
150
+ components: [buttonRow],
151
+ });
152
+ });
153
+ },
154
+ };
@@ -0,0 +1,23 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ PermissionFlagsBits,
4
+ } = require('discord.js');
5
+
6
+ module.exports = {
7
+ category: 'moderation',
8
+ data: new SlashCommandBuilder()
9
+ .setName('kick')
10
+ .setDescription('Select a member and kick them (but not really).')
11
+ .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
12
+ .setDMPermission(false)
13
+ .addUserOption(option => option
14
+ .setName('target')
15
+ .setDescription('The member to kick')
16
+ .setRequired(true),
17
+ ),
18
+ async execute(interaction) {
19
+ const user = interaction.options.getUser('target');
20
+
21
+ await interaction.reply({ content: `You wanted to kick: ${user} :scales:`, ephemeral: true });
22
+ },
23
+ };
@@ -0,0 +1,83 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ ButtonBuilder,
4
+ ButtonStyle,
5
+ ActionRowBuilder,
6
+ ComponentType,
7
+ } = require('discord.js');
8
+ const emoji = require('./../../utils/emoji.js');
9
+
10
+ module.exports = {
11
+ category: 'utility',
12
+ data: new SlashCommandBuilder()
13
+ .setName('buttons')
14
+ .setDescription('Press the buttons!')
15
+ .setDMPermission(false),
16
+ async execute(interaction) {
17
+ const primaryButton = new ButtonBuilder()
18
+ .setCustomId('primary-button')
19
+ .setLabel('Primary button')
20
+ .setStyle(ButtonStyle.Primary);
21
+
22
+ const secondaryButton = new ButtonBuilder()
23
+ .setCustomId('secondary-button')
24
+ .setLabel('Secondary button')
25
+ .setStyle(ButtonStyle.Secondary);
26
+
27
+ const successButton = new ButtonBuilder()
28
+ .setCustomId('success-button')
29
+ .setEmoji(emoji.white_check_mark)
30
+ .setLabel('Success button')
31
+ .setStyle(ButtonStyle.Success);
32
+
33
+ const dangerButton = new ButtonBuilder()
34
+ .setCustomId('danger-button')
35
+ .setEmoji(emoji.warning)
36
+ .setLabel('Danger button')
37
+ .setStyle(ButtonStyle.Danger);
38
+
39
+ const linkButton = new ButtonBuilder()
40
+ .setURL('https://github.com/FJrodafo/DiscordAPP')
41
+ .setLabel('Link button')
42
+ .setStyle(ButtonStyle.Link);
43
+
44
+ const buttonRow = new ActionRowBuilder().addComponents(
45
+ primaryButton,
46
+ secondaryButton,
47
+ successButton,
48
+ dangerButton,
49
+ linkButton,
50
+ );
51
+
52
+ const reply = await interaction.reply({
53
+ content: 'Press the buttons!',
54
+ components: [buttonRow],
55
+ ephemeral: true,
56
+ });
57
+
58
+ const collector = reply.createMessageComponentCollector({
59
+ componentType: ComponentType.Button,
60
+ filter: (i) => i.user.id === interaction.user.id,
61
+ time: 60_000,
62
+ });
63
+
64
+ collector.on('collect', async (i) => {
65
+ if (i.customId === 'primary-button') i.reply({ content: 'You clicked on the primary button!', ephemeral: true });
66
+ if (i.customId === 'secondary-button') i.reply({ content: 'You clicked on the secondary button!', ephemeral: true });
67
+ if (i.customId === 'success-button') i.reply({ content: 'You clicked on the success button!', ephemeral: true });
68
+ if (i.customId === 'danger-button') i.reply({ content: 'You clicked on the danger button!', ephemeral: true });
69
+ });
70
+
71
+ collector.on('end', async () => {
72
+ primaryButton.setDisabled(true);
73
+ secondaryButton.setDisabled(true);
74
+ successButton.setDisabled(true);
75
+ dangerButton.setDisabled(true);
76
+
77
+ reply.edit({
78
+ content: 'You run out of time! Try again later...',
79
+ components: [buttonRow],
80
+ });
81
+ });
82
+ },
83
+ };
@@ -0,0 +1,33 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ EmbedBuilder,
4
+ } = require('discord.js');
5
+
6
+ module.exports = {
7
+ category: 'utility',
8
+ data: new SlashCommandBuilder()
9
+ .setName('embed')
10
+ .setDescription('Embed preview!')
11
+ .setDMPermission(false),
12
+ async execute(interaction) {
13
+ const exampleEmbed = new EmbedBuilder()
14
+ .setColor(0x5865f2)
15
+ .setTitle('Some title')
16
+ .setURL('https://discord.js.org/')
17
+ .setAuthor({ name: 'Some name', iconURL: 'https://i.imgur.com/AfFp7pu.png', url: 'https://discord.js.org' })
18
+ .setDescription('Some description here')
19
+ .setThumbnail('https://i.imgur.com/AfFp7pu.png')
20
+ .addFields(
21
+ { name: 'Regular field title', value: 'Some value here' },
22
+ { name: '\u200B', value: '\u200B' },
23
+ { name: 'Inline field title', value: 'Some value here', inline: true },
24
+ { name: 'Inline field title', value: 'Some value here', inline: true },
25
+ )
26
+ .addFields({ name: 'Inline field title', value: 'Some value here', inline: true })
27
+ .setImage('https://i.imgur.com/AfFp7pu.png')
28
+ .setTimestamp()
29
+ .setFooter({ text: 'Some footer text here', iconURL: 'https://i.imgur.com/AfFp7pu.png' });
30
+
31
+ return interaction.reply({ embeds: [exampleEmbed], ephemeral: true });
32
+ },
33
+ };
@@ -0,0 +1,61 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ EmbedBuilder,
4
+ } = require('discord.js');
5
+
6
+ module.exports = {
7
+ category: 'utility',
8
+ data: new SlashCommandBuilder()
9
+ .setName('info')
10
+ .setDescription('Get info about the server or the user!')
11
+ .setDMPermission(false)
12
+ .addSubcommand(subcommand => subcommand
13
+ .setName('server')
14
+ .setDescription('Display info about this server!'),
15
+ )
16
+ .addSubcommand(subcommand => subcommand
17
+ .setName('user')
18
+ .setDescription('Provides information about the user!')
19
+ .addUserOption(option => option
20
+ .setName('target')
21
+ .setDescription('Choose a user!')
22
+ .setRequired(false),
23
+ ),
24
+ ),
25
+ async execute(interaction) {
26
+ const subcommand = interaction.options.getSubcommand();
27
+
28
+ if (subcommand === 'server') return handleServerInfo(interaction);
29
+ else if (subcommand === 'user') return handleUserInfo(interaction);
30
+ },
31
+ };
32
+
33
+ async function handleServerInfo(interaction) {
34
+ const guild = interaction.guild;
35
+
36
+ const embed = new EmbedBuilder()
37
+ .setThumbnail(`${guild.iconURL()}`)
38
+ .addFields(
39
+ { name: 'Server Name:', value: `${guild.name}`, inline: true },
40
+ { name: 'ID:', value: `${guild.id}`, inline: true },
41
+ { name: 'Total members:', value: `${guild.memberCount}`, inline: true },
42
+ { name: 'Created at:', value: `${guild.createdAt}`, inline: false },
43
+ );
44
+
45
+ await interaction.reply({ embeds: [embed], ephemeral: true });
46
+ }
47
+
48
+ async function handleUserInfo(interaction) {
49
+ const user = interaction.options.getUser('target') || interaction.user;
50
+ const member = interaction.options.getMember('target') || interaction.member;
51
+
52
+ const embed = new EmbedBuilder()
53
+ .setThumbnail(`${user.displayAvatarURL()}`)
54
+ .addFields(
55
+ { name: 'User:', value: `${user.username}`, inline: true },
56
+ { name: 'ID:', value: `${user.id}`, inline: true },
57
+ { name: 'Joined at:', value: `${member.joinedAt}`, inline: false },
58
+ );
59
+
60
+ await interaction.reply({ embeds: [embed], ephemeral: true });
61
+ }
@@ -0,0 +1,189 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ ButtonBuilder,
4
+ ButtonStyle,
5
+ ActionRowBuilder,
6
+ ComponentType,
7
+ EmbedBuilder,
8
+ } = require('discord.js');
9
+ const emoji = require('./../../utils/emoji.js');
10
+
11
+ module.exports = {
12
+ category: 'utility',
13
+ data: new SlashCommandBuilder()
14
+ .setName('paginate')
15
+ .setDescription('Navigate through pages using buttons!')
16
+ .setDMPermission(false)
17
+ .addSubcommand(subcommand => subcommand
18
+ .setName('two-pages')
19
+ .setDescription('Example of pagination with two pages!'),
20
+ )
21
+ .addSubcommand(subcommand => subcommand
22
+ .setName('multiple-pages')
23
+ .setDescription('Example of pagination with multiple pages!'),
24
+ )
25
+ .addSubcommand(subcommand => subcommand
26
+ .setName('too-many-pages')
27
+ .setDescription('Example of pagination with too many pages!'),
28
+ ),
29
+ async execute(interaction) {
30
+ const subcommand = interaction.options.getSubcommand();
31
+
32
+ if (subcommand === 'two-pages') {
33
+ const pages = [
34
+ new EmbedBuilder().setTitle('Page 1').setDescription('This is the first page.').setColor(0x5865f2),
35
+ new EmbedBuilder().setTitle('Page 2').setDescription('This is the second page.').setColor(0x5865f2),
36
+ ];
37
+
38
+ return handlePagination(interaction, pages);
39
+ }
40
+ else if (subcommand === 'multiple-pages') {
41
+ const pages = [
42
+ new EmbedBuilder().setTitle('Page 1').setDescription('This is the first page.').setColor(0x5865f2),
43
+ new EmbedBuilder().setTitle('Page 2').setDescription('This is the second page.').setColor(0x5865f2),
44
+ new EmbedBuilder().setTitle('Page 3').setDescription('This is the third page.').setColor(0x5865f2),
45
+ new EmbedBuilder().setTitle('Page 4').setDescription('This is the fourth page.').setColor(0x5865f2),
46
+ ];
47
+
48
+ return handlePagination(interaction, pages);
49
+ }
50
+ else if (subcommand === 'too-many-pages') {
51
+ const pages = Array.from({ length: 10 }, (_, i) => (
52
+ new EmbedBuilder()
53
+ .setTitle(`Page ${i + 1}`)
54
+ .setDescription('This is the content of the page.')
55
+ .setColor(0x5865f2)
56
+ ));
57
+
58
+ return handleAdvancedPagination(interaction, pages);
59
+ }
60
+ },
61
+ };
62
+
63
+ async function handlePagination(interaction, pages) {
64
+ let currentPage = 0;
65
+
66
+ const backButton = new ButtonBuilder()
67
+ .setCustomId('back-button')
68
+ .setLabel(emoji.arrow_left)
69
+ .setStyle(ButtonStyle.Primary)
70
+ .setDisabled(true);
71
+
72
+ const nextButton = new ButtonBuilder()
73
+ .setCustomId('next-button')
74
+ .setLabel(emoji.arrow_right)
75
+ .setStyle(ButtonStyle.Primary)
76
+ .setDisabled(pages.length === 1);
77
+
78
+ const buttonRow = new ActionRowBuilder().addComponents(
79
+ backButton,
80
+ nextButton,
81
+ );
82
+
83
+ const reply = await interaction.reply({
84
+ embeds: [pages[currentPage]],
85
+ components: [buttonRow],
86
+ ephemeral: true,
87
+ });
88
+
89
+ const collector = reply.createMessageComponentCollector({
90
+ componentType: ComponentType.Button,
91
+ filter: (i) => i.user.id === interaction.user.id,
92
+ time: 60_000,
93
+ });
94
+
95
+ collector.on('collect', async (i) => {
96
+ if (i.customId === 'back-button') currentPage--;
97
+ else if (i.customId === 'next-button') currentPage++;
98
+
99
+ backButton.setDisabled(currentPage === 0);
100
+ nextButton.setDisabled(currentPage === pages.length - 1);
101
+
102
+ await i.update({
103
+ embeds: [pages[currentPage]],
104
+ components: [buttonRow],
105
+ });
106
+ });
107
+
108
+ collector.on('end', async () => {
109
+ backButton.setDisabled(true);
110
+ nextButton.setDisabled(true);
111
+
112
+ await interaction.editReply({ components: [buttonRow] });
113
+ });
114
+ }
115
+
116
+ async function handleAdvancedPagination(interaction, pages) {
117
+ let currentPage = 0;
118
+
119
+ const firstPageButton = new ButtonBuilder()
120
+ .setCustomId('first-page-button')
121
+ .setLabel(emoji.arrow_up)
122
+ .setStyle(ButtonStyle.Primary)
123
+ .setDisabled(true);
124
+
125
+ const backButton = new ButtonBuilder()
126
+ .setCustomId('back-button')
127
+ .setLabel(emoji.arrow_left)
128
+ .setStyle(ButtonStyle.Primary)
129
+ .setDisabled(true);
130
+
131
+ const nextButton = new ButtonBuilder()
132
+ .setCustomId('next-button')
133
+ .setLabel(emoji.arrow_right)
134
+ .setStyle(ButtonStyle.Primary)
135
+ .setDisabled(pages.length <= 1);
136
+
137
+ const lastPageButton = new ButtonBuilder()
138
+ .setCustomId('last-page-button')
139
+ .setLabel(emoji.arrow_down)
140
+ .setStyle(ButtonStyle.Primary)
141
+ .setDisabled(pages.length <= 1);
142
+
143
+ const buttonRow = new ActionRowBuilder().addComponents(
144
+ firstPageButton,
145
+ backButton,
146
+ nextButton,
147
+ lastPageButton,
148
+ );
149
+
150
+ const reply = await interaction.reply({
151
+ embeds: [pages[currentPage]],
152
+ components: [buttonRow],
153
+ ephemeral: true,
154
+ });
155
+
156
+ const collector = reply.createMessageComponentCollector({
157
+ componentType: ComponentType.Button,
158
+ filter: (i) => i.user.id === interaction.user.id,
159
+ time: 120_000,
160
+ });
161
+
162
+ collector.on('collect', async (i) => {
163
+ if (i.customId === 'first-page-button') currentPage = 0;
164
+ if (i.customId === 'back-button') currentPage--;
165
+ if (i.customId === 'next-button') currentPage++;
166
+ if (i.customId === 'last-page-button') currentPage = pages.length - 1;
167
+
168
+ firstPageButton.setDisabled(currentPage === 0);
169
+ backButton.setDisabled(currentPage === 0);
170
+ nextButton.setDisabled(currentPage === pages.length - 1);
171
+ lastPageButton.setDisabled(currentPage === pages.length - 1);
172
+
173
+ await i.update({
174
+ embeds: [pages[currentPage]],
175
+ components: [buttonRow],
176
+ });
177
+ });
178
+
179
+ collector.on('end', async () => {
180
+ firstPageButton.setDisabled(true);
181
+ backButton.setDisabled(true);
182
+ nextButton.setDisabled(true);
183
+ lastPageButton.setDisabled(true);
184
+
185
+ await interaction.editReply({
186
+ components: [buttonRow],
187
+ });
188
+ });
189
+ }
@@ -0,0 +1,133 @@
1
+ const {
2
+ SlashCommandBuilder,
3
+ ChannelSelectMenuBuilder,
4
+ ChannelType,
5
+ RoleSelectMenuBuilder,
6
+ StringSelectMenuBuilder,
7
+ StringSelectMenuOptionBuilder,
8
+ UserSelectMenuBuilder,
9
+ ActionRowBuilder,
10
+ ComponentType,
11
+ } = require('discord.js');
12
+ const emoji = require('./../../utils/emoji.js');
13
+
14
+ module.exports = {
15
+ category: 'utility',
16
+ data: new SlashCommandBuilder()
17
+ .setName('select-menus')
18
+ .setDescription('Make a selection!')
19
+ .setDMPermission(false)
20
+ .addSubcommand(subcommand => subcommand
21
+ .setName('channel')
22
+ .setDescription('Example of a channel selection menu!'),
23
+ )
24
+ .addSubcommand(subcommand => subcommand
25
+ .setName('role')
26
+ .setDescription('Example of a role selection menu!'),
27
+ )
28
+ .addSubcommand(subcommand => subcommand
29
+ .setName('string')
30
+ .setDescription('Example of a string selection menu!'),
31
+ )
32
+ .addSubcommand(subcommand => subcommand
33
+ .setName('user')
34
+ .setDescription('Example of a user selection menu!'),
35
+ ),
36
+ async execute(interaction) {
37
+ const subcommand = interaction.options.getSubcommand();
38
+ const starters = [
39
+ {
40
+ label: 'Bulbasaur',
41
+ description: 'The dual-type Grass/Poison Seed Pokémon.',
42
+ value: 'bulbasaur',
43
+ emoji: emoji.herb,
44
+ },
45
+ {
46
+ label: 'Charmander',
47
+ description: 'The Fire-type Lizard Pokémon.',
48
+ value: 'charmander',
49
+ emoji: emoji.fire,
50
+ },
51
+ {
52
+ label: 'Squirtle',
53
+ description: 'The Water-type Tiny Turtle Pokémon.',
54
+ value: 'squirtle',
55
+ emoji: emoji.bubbles,
56
+ },
57
+ ];
58
+
59
+ if (subcommand === 'channel') {
60
+ const channelMenu = new ChannelSelectMenuBuilder()
61
+ .setCustomId(interaction.id)
62
+ .setMinValues(0)
63
+ .setMaxValues(1)
64
+ .setChannelTypes(ChannelType.GuildText);
65
+
66
+ await handleSelection(interaction, channelMenu, ComponentType.ChannelSelect);
67
+ }
68
+ else if (subcommand === 'role') {
69
+ const roleMenu = new RoleSelectMenuBuilder()
70
+ .setCustomId(interaction.id)
71
+ .setMinValues(0)
72
+ .setMaxValues(1);
73
+
74
+ await handleSelection(interaction, roleMenu, ComponentType.RoleSelect);
75
+ }
76
+ else if (subcommand === 'string') {
77
+ const stringMenu = new StringSelectMenuBuilder()
78
+ .setCustomId('starter')
79
+ .setPlaceholder('Make a selection!')
80
+ .setMinValues(0)
81
+ .setMaxValues(starters.length)
82
+ .addOptions(
83
+ starters.map((starter) => new StringSelectMenuOptionBuilder()
84
+ .setLabel(starter.label)
85
+ .setDescription(starter.description)
86
+ .setValue(starter.value)
87
+ .setEmoji(starter.emoji),
88
+ ),
89
+ );
90
+
91
+ await handleSelection(interaction, stringMenu, ComponentType.StringSelect);
92
+ }
93
+ else if (subcommand === 'user') {
94
+ const userMenu = new UserSelectMenuBuilder()
95
+ .setCustomId(interaction.id)
96
+ .setMinValues(0)
97
+ .setMaxValues(1);
98
+
99
+ await handleSelection(interaction, userMenu, ComponentType.UserSelect);
100
+ }
101
+ },
102
+ };
103
+
104
+ async function handleSelection(interaction, menu, componentType) {
105
+ const actionRow = new ActionRowBuilder().addComponents(menu);
106
+
107
+ const reply = await interaction.reply({
108
+ components: [actionRow],
109
+ ephemeral: true,
110
+ });
111
+
112
+ const collector = reply.createMessageComponentCollector({
113
+ componentType: componentType,
114
+ filter: (i) => i.user.id === interaction.user.id,
115
+ time: 60_000,
116
+ });
117
+
118
+ collector.on('collect', async (i) => {
119
+ await i.deferUpdate();
120
+ const selectedValues = i.values;
121
+
122
+ if (!selectedValues.length) {
123
+ await interaction.followUp({ content: 'You have emptied your selection.', ephemeral: true });
124
+ }
125
+ else {
126
+ await interaction.followUp({ content: `You have selected: ${selectedValues.join(', ')}`, ephemeral: true });
127
+ }
128
+ });
129
+
130
+ collector.on('end', async () => {
131
+ await interaction.editReply({ content: 'You ran out of time! Try again later...', components: [] });
132
+ });
133
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "guildId": "",
3
+ "clientId": "",
4
+ "token": ""
5
+ }