Initial commit of karuta-starboard bot

This commit is contained in:
UltraLionFr
2025-07-19 05:01:14 +02:00
commit 85800e3c8f
13 changed files with 371 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
node_modules/
package-lock.json
.env
db.json
+30
View File
@@ -0,0 +1,30 @@
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
const { getMessages } = require('../utils/configManager');
const { setGuildData } = require('../utils/dbManager');
module.exports = {
data: new SlashCommandBuilder()
.setName('setemoji')
.setDescription('Set the reaction emoji')
.addStringOption(option =>
option.setName('emoji')
.setDescription('Emoji (ex: ⭐ or <:name:id> or <a:name:id>)')
.setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
async execute(interaction) {
const emoji = interaction.options.getString('emoji');
setGuildData(interaction.guildId, { emoji });
const messages = getMessages();
if (!messages.emoji_set_success) {
return interaction.reply({
content: '❌ Missing `emoji_set_success` message in config.yml.',
ephemeral: true
});
}
const reply = messages.emoji_set_success.replace('{emoji}', emoji);
await interaction.reply({ content: reply, ephemeral: true });
}
};
+30
View File
@@ -0,0 +1,30 @@
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
const { getMessages } = require('../utils/configManager');
const { setGuildData } = require('../utils/dbManager');
module.exports = {
data: new SlashCommandBuilder()
.setName('setstarboard')
.setDescription('Set up the highlight channel')
.addChannelOption(option =>
option.setName('channel')
.setDescription('Select the channel for highlighted messages')
.setRequired(true))
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild),
async execute(interaction) {
const channel = interaction.options.getChannel('channel');
setGuildData(interaction.guildId, { starboard: channel.id });
const messages = getMessages();
if (!messages.starboard_set_success) {
return interaction.reply({
content: '❌ Missing `starboard_set_success` message in config.yml.',
ephemeral: true
});
}
const reply = messages.starboard_set_success.replace('{channel}', `<#${channel.id}>`);
await interaction.reply({ content: reply, ephemeral: true });
}
};
+12
View File
@@ -0,0 +1,12 @@
messages:
error: "❌ An error occurred while executing the command."
emoji_set_success: "Emoji set: {emoji}"
starboard_set_success: "Channel set: {channel}"
embed_jump_text: "Click to jump to message!"
appearance:
embed_color: "#ff5733"
activity:
name: "Made by UltraLion"
type: "WATCHING" # PLAYING, LISTENING, COMPETING, WATCHING
+23
View File
@@ -0,0 +1,23 @@
const { REST, Routes } = require('discord.js');
const fs = require('fs');
require('dotenv').config();
const commands = [];
const commandFiles = fs.readdirSync('./SlashCommands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`./SlashCommands/${file}`);
commands.push(command.data.toJSON());
}
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);
(async () => {
try {
console.log('⏳ Deploying slash commands...');
await rest.put(Routes.applicationCommands(process.env.CLIENT_ID), { body: commands });
console.log('✅ Commands deployed successfully.');
} catch (error) {
console.error(error);
}
})();
+19
View File
@@ -0,0 +1,19 @@
module.exports = {
name: 'interactionCreate',
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
await interaction.reply({
content: '❌ Error executing command.',
ephemeral: true
});
}
}
};
+46
View File
@@ -0,0 +1,46 @@
const { EmbedBuilder } = require('discord.js');
const { getGuildData } = require('../utils/dbManager');
const { getMessages, getAppearance } = require('../utils/configManager');
module.exports = {
name: 'messageReactionAdd',
async execute(reaction, user) {
if (user.bot) return;
const message = reaction.message.partial ? await reaction.message.fetch() : reaction.message;
const guildConfig = getGuildData(message.guildId);
if (!guildConfig || !guildConfig.emoji || !guildConfig.starboard) return;
const usedEmoji = reaction.emoji.toString();
if (usedEmoji !== guildConfig.emoji) return;
if (!message.author?.bot || !message.author.username.includes('Karuta')) return;
const channel = await reaction.client.channels.fetch(guildConfig.starboard);
const messages = getMessages();
const appearance = getAppearance();
if (!messages.embed_jump_text || !appearance.embed_color) {
console.warn('⚠️ Missing config: embed_jump_text or embed_color');
return;
}
const jumpLink = `https://discord.com/channels/${message.guildId}/${message.channelId}/${message.id}`;
const jumpText = messages.embed_jump_text;
const embedColor = parseInt(appearance.embed_color.replace('#', ''), 16);
const embed = new EmbedBuilder()
.setDescription(`${message.content}\n\n[${jumpText}](${jumpLink})`)
.setColor(embedColor)
.setTimestamp();
const attachment = message.attachments.find(att =>
att.contentType?.startsWith('image/') || att.contentType?.startsWith('video/')
);
if (attachment) embed.setImage(attachment.url);
await channel.send({ embeds: [embed] });
}
};
+20
View File
@@ -0,0 +1,20 @@
const { ActivityType } = require('discord.js');
const { getActivity } = require('../utils/configManager');
module.exports = {
name: 'ready',
once: true,
execute(client) {
console.log(`✅ Logged in as ${client.user.tag}`);
const activity = getActivity();
if (!activity.name || !activity.type) {
console.warn('⚠️ Missing configuration for bot status in config.yml.');
return;
}
client.user.setActivity(activity.name, {
type: ActivityType[activity.type.toUpperCase()]
});
}
};
+38
View File
@@ -0,0 +1,38 @@
const { Client, GatewayIntentBits, Collection, Partials } = require('discord.js');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMessageReactions
],
partials: [Partials.Message, Partials.Channel, Partials.Reaction]
});
client.commands = new Collection();
// Load commands
const commandsPath = path.join(__dirname, 'SlashCommands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(path.join(commandsPath, file));
client.commands.set(command.data.name, command);
}
// Load events
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const event = require(path.join(eventsPath, file));
if (event.once) {
client.once(event.name, (...args) => event.execute(...args, client));
} else {
client.on(event.name, (...args) => event.execute(...args, client));
}
}
client.login(process.env.TOKEN);
+22
View File
@@ -0,0 +1,22 @@
{
"dependencies": {
"@discordjs/rest": "^2.5.1",
"csv-parser": "^3.2.0",
"discord-api-types": "^0.38.13",
"discord.js": "^14.20.0",
"dotenv": "^16.5.0",
"fs": "^0.0.1-security",
"node-fetch": "^3.3.2",
"yaml": "^2.8.0"
},
"name": "dabi",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
+26
View File
@@ -0,0 +1,26 @@
require('dotenv').config();
const { Client, GatewayIntentBits } = require('discord.js');
const fs = require('fs');
const path = require('path');
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
const TOKEN = process.env.TOKEN;
const AVATAR_PATH = path.join(__dirname, 'assets', 'avatar.gif');
client.once('ready', async () => {
try {
// Read GIF file
const avatar = fs.readFileSync(AVATAR_PATH);
// Set the avatar
await client.user.setAvatar(avatar);
console.log('Bot avatar successfully updated!');
} catch (error) {
console.error('Erreur de Discord :', error.response?.data || error);
console.error('Error updating bot avatar:', error);
}
});
client.login(TOKEN);
+65
View File
@@ -0,0 +1,65 @@
const fs = require('fs');
const YAML = require('yaml');
const configPath = './config.yml';
function readConfig() {
try {
const file = fs.readFileSync(configPath, 'utf8');
return YAML.parse(file) || {};
} catch (error) {
console.error('Error reading config.yml file:', error);
return {};
}
}
function writeConfig(config) {
try {
const yamlString = YAML.stringify(config);
fs.writeFileSync(configPath, yamlString, 'utf8');
} catch (error) {
console.error('Error writing to config.yml:', error);
}
}
function getMessages() {
const config = readConfig();
return config.messages || {};
}
function getGuildConfig(guildId) {
const config = readConfig();
if (!config.guilds) config.guilds = {};
if (!config.guilds[guildId]) config.guilds[guildId] = {};
return config.guilds[guildId];
}
function setGuildConfig(guildId, newData) {
const config = readConfig();
if (!config.guilds) config.guilds = {};
config.guilds[guildId] = {
...config.guilds[guildId],
...newData
};
writeConfig(config);
}
function getAppearance() {
const config = readConfig();
return config.appearance || {};
}
function getActivity() {
const config = readConfig();
return config.activity || {};
}
module.exports = {
readConfig,
writeConfig,
getMessages,
getGuildConfig,
setGuildConfig,
getAppearance,
getActivity
};
+36
View File
@@ -0,0 +1,36 @@
const fs = require('fs');
const dbPath = './db.json';
function readDB() {
try {
const data = fs.readFileSync(dbPath, 'utf8');
return JSON.parse(data) || {};
} catch {
return {};
}
}
function writeDB(data) {
fs.writeFileSync(dbPath, JSON.stringify(data, null, 2));
}
function getGuildData(guildId) {
const db = readDB();
if (!db[guildId]) db[guildId] = {};
return db[guildId];
}
function setGuildData(guildId, newData) {
const db = readDB();
db[guildId] = {
...db[guildId],
...newData
};
writeDB(db);
}
module.exports = {
getGuildData,
setGuildData
};