feature: emote adding, listing and removing
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
config.json
|
||||||
|
node_modules/
|
43
commands/emotes/emotelog.js
Normal file
43
commands/emotes/emotelog.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("emotelog")
|
||||||
|
.setDescription("Logs the emote inputted.")
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName("emote")
|
||||||
|
.setDescription("The emote to log.")
|
||||||
|
.setRequired(true)
|
||||||
|
),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
|
// this regex matches emotes in the format <:name:id> or <a:name:id>
|
||||||
|
let emoteIdRegex = /<(?<animated>a?):(?<name>\w{0,22})\w*:(?<id>\d+)>/gm;
|
||||||
|
let replyMessage = "";
|
||||||
|
const input = interaction.options.getString("emote");
|
||||||
|
// remove duplicates from the emote list
|
||||||
|
let emoteList = input.matchAll(emoteIdRegex);
|
||||||
|
emoteList = [...new Set(emoteList)].join(" ");
|
||||||
|
for (const match of emoteList.matchAll(emoteIdRegex)) {
|
||||||
|
const link = "https://cdn.discordapp.com/emojis/" + match.groups.id + ((match.groups.animated == 'a') ? ".gif": ".webp") + "?quality=lossless";
|
||||||
|
const copyname = match.groups.name + "_FE";
|
||||||
|
await interaction.guild.emojis.create({attachment: link, name: copyname});
|
||||||
|
await interaction.editReply(`Logged ${match[0]}!\n`);
|
||||||
|
replyMessage += `Logged ${match[0]}!\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a message to say how many emote spots are left
|
||||||
|
interaction.guild.emojis.fetch()
|
||||||
|
.then(emojis => {
|
||||||
|
let animatedEmojis = emojis.filter(emoji => emoji.animated);
|
||||||
|
replyMessage += `\nAnimated emotes: ${animatedEmojis.size}/50\n`
|
||||||
|
let staticEmojis = emojis.filter(emoji => !emoji.animated);
|
||||||
|
replyMessage += `Static emotes: ${staticEmojis.size}/50\n`
|
||||||
|
interaction.editReply(replyMessage);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://cdn.discordapp.com/emojis/1166779045711720541.webp?size=44&quality=lossless
|
28
commands/emotes/list.js
Normal file
28
commands/emotes/list.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("listemotes")
|
||||||
|
.setDescription("Lists all emotes in the server."),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.reply("Fetching emotes...");
|
||||||
|
let replyMessage = "";
|
||||||
|
interaction.guild.emojis.fetch()
|
||||||
|
.then(emojis => {
|
||||||
|
// we need to make followup messages if the list is too long
|
||||||
|
let emoteList = "";
|
||||||
|
emojis.forEach(emoji => {
|
||||||
|
emoteList += `${emoji} - ${emoji.name}\n`;
|
||||||
|
if (emoteList.length > 1900) {
|
||||||
|
interaction.followUp(emoteList);
|
||||||
|
emoteList = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (emoteList.length > 0) {
|
||||||
|
interaction.followUp(emoteList);
|
||||||
|
}
|
||||||
|
interaction.followUp("Total emotes: " + emojis.size + "\n");
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
},
|
||||||
|
};
|
29
commands/emotes/rm.js
Normal file
29
commands/emotes/rm.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("rm")
|
||||||
|
.setDescription("Removes emotes from the server.")
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName("emotes")
|
||||||
|
.setDescription("Emotes to remove.")
|
||||||
|
.setRequired(true)),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.reply("Removing emotes...");
|
||||||
|
// find every emote in the message and remove them
|
||||||
|
let emoteList = interaction.options.getString("emotes");
|
||||||
|
let emoteIdRegex = /<(?<animated>a?):(?<name>\w{0,22})\w*:(?<id>\d+)>/gm;
|
||||||
|
let replyMessage = "";
|
||||||
|
for (const match of emoteList.matchAll(emoteIdRegex)) {
|
||||||
|
let emote = interaction.guild.emojis.cache.find(emoji => emoji.id === match.groups.id);
|
||||||
|
if (emote) {
|
||||||
|
await emote.delete();
|
||||||
|
replyMessage += `Removed ${match[0]}!\n`;
|
||||||
|
} else {
|
||||||
|
replyMessage += `Could not find ${match[0]}!\n`;
|
||||||
|
}
|
||||||
|
interaction.editReply(replyMessage);
|
||||||
|
replyMessage = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
10
commands/utils/ping.js
Normal file
10
commands/utils/ping.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName("ping")
|
||||||
|
.setDescription("Replies with Pong!"),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.reply("Pong!");
|
||||||
|
},
|
||||||
|
};
|
46
deploy-commands.js
Normal file
46
deploy-commands.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const { REST, Routes } = require('discord.js');
|
||||||
|
const { clientId, token } = require('./config.json');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const commands = [];
|
||||||
|
// Grab all the command files from the commands directory you created earlier
|
||||||
|
const foldersPath = path.join(__dirname, 'commands');
|
||||||
|
const commandFolders = fs.readdirSync(foldersPath);
|
||||||
|
|
||||||
|
for (const folder of commandFolders) {
|
||||||
|
// Grab all the command files from the commands directory you created earlier
|
||||||
|
const commandsPath = path.join(foldersPath, folder);
|
||||||
|
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||||
|
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||||
|
for (const file of commandFiles) {
|
||||||
|
const filePath = path.join(commandsPath, file);
|
||||||
|
const command = require(filePath);
|
||||||
|
if ('data' in command && 'execute' in command) {
|
||||||
|
commands.push(command.data.toJSON());
|
||||||
|
} else {
|
||||||
|
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct and prepare an instance of the REST module
|
||||||
|
const rest = new REST().setToken(token);
|
||||||
|
|
||||||
|
// and deploy your commands!
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||||
|
|
||||||
|
// The put method is used to fully refresh all commands in the guild with the current set
|
||||||
|
const data = await rest.put(
|
||||||
|
Routes.applicationCommands(clientId),
|
||||||
|
{ body: commands },
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||||
|
} catch (error) {
|
||||||
|
// And of course, make sure you catch and log any errors!
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
})();
|
26
events/interactionCreate.js
Normal file
26
events/interactionCreate.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const { Events } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.InteractionCreate,
|
||||||
|
async execute(interaction) {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
const command = interaction.client.commands.get(interaction.commandName);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await command.execute(interaction);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
if (interaction.replied || interaction.deferred) {
|
||||||
|
await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
|
} else {
|
||||||
|
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
9
events/ready.js
Normal file
9
events/ready.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const { Events } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.ClientReady,
|
||||||
|
once: true,
|
||||||
|
execute(client) {
|
||||||
|
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||||
|
},
|
||||||
|
};
|
42
index.js
Normal file
42
index.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
|
||||||
|
const { token } = require('./config.json');
|
||||||
|
|
||||||
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||||
|
|
||||||
|
client.commands = new Collection();
|
||||||
|
|
||||||
|
const foldersPath = path.join(__dirname, 'commands');
|
||||||
|
const commandFolders = fs.readdirSync(foldersPath);
|
||||||
|
|
||||||
|
for (const folder of commandFolders) {
|
||||||
|
const commandsPath = path.join(foldersPath, folder);
|
||||||
|
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||||
|
for (const file of commandFiles) {
|
||||||
|
const filePath = path.join(commandsPath, file);
|
||||||
|
const command = require(filePath);
|
||||||
|
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||||
|
if ('data' in command && 'execute' in command) {
|
||||||
|
client.commands.set(command.data.name, command);
|
||||||
|
} else {
|
||||||
|
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const eventsPath = path.join(__dirname, 'events');
|
||||||
|
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
|
||||||
|
|
||||||
|
for (const file of eventFiles) {
|
||||||
|
const filePath = path.join(eventsPath, file);
|
||||||
|
const event = require(filePath);
|
||||||
|
if (event.once) {
|
||||||
|
client.once(event.name, (...args) => event.execute(...args));
|
||||||
|
} else {
|
||||||
|
client.on(event.name, (...args) => event.execute(...args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.login(token);
|
1384
package-lock.json
generated
Normal file
1384
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
package.json
Normal file
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "emot_man",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "A discord emoji bot",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node index.js",
|
||||||
|
"deploy-commands": "node deploy-commands.js"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"discord",
|
||||||
|
"bot",
|
||||||
|
"emotes",
|
||||||
|
"manager"
|
||||||
|
],
|
||||||
|
"author": "skkeye",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"discord.js": "^14.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^8.52.0"
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user