@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.
- package/CONTRIBUTING +9 -0
- package/LICENSE +121 -0
- package/dashboard/index.js +27 -0
- package/dashboard/public/index.html +18 -0
- package/dashboard/public/script.js +11 -0
- package/dashboard/public/style.css +0 -0
- package/dashboard/routes/api.js +75 -0
- package/dashboard/routes/logs.js +11 -0
- package/dashboard/routes/metrics.js +57 -0
- package/dashboard/utils/format.js +60 -0
- package/dashboard/utils/logs.js +19 -0
- package/package.json +46 -0
- package/src/commands/admin/ping.js +13 -0
- package/src/commands/admin/prune.js +36 -0
- package/src/commands/admin/reload.js +36 -0
- package/src/commands/context-menu/avatar.js +19 -0
- package/src/commands/context-menu/user.js +26 -0
- package/src/commands/help/help.js +154 -0
- package/src/commands/moderation/kick.js +23 -0
- package/src/commands/utility/buttons.js +83 -0
- package/src/commands/utility/embed.js +33 -0
- package/src/commands/utility/info.js +61 -0
- package/src/commands/utility/paginate.js +189 -0
- package/src/commands/utility/select-menus.js +133 -0
- package/src/config.example.json +5 -0
- package/src/deploy-commands.js +77 -0
- package/src/events/interactionCreate.js +51 -0
- package/src/events/messageCreate.js +16 -0
- package/src/events/ready.js +29 -0
- package/src/index.js +79 -0
- package/src/utils/emoji.js +17 -0
|
@@ -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
|
+
}
|