Add files via upload
This commit is contained in:
38
commands/dev/reload.js
Normal file
38
commands/dev/reload.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
category: 'dev',
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('reload')
|
||||||
|
.setDescription('Reloads a command.')
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('command')
|
||||||
|
.setDescription('The command to reload.')
|
||||||
|
.setRequired(true)),
|
||||||
|
async execute(interaction) {
|
||||||
|
// Vérification côté bot pour être sûr
|
||||||
|
if (!interaction.member.permissions.has(PermissionFlagsBits.Administrator)) {
|
||||||
|
return interaction.reply({ content: '❌ Only administrators can use this command.', ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const commandName = interaction.options.getString('command', true).toLowerCase();
|
||||||
|
const command = interaction.client.commands.get(commandName);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
return interaction.reply({ content: `There is no command with name \`${commandName}\`!`, ephemeral: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
delete require.cache[require.resolve(`../${command.category}/${command.data.name}.js`)];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newCommand = require(`../${command.category}/${command.data.name}.js`);
|
||||||
|
interaction.client.commands.set(newCommand.data.name, newCommand);
|
||||||
|
await interaction.reply({ content: `✅ Command \`${newCommand.data.name}\` was reloaded!`, ephemeral: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
await interaction.reply({ content: `❌ Error while reloading \`${command.data.name}\`:\n\`${error.message}\``, ephemeral: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
95
commands/moderation/ban.js
Normal file
95
commands/moderation/ban.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
|
||||||
|
const db = require('../../functions/database/db.js'); // instance mysql2/promise
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
category: 'moderation',
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('ban')
|
||||||
|
.setDescription('Select a member and ban them.')
|
||||||
|
.addUserOption(option =>
|
||||||
|
option.setName('target')
|
||||||
|
.setDescription('The member to ban')
|
||||||
|
.setRequired(true))
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('reason')
|
||||||
|
.setDescription('Reason')
|
||||||
|
.setRequired(false))
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('duration')
|
||||||
|
.setDescription('Ban duration (ex: 1h, 2d, leave empty for permanent)')
|
||||||
|
.setRequired(false))
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
const target = interaction.options.getUser('target');
|
||||||
|
const reason = interaction.options.getString('reason') || 'No reason provided';
|
||||||
|
const durationInput = interaction.options.getString('duration');
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
|
if (!target) return interaction.editReply({ content: 'No user specified!' });
|
||||||
|
await interaction.guild.bans.create(target.id, { reason: `Banned by ${interaction.user.tag}: ${reason}` });
|
||||||
|
|
||||||
|
let type = 'Permanent';
|
||||||
|
let unbanDate = null;
|
||||||
|
|
||||||
|
if (durationInput) {
|
||||||
|
const regex = /^(\d+)([smhd])$/;
|
||||||
|
const match = durationInput.match(regex);
|
||||||
|
if (match) {
|
||||||
|
const value = parseInt(match[1]);
|
||||||
|
const unit = match[2];
|
||||||
|
let multiplier = 1000;
|
||||||
|
if (unit === 'm') multiplier *= 60;
|
||||||
|
if (unit === 'h') multiplier *= 60 * 60;
|
||||||
|
if (unit === 'd') multiplier *= 60 * 60 * 24;
|
||||||
|
unbanDate = Date.now() + value * multiplier;
|
||||||
|
type = 'Temporary';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Bannissement
|
||||||
|
interaction.guild.bans.create(target.id, { reason: `Banned by ${interaction.user.tag}: ${reason}` });
|
||||||
|
|
||||||
|
// Stockage dans MySQL (bans)
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO bans (userId, reason, modId, timestamp, type, unbanDate)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
ON DUPLICATE KEY UPDATE reason=VALUES(reason), modId=VALUES(modId), timestamp=VALUES(timestamp), type=VALUES(type), unbanDate=VALUES(unbanDate)`,
|
||||||
|
[target.id, reason, interaction.user.id, Date.now(), type, unbanDate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stockage dans logs (ajout guildId)
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[target.id, target.tag, interaction.user.id, interaction.user.tag, 'ban', reason, type, interaction.guild.id, Date.now()]
|
||||||
|
);
|
||||||
|
|
||||||
|
await interaction.editReply(`✅ Successfully banned ${target.tag} (${type})`);
|
||||||
|
|
||||||
|
// Embed log
|
||||||
|
const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'logs');
|
||||||
|
if (logChannel) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle('User Banned')
|
||||||
|
.setColor('Red')
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Banned User', value: `<@${target.id}> (${target.tag})`, inline: true },
|
||||||
|
{ name: 'Banned By', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
|
||||||
|
{ name: 'Reason', value: reason, inline: false },
|
||||||
|
{ name: 'Type', value: type, inline: true },
|
||||||
|
{ name: 'Date', value: new Date().toLocaleString(), inline: true },
|
||||||
|
{ name: 'Guild', value: interaction.guild.name, inline: true }
|
||||||
|
)
|
||||||
|
.setTimestamp();
|
||||||
|
logChannel.send({ embeds: [embed] });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await interaction.editReply({ content: `❌ Failed to ban ${target.tag}` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
58
commands/moderation/casier.js
Normal file
58
commands/moderation/casier.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
const db = require('../../functions/database/db.js'); // instance mysql2/promise
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
category: 'moderation',
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('casier')
|
||||||
|
.setDescription('Voir toutes les sanctions d’un membre.')
|
||||||
|
.addUserOption(option =>
|
||||||
|
option.setName('membre')
|
||||||
|
.setDescription('Le membre dont tu veux voir le casier')
|
||||||
|
.setRequired(true)),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
|
const member = interaction.options.getUser('membre');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [rows] = await db.query(
|
||||||
|
'SELECT * FROM logs WHERE userId = ? ORDER BY timestamp DESC',
|
||||||
|
[member.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!rows.length) return interaction.editReply(`❌ ${member.tag} n’a aucune sanction.`);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(`Casier de ${member.tag}`)
|
||||||
|
.setColor('Orange')
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
rows.forEach((row, index) => {
|
||||||
|
const date = new Date(row.timestamp).toLocaleString();
|
||||||
|
const actionCapitalized = row.action.charAt(0).toUpperCase() + row.action.slice(1); // Ban/Unban
|
||||||
|
|
||||||
|
// Récupère le nom du serveur via l'ID si présent
|
||||||
|
const guildName = row.guildId
|
||||||
|
? interaction.client.guilds.cache.get(row.guildId)?.name || 'Unknown Server'
|
||||||
|
: 'Unknown Server';
|
||||||
|
|
||||||
|
embed.addFields({
|
||||||
|
name: `#${index + 1} - ${actionCapitalized}`,
|
||||||
|
value: `**Modérateur:** <@${row.modId}> (${row.modTag || 'N/A'})\n` +
|
||||||
|
`**Raison:** ${row.reason || 'N/A'}\n` +
|
||||||
|
`**Serveur:** ${guildName}\n` +
|
||||||
|
`**Date:** ${date}`,
|
||||||
|
inline: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.editReply({ embeds: [embed] });
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await interaction.editReply('❌ Une erreur est survenue lors de la récupération du casier.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
65
commands/moderation/unban.js
Normal file
65
commands/moderation/unban.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||||
|
const db = require('../../functions/database/db.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
category: 'moderation',
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('unban')
|
||||||
|
.setDescription('Unban a user by their ID or tag.')
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('user')
|
||||||
|
.setDescription('The ID or tag (Username#1234) of the user to unban')
|
||||||
|
.setRequired(true))
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
|
const input = interaction.options.getString('user').trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bannedUsers = await interaction.guild.bans.fetch();
|
||||||
|
const bannedUser = bannedUsers.find(b =>
|
||||||
|
b.user.id === input || b.user.tag.toLowerCase() === input.toLowerCase()
|
||||||
|
);
|
||||||
|
if (!bannedUser) return interaction.editReply('❌ This user is not banned.');
|
||||||
|
|
||||||
|
// Récupération depuis MySQL
|
||||||
|
const [rows] = await db.query('SELECT * FROM bans WHERE userId = ?', [bannedUser.user.id]);
|
||||||
|
const banRecord = rows[0];
|
||||||
|
|
||||||
|
const reason = `Unbanned by ${interaction.user.tag}`;
|
||||||
|
await interaction.guild.members.unban(bannedUser.user.id, reason);
|
||||||
|
|
||||||
|
if (banRecord) await db.query('DELETE FROM bans WHERE userId = ?', [bannedUser.user.id]);
|
||||||
|
|
||||||
|
await interaction.editReply(`✅ Successfully unbanned ${bannedUser.user.tag}`);
|
||||||
|
|
||||||
|
const logChannel = interaction.guild.channels.cache.find(ch => ch.name === 'logs');
|
||||||
|
if (logChannel) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle('User Unbanned')
|
||||||
|
.setColor('Green')
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Membre Banni', value: `<@${bannedUser.user.id}> (${bannedUser.user.tag})`, inline: true },
|
||||||
|
{ name: 'Modérateur', value: `<@${interaction.user.id}> (${interaction.user.tag})`, inline: true },
|
||||||
|
{ name: 'Date', value: new Date().toLocaleString(), inline: true },
|
||||||
|
{ name: 'Guild', value: interaction.guild.name, inline: true }
|
||||||
|
)
|
||||||
|
.setTimestamp();
|
||||||
|
logChannel.send({ embeds: [embed] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log dans la DB avec guildId
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, guildId, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[bannedUser.user.id, bannedUser.user.tag, interaction.user.id, interaction.user.tag, 'unban', reason, null, interaction.guild.id, Date.now()]
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await interaction.editReply('❌ Failed to unban the user.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
23
commands/utility/ping.js
Normal file
23
commands/utility/ping.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
category: 'utility',
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('ping')
|
||||||
|
.setDescription('Replies with API latency and ICMP latency!'),
|
||||||
|
async execute(interaction) {
|
||||||
|
// --- API latency Discord ---
|
||||||
|
const apiLatency = Math.round(interaction.client.ws.ping);
|
||||||
|
|
||||||
|
// --- Reply final ---
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor('#00FFFF')
|
||||||
|
.setTitle('🏓 Ping Status')
|
||||||
|
.addFields(
|
||||||
|
{ name: '🌐 API Latency', value: `${apiLatency}ms`, inline: true }
|
||||||
|
)
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed] });
|
||||||
|
},
|
||||||
|
};
|
||||||
4
config.json
Normal file
4
config.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"token": "-",
|
||||||
|
"clientId": "1407164184373887038"
|
||||||
|
}
|
||||||
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 folders 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), // pas de guildId ici
|
||||||
|
{ 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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
50
eslint.config.js
Normal file
50
eslint.config.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
const js = require('@eslint/js');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'arrow-spacing': ['warn', { before: true, after: true }],
|
||||||
|
'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
|
||||||
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'comma-spacing': 'error',
|
||||||
|
'comma-style': 'error',
|
||||||
|
curly: ['error', 'multi-line', 'consistent'],
|
||||||
|
'dot-location': ['error', 'property'],
|
||||||
|
'handle-callback-err': 'off',
|
||||||
|
indent: ['error', 'tab'],
|
||||||
|
'keyword-spacing': 'error',
|
||||||
|
'max-nested-callbacks': ['error', { max: 4 }],
|
||||||
|
'max-statements-per-line': ['error', { max: 2 }],
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-empty-function': 'error',
|
||||||
|
'no-floating-decimal': 'error',
|
||||||
|
'no-inline-comments': 'error',
|
||||||
|
'no-lonely-if': 'error',
|
||||||
|
'no-multi-spaces': 'error',
|
||||||
|
'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1, maxBOF: 0 }],
|
||||||
|
'no-shadow': ['error', { allow: ['err', 'resolve', 'reject'] }],
|
||||||
|
'no-trailing-spaces': ['error'],
|
||||||
|
'no-var': 'error',
|
||||||
|
'no-undef': 'off',
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'prefer-const': 'error',
|
||||||
|
quotes: ['error', 'single'],
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'space-before-blocks': 'error',
|
||||||
|
'space-before-function-paren': ['error', {
|
||||||
|
anonymous: 'never',
|
||||||
|
named: 'never',
|
||||||
|
asyncArrow: 'always',
|
||||||
|
}],
|
||||||
|
'space-in-parens': 'error',
|
||||||
|
'space-infix-ops': 'error',
|
||||||
|
'space-unary-ops': 'error',
|
||||||
|
'spaced-comment': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
26
events/interactionCreate.js
Normal file
26
events/interactionCreate.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const { Events, MessageFlags } = 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!', flags: MessageFlags.Ephemeral });
|
||||||
|
} else {
|
||||||
|
await interaction.reply({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
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! Connectée en tant que ${client.user.tag}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
48
functions/database/db.js
Normal file
48
functions/database/db.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
|
||||||
|
|
||||||
|
const pool = mysql.createPool({
|
||||||
|
host: '192.168.1.6',
|
||||||
|
user: 'bot',
|
||||||
|
password: 'NxKr63LJB65pHv%t7E$JqgxKRsZMw%VIHEZAjq%^O0KYKjW#cRc^ebIH@%S9kaTh*GIg^D3ai4KBjMeXEh6xwv#9afQIR2$!2UB8C3ToXjnYFmzR%$lfpshnf8g@8229',
|
||||||
|
database: 'bot',
|
||||||
|
waitForConnections: true,
|
||||||
|
connectionLimit: 10,
|
||||||
|
queueLimit: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
async function initDB() {
|
||||||
|
const conn = await pool.getConnection();
|
||||||
|
try {
|
||||||
|
await conn.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS bans (
|
||||||
|
userId VARCHAR(32) PRIMARY KEY,
|
||||||
|
reason TEXT,
|
||||||
|
modId VARCHAR(32),
|
||||||
|
timestamp BIGINT,
|
||||||
|
type VARCHAR(20) NOT NULL,
|
||||||
|
unbanDate BIGINT
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
await conn.query(`
|
||||||
|
CREATE TABLE IF NOT EXISTS logs (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
userId VARCHAR(32),
|
||||||
|
userTag VARCHAR(100),
|
||||||
|
modId VARCHAR(32),
|
||||||
|
modTag VARCHAR(100),
|
||||||
|
action VARCHAR(20),
|
||||||
|
reason TEXT,
|
||||||
|
type ENUM('Permanent','Temporary') DEFAULT 'Permanent',
|
||||||
|
timestamp BIGINT
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
} finally {
|
||||||
|
conn.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initDB().catch(console.error);
|
||||||
|
|
||||||
|
module.exports = pool;
|
||||||
33
functions/moderation/autoUnban.js
Normal file
33
functions/moderation/autoUnban.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// functions/moderation/autoUnban.js
|
||||||
|
module.exports = (client, db) => {
|
||||||
|
setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const now = Date.now();
|
||||||
|
const [expiredBans] = await db.query(
|
||||||
|
'SELECT * FROM bans WHERE type = ? AND unbanDate <= ?',
|
||||||
|
['Temporary', now]
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const ban of expiredBans) {
|
||||||
|
for (const guild of client.guilds.cache.values()) {
|
||||||
|
try {
|
||||||
|
await guild.members.unban(ban.userId, 'Temporary ban expired');
|
||||||
|
console.log(`Auto-unbanned ${ban.userId} from guild ${guild.id}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to unban ${ban.userId} from guild ${guild.id}`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.query('DELETE FROM bans WHERE userId = ?', [ban.userId]);
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO logs (userId, userTag, modId, modTag, action, reason, type, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
[ban.userId, null, null, null, 'auto-unban', 'Temporary ban expired', null, Date.now()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error during auto-unban:', err);
|
||||||
|
}
|
||||||
|
}, 60_000);
|
||||||
|
};
|
||||||
43
index.js
Normal file
43
index.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const { Client, Collection, GatewayIntentBits } = require('discord.js');
|
||||||
|
const { token } = require('./config.json');
|
||||||
|
|
||||||
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||||
|
|
||||||
|
const db = require ('./functions/database/db.js');
|
||||||
|
const autoUnban = require('./functions/moderation/autoUnban.js');
|
||||||
|
autoUnban(client, db); // db = instance mysql2/promise
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
1508
package-lock.json
generated
Normal file
1508
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "femboy-france-bot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"discord.js": "^14.21.0",
|
||||||
|
"mysql2": "^3.14.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.33.0",
|
||||||
|
"eslint": "^9.33.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user