feature: emote adding, listing and removing

This commit is contained in:
skkeye
2023-11-02 22:54:08 -04:00
commit ecb0d624ab
11 changed files with 1643 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
config.json
node_modules/

View 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
View 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
View 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
View 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
View 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);
}
})();

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

24
package.json Normal file
View 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"
}
}