mirror of
https://github.com/arthur-pbty/LazyBot.git
synced 2026-06-03 15:07:29 +02:00
add anti raide & warn commands
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
const addCommand = require('../../fonctions/addCommand');
|
||||
const db = require('../../db');
|
||||
|
||||
module.exports = addCommand({
|
||||
name: 'clearwarns',
|
||||
description: 'Supprimer tous les avertissements d\'un utilisateur',
|
||||
aliases: ['resetwarns', 'warnreset', 'warnclear'],
|
||||
permissions: ['Administrator'],
|
||||
botOwnerOnly: false,
|
||||
dm: false,
|
||||
scope: 'global',
|
||||
slashOptions: [
|
||||
{ type: 'USER', name: 'utilisateur', description: 'L\'utilisateur dont vous voulez effacer les warns', required: true }
|
||||
],
|
||||
|
||||
executeSlash: async (client, interaction) => {
|
||||
const user = interaction.options.getUser('utilisateur');
|
||||
const guildId = interaction.guild.id;
|
||||
|
||||
try {
|
||||
// Compter les warns avant suppression
|
||||
const countResult = await db.getAsync(
|
||||
"SELECT COUNT(*) as count FROM warnings WHERE guild_id = ? AND user_id = ?",
|
||||
[guildId, user.id]
|
||||
);
|
||||
|
||||
if (countResult.count === 0) {
|
||||
return interaction.reply({
|
||||
content: `✅ **${user.tag}** n'a aucun avertissement.`,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
// Supprimer tous les warns
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"DELETE FROM warnings WHERE guild_id = ? AND user_id = ?",
|
||||
[guildId, user.id],
|
||||
function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x57F287)
|
||||
.setTitle('✅ Avertissements effacés')
|
||||
.setDescription(`Tous les avertissements de **${user.tag}** ont été supprimés.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${user}`, inline: true },
|
||||
{ name: '🗑️ Warns supprimés', value: `${countResult.count}`, inline: true },
|
||||
{ name: '👮 Par', value: `${interaction.user}`, inline: true }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur clearwarns:', err);
|
||||
return interaction.reply({
|
||||
content: '❌ Une erreur est survenue.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
executePrefix: async (client, message, args) => {
|
||||
if (args.length < 1) {
|
||||
return message.reply('❌ Usage: `!clearwarns <@utilisateur>`');
|
||||
}
|
||||
|
||||
const userId = args[0].replace(/[<@!>]/g, '');
|
||||
const user = await client.users.fetch(userId).catch(() => null);
|
||||
const guildId = message.guild.id;
|
||||
|
||||
if (!user) {
|
||||
return message.reply('❌ Utilisateur non trouvé.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Compter les warns avant suppression
|
||||
const countResult = await db.getAsync(
|
||||
"SELECT COUNT(*) as count FROM warnings WHERE guild_id = ? AND user_id = ?",
|
||||
[guildId, user.id]
|
||||
);
|
||||
|
||||
if (countResult.count === 0) {
|
||||
return message.reply(`✅ **${user.tag}** n'a aucun avertissement.`);
|
||||
}
|
||||
|
||||
// Supprimer tous les warns
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"DELETE FROM warnings WHERE guild_id = ? AND user_id = ?",
|
||||
[guildId, user.id],
|
||||
function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x57F287)
|
||||
.setTitle('✅ Avertissements effacés')
|
||||
.setDescription(`Tous les avertissements de **${user.tag}** ont été supprimés.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${user}`, inline: true },
|
||||
{ name: '🗑️ Warns supprimés', value: `${countResult.count}`, inline: true },
|
||||
{ name: '👮 Par', value: `${message.author}`, inline: true }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await message.reply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur clearwarns:', err);
|
||||
return message.reply('❌ Une erreur est survenue.');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
const addCommand = require('../../fonctions/addCommand');
|
||||
const db = require('../../db');
|
||||
|
||||
module.exports = addCommand({
|
||||
name: 'delwarn',
|
||||
description: 'Supprimer un avertissement',
|
||||
aliases: ['removewarn', 'unwarn', 'clearwarn'],
|
||||
permissions: ['ModerateMembers'],
|
||||
botOwnerOnly: false,
|
||||
dm: false,
|
||||
scope: 'global',
|
||||
slashOptions: [
|
||||
{ type: 'INTEGER', name: 'id', description: 'L\'ID de l\'avertissement à supprimer', required: true }
|
||||
],
|
||||
|
||||
executeSlash: async (client, interaction) => {
|
||||
const warnId = interaction.options.getInteger('id');
|
||||
const guildId = interaction.guild.id;
|
||||
|
||||
try {
|
||||
// Vérifier que le warn existe
|
||||
const warn = await db.getAsync(
|
||||
"SELECT * FROM warnings WHERE id = ? AND guild_id = ?",
|
||||
[warnId, guildId]
|
||||
);
|
||||
|
||||
if (!warn) {
|
||||
return interaction.reply({
|
||||
content: '❌ Avertissement non trouvé.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
// Supprimer le warn
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run("DELETE FROM warnings WHERE id = ?", [warnId], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const user = await client.users.fetch(warn.user_id).catch(() => null);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x57F287)
|
||||
.setTitle('✅ Avertissement supprimé')
|
||||
.addFields(
|
||||
{ name: '🆔 Warn ID', value: `#${warnId}`, inline: true },
|
||||
{ name: '👤 Utilisateur', value: user ? `${user.tag}` : warn.user_id, inline: true },
|
||||
{ name: '📝 Raison originale', value: warn.reason, inline: false }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur delwarn:', err);
|
||||
return interaction.reply({
|
||||
content: '❌ Une erreur est survenue.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
executePrefix: async (client, message, args) => {
|
||||
if (args.length < 1) {
|
||||
return message.reply('❌ Usage: `!delwarn <id>`');
|
||||
}
|
||||
|
||||
const warnId = parseInt(args[0]);
|
||||
const guildId = message.guild.id;
|
||||
|
||||
if (isNaN(warnId)) {
|
||||
return message.reply('❌ L\'ID doit être un nombre.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Vérifier que le warn existe
|
||||
const warn = await db.getAsync(
|
||||
"SELECT * FROM warnings WHERE id = ? AND guild_id = ?",
|
||||
[warnId, guildId]
|
||||
);
|
||||
|
||||
if (!warn) {
|
||||
return message.reply('❌ Avertissement non trouvé.');
|
||||
}
|
||||
|
||||
// Supprimer le warn
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run("DELETE FROM warnings WHERE id = ?", [warnId], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const user = await client.users.fetch(warn.user_id).catch(() => null);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x57F287)
|
||||
.setTitle('✅ Avertissement supprimé')
|
||||
.addFields(
|
||||
{ name: '🆔 Warn ID', value: `#${warnId}`, inline: true },
|
||||
{ name: '👤 Utilisateur', value: user ? `${user.tag}` : warn.user_id, inline: true },
|
||||
{ name: '📝 Raison originale', value: warn.reason, inline: false }
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await message.reply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur delwarn:', err);
|
||||
return message.reply('❌ Une erreur est survenue.');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,174 @@
|
||||
const { EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const addCommand = require('../../fonctions/addCommand');
|
||||
const db = require('../../db');
|
||||
const { checkWarningSanctions } = require('../../fonctions/antiraid');
|
||||
|
||||
module.exports = addCommand({
|
||||
name: 'warn',
|
||||
description: 'Avertir un utilisateur',
|
||||
aliases: ['avertir', 'avertissement'],
|
||||
permissions: ['ModerateMembers'],
|
||||
botOwnerOnly: false,
|
||||
dm: false,
|
||||
scope: 'global',
|
||||
slashOptions: [
|
||||
{ type: 'USER', name: 'utilisateur', description: 'L\'utilisateur à avertir', required: true },
|
||||
{ type: 'STRING', name: 'raison', description: 'La raison de l\'avertissement', required: true }
|
||||
],
|
||||
|
||||
executeSlash: async (client, interaction) => {
|
||||
const user = interaction.options.getUser('utilisateur');
|
||||
const reason = interaction.options.getString('raison');
|
||||
const moderator = interaction.user;
|
||||
const guildId = interaction.guild.id;
|
||||
|
||||
// Vérifier qu'on ne peut pas warn un admin/mod
|
||||
const member = await interaction.guild.members.fetch(user.id).catch(() => null);
|
||||
if (member && member.permissions.has(PermissionFlagsBits.ModerateMembers)) {
|
||||
return interaction.reply({
|
||||
content: '❌ Vous ne pouvez pas avertir un modérateur.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Ajouter le warn
|
||||
const warnId = await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"INSERT INTO warnings (guild_id, user_id, moderator_id, reason, source) VALUES (?, ?, ?, ?, ?)",
|
||||
[guildId, user.id, moderator.id, reason, 'manual'],
|
||||
function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve(this.lastID);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Compter les warns
|
||||
const countResult = await db.getAsync(
|
||||
"SELECT COUNT(*) as count FROM warnings WHERE guild_id = ? AND user_id = ?",
|
||||
[guildId, user.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xFFA500)
|
||||
.setTitle('⚠️ Avertissement')
|
||||
.setDescription(`**${user.tag}** a reçu un avertissement.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${user}`, inline: true },
|
||||
{ name: '👮 Modérateur', value: `${moderator}`, inline: true },
|
||||
{ name: '📊 Total warns', value: `${countResult.count}`, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false },
|
||||
{ name: '🆔 Warn ID', value: `#${warnId}`, inline: true }
|
||||
)
|
||||
.setThumbnail(user.displayAvatarURL({ size: 64 }))
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
|
||||
// Notifier l'utilisateur en MP
|
||||
try {
|
||||
const dmEmbed = new EmbedBuilder()
|
||||
.setColor(0xFFA500)
|
||||
.setTitle('⚠️ Vous avez reçu un avertissement')
|
||||
.setDescription(`Vous avez été averti sur **${interaction.guild.name}**.`)
|
||||
.addFields(
|
||||
{ name: '📝 Raison', value: reason },
|
||||
{ name: '📊 Total avertissements', value: `${countResult.count}` }
|
||||
)
|
||||
.setTimestamp();
|
||||
await user.send({ embeds: [dmEmbed] });
|
||||
} catch {}
|
||||
|
||||
// Vérifier les sanctions automatiques
|
||||
await checkWarningSanctions(guildId, user.id, client);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur warn:', err);
|
||||
return interaction.reply({
|
||||
content: '❌ Une erreur est survenue.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
executePrefix: async (client, message, args) => {
|
||||
if (args.length < 2) {
|
||||
return message.reply('❌ Usage: `!warn <@utilisateur> <raison>`');
|
||||
}
|
||||
|
||||
const userMention = args[0];
|
||||
const userId = userMention.replace(/[<@!>]/g, '');
|
||||
const reason = args.slice(1).join(' ');
|
||||
const moderator = message.author;
|
||||
const guildId = message.guild.id;
|
||||
|
||||
const user = await client.users.fetch(userId).catch(() => null);
|
||||
if (!user) {
|
||||
return message.reply('❌ Utilisateur non trouvé.');
|
||||
}
|
||||
|
||||
// Vérifier qu'on ne peut pas warn un admin/mod
|
||||
const member = await message.guild.members.fetch(user.id).catch(() => null);
|
||||
if (member && member.permissions.has(PermissionFlagsBits.ModerateMembers)) {
|
||||
return message.reply('❌ Vous ne pouvez pas avertir un modérateur.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Ajouter le warn
|
||||
const warnId = await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"INSERT INTO warnings (guild_id, user_id, moderator_id, reason, source) VALUES (?, ?, ?, ?, ?)",
|
||||
[guildId, user.id, moderator.id, reason, 'manual'],
|
||||
function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve(this.lastID);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Compter les warns
|
||||
const countResult = await db.getAsync(
|
||||
"SELECT COUNT(*) as count FROM warnings WHERE guild_id = ? AND user_id = ?",
|
||||
[guildId, user.id]
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xFFA500)
|
||||
.setTitle('⚠️ Avertissement')
|
||||
.setDescription(`**${user.tag}** a reçu un avertissement.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${user}`, inline: true },
|
||||
{ name: '👮 Modérateur', value: `${moderator}`, inline: true },
|
||||
{ name: '📊 Total warns', value: `${countResult.count}`, inline: true },
|
||||
{ name: '📝 Raison', value: reason, inline: false },
|
||||
{ name: '🆔 Warn ID', value: `#${warnId}`, inline: true }
|
||||
)
|
||||
.setThumbnail(user.displayAvatarURL({ size: 64 }))
|
||||
.setTimestamp();
|
||||
|
||||
await message.reply({ embeds: [embed] });
|
||||
|
||||
// Notifier l'utilisateur en MP
|
||||
try {
|
||||
const dmEmbed = new EmbedBuilder()
|
||||
.setColor(0xFFA500)
|
||||
.setTitle('⚠️ Vous avez reçu un avertissement')
|
||||
.setDescription(`Vous avez été averti sur **${message.guild.name}**.`)
|
||||
.addFields(
|
||||
{ name: '📝 Raison', value: reason },
|
||||
{ name: '📊 Total avertissements', value: `${countResult.count}` }
|
||||
)
|
||||
.setTimestamp();
|
||||
await user.send({ embeds: [dmEmbed] });
|
||||
} catch {}
|
||||
|
||||
// Vérifier les sanctions automatiques
|
||||
await checkWarningSanctions(guildId, user.id, client);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur warn:', err);
|
||||
return message.reply('❌ Une erreur est survenue.');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,144 @@
|
||||
const { EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const addCommand = require('../../fonctions/addCommand');
|
||||
const db = require('../../db');
|
||||
|
||||
module.exports = addCommand({
|
||||
name: 'warnings',
|
||||
description: 'Voir les avertissements d\'un utilisateur',
|
||||
aliases: ['warns', 'warninfo', 'warnlist', 'infractions'],
|
||||
permissions: [],
|
||||
botOwnerOnly: false,
|
||||
dm: false,
|
||||
scope: 'global',
|
||||
slashOptions: [
|
||||
{ type: 'USER', name: 'utilisateur', description: 'L\'utilisateur dont vous voulez voir les warns (par défaut: vous)', required: false }
|
||||
],
|
||||
|
||||
executeSlash: async (client, interaction) => {
|
||||
const targetUser = interaction.options.getUser('utilisateur') || interaction.user;
|
||||
const guildId = interaction.guild.id;
|
||||
|
||||
// Si l'utilisateur veut voir les warns d'un autre, il doit être modo
|
||||
if (targetUser.id !== interaction.user.id) {
|
||||
const member = interaction.member;
|
||||
if (!member.permissions.has(PermissionFlagsBits.ModerateMembers)) {
|
||||
return interaction.reply({
|
||||
content: '❌ Vous ne pouvez voir que vos propres avertissements.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const warnings = await db.allAsync(
|
||||
"SELECT * FROM warnings WHERE guild_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 25",
|
||||
[guildId, targetUser.id]
|
||||
);
|
||||
|
||||
if (!warnings || warnings.length === 0) {
|
||||
return interaction.reply({
|
||||
content: `✅ **${targetUser.tag}** n'a aucun avertissement sur ce serveur.`,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xFFA500)
|
||||
.setTitle(`⚠️ Avertissements de ${targetUser.tag}`)
|
||||
.setThumbnail(targetUser.displayAvatarURL({ size: 64 }))
|
||||
.setDescription(`Total: **${warnings.length}** avertissement(s)`)
|
||||
.setTimestamp();
|
||||
|
||||
// Ajouter les 10 derniers warns
|
||||
const recentWarns = warnings.slice(0, 10);
|
||||
for (const warn of recentWarns) {
|
||||
const moderator = await client.users.fetch(warn.moderator_id).catch(() => null);
|
||||
const date = new Date(warn.created_at * 1000).toLocaleDateString('fr-FR');
|
||||
const source = warn.source === 'manual' ? '👮 Manuel' : '🤖 Auto';
|
||||
|
||||
embed.addFields({
|
||||
name: `#${warn.id} - ${date}`,
|
||||
value: `**Raison:** ${warn.reason}\n**Par:** ${moderator ? moderator.tag : 'Inconnu'} (${source})`,
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
if (warnings.length > 10) {
|
||||
embed.setFooter({ text: `Affichage des 10 derniers sur ${warnings.length} avertissements` });
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [embed], ephemeral: targetUser.id === interaction.user.id });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur warnings:', err);
|
||||
return interaction.reply({
|
||||
content: '❌ Une erreur est survenue.',
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
executePrefix: async (client, message, args) => {
|
||||
let targetUser;
|
||||
|
||||
if (args.length > 0) {
|
||||
const userId = args[0].replace(/[<@!>]/g, '');
|
||||
targetUser = await client.users.fetch(userId).catch(() => null);
|
||||
|
||||
// Si l'utilisateur veut voir les warns d'un autre, il doit être modo
|
||||
if (targetUser && targetUser.id !== message.author.id) {
|
||||
if (!message.member.permissions.has(PermissionFlagsBits.ModerateMembers)) {
|
||||
return message.reply('❌ Vous ne pouvez voir que vos propres avertissements.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetUser) {
|
||||
targetUser = message.author;
|
||||
}
|
||||
|
||||
const guildId = message.guild.id;
|
||||
|
||||
try {
|
||||
const warnings = await db.allAsync(
|
||||
"SELECT * FROM warnings WHERE guild_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 25",
|
||||
[guildId, targetUser.id]
|
||||
);
|
||||
|
||||
if (!warnings || warnings.length === 0) {
|
||||
return message.reply(`✅ **${targetUser.tag}** n'a aucun avertissement sur ce serveur.`);
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xFFA500)
|
||||
.setTitle(`⚠️ Avertissements de ${targetUser.tag}`)
|
||||
.setThumbnail(targetUser.displayAvatarURL({ size: 64 }))
|
||||
.setDescription(`Total: **${warnings.length}** avertissement(s)`)
|
||||
.setTimestamp();
|
||||
|
||||
// Ajouter les 10 derniers warns
|
||||
const recentWarns = warnings.slice(0, 10);
|
||||
for (const warn of recentWarns) {
|
||||
const moderator = await client.users.fetch(warn.moderator_id).catch(() => null);
|
||||
const date = new Date(warn.created_at * 1000).toLocaleDateString('fr-FR');
|
||||
const source = warn.source === 'manual' ? '👮 Manuel' : '🤖 Auto';
|
||||
|
||||
embed.addFields({
|
||||
name: `#${warn.id} - ${date}`,
|
||||
value: `**Raison:** ${warn.reason}\n**Par:** ${moderator ? moderator.tag : 'Inconnu'} (${source})`,
|
||||
inline: false
|
||||
});
|
||||
}
|
||||
|
||||
if (warnings.length > 10) {
|
||||
embed.setFooter({ text: `Affichage des 10 derniers sur ${warnings.length} avertissements` });
|
||||
}
|
||||
|
||||
await message.reply({ embeds: [embed] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur warnings:', err);
|
||||
return message.reply('❌ Une erreur est survenue.');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -256,6 +256,138 @@ db.exec(`
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_nickname_history_user ON nickname_history(guild_id, user_id, changed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS antiraid_config (
|
||||
guild_id TEXT PRIMARY KEY,
|
||||
enabled INTEGER NOT NULL DEFAULT 0,
|
||||
log_channel_id TEXT,
|
||||
|
||||
-- Anti-link
|
||||
antilink_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antilink_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antilink_whitelist_domains TEXT DEFAULT '[]',
|
||||
antilink_exclude_channels TEXT DEFAULT '[]',
|
||||
antilink_exclude_roles TEXT DEFAULT '[]',
|
||||
antilink_warn_message TEXT DEFAULT '⚠️ Les liens ne sont pas autorisés ici.',
|
||||
|
||||
-- Anti-invite (liens Discord)
|
||||
antiinvite_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antiinvite_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antiinvite_allow_own_server INTEGER NOT NULL DEFAULT 1,
|
||||
antiinvite_exclude_channels TEXT DEFAULT '[]',
|
||||
antiinvite_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-spam
|
||||
antispam_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antispam_action TEXT NOT NULL DEFAULT 'mute',
|
||||
antispam_max_messages INTEGER NOT NULL DEFAULT 5,
|
||||
antispam_interval_seconds INTEGER NOT NULL DEFAULT 5,
|
||||
antispam_mute_duration_minutes INTEGER NOT NULL DEFAULT 10,
|
||||
antispam_exclude_channels TEXT DEFAULT '[]',
|
||||
antispam_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-duplicate (messages identiques)
|
||||
antidupe_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antidupe_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antidupe_max_duplicates INTEGER NOT NULL DEFAULT 3,
|
||||
antidupe_interval_seconds INTEGER NOT NULL DEFAULT 60,
|
||||
antidupe_exclude_channels TEXT DEFAULT '[]',
|
||||
antidupe_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-mass mention
|
||||
antimention_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antimention_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antimention_max_mentions INTEGER NOT NULL DEFAULT 5,
|
||||
antimention_exclude_channels TEXT DEFAULT '[]',
|
||||
antimention_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-mass emoji
|
||||
antiemoji_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antiemoji_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antiemoji_max_emojis INTEGER NOT NULL DEFAULT 10,
|
||||
antiemoji_exclude_channels TEXT DEFAULT '[]',
|
||||
antiemoji_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-caps (majuscules)
|
||||
anticaps_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
anticaps_action TEXT NOT NULL DEFAULT 'delete',
|
||||
anticaps_max_percent INTEGER NOT NULL DEFAULT 70,
|
||||
anticaps_min_length INTEGER NOT NULL DEFAULT 10,
|
||||
anticaps_exclude_channels TEXT DEFAULT '[]',
|
||||
anticaps_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-bot join
|
||||
antibot_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antibot_action TEXT NOT NULL DEFAULT 'kick',
|
||||
antibot_min_account_age_days INTEGER NOT NULL DEFAULT 7,
|
||||
antibot_no_avatar_action INTEGER NOT NULL DEFAULT 0,
|
||||
antibot_suspicious_name_action INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
-- Anti-mass join (raid de comptes)
|
||||
antimassj_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antimassj_action TEXT NOT NULL DEFAULT 'kick',
|
||||
antimassj_max_joins INTEGER NOT NULL DEFAULT 10,
|
||||
antimassj_interval_seconds INTEGER NOT NULL DEFAULT 10,
|
||||
antimassj_lockdown_duration_minutes INTEGER NOT NULL DEFAULT 5,
|
||||
|
||||
-- Anti-newline spam
|
||||
antinewline_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antinewline_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antinewline_max_lines INTEGER NOT NULL DEFAULT 15,
|
||||
antinewline_exclude_channels TEXT DEFAULT '[]',
|
||||
antinewline_exclude_roles TEXT DEFAULT '[]',
|
||||
|
||||
-- Anti-badwords (gros mots)
|
||||
antibadwords_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
antibadwords_action TEXT NOT NULL DEFAULT 'delete',
|
||||
antibadwords_words TEXT DEFAULT '[]',
|
||||
antibadwords_exclude_channels TEXT DEFAULT '[]',
|
||||
antibadwords_exclude_roles TEXT DEFAULT '[]',
|
||||
antibadwords_warn_message TEXT DEFAULT '⚠️ Les insultes et gros mots ne sont pas autorisés.'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS antiraid_warnings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
warning_type TEXT NOT NULL,
|
||||
warned_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_antiraid_warnings ON antiraid_warnings(guild_id, user_id, warning_type, warned_at);
|
||||
|
||||
-- Table des warns utilisateurs
|
||||
CREATE TABLE IF NOT EXISTS warnings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
moderator_id TEXT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
source TEXT DEFAULT 'manual',
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_warnings ON warnings(guild_id, user_id, created_at DESC);
|
||||
|
||||
-- Configuration des sanctions automatiques par warns
|
||||
CREATE TABLE IF NOT EXISTS warnings_config (
|
||||
guild_id TEXT PRIMARY KEY,
|
||||
enabled INTEGER NOT NULL DEFAULT 0,
|
||||
warn1_action TEXT DEFAULT 'none',
|
||||
warn1_duration INTEGER DEFAULT 10,
|
||||
warn2_action TEXT DEFAULT 'none',
|
||||
warn2_duration INTEGER DEFAULT 30,
|
||||
warn3_action TEXT DEFAULT 'mute',
|
||||
warn3_duration INTEGER DEFAULT 60,
|
||||
warn4_action TEXT DEFAULT 'kick',
|
||||
warn4_duration INTEGER DEFAULT 0,
|
||||
warn5_action TEXT DEFAULT 'ban',
|
||||
warn5_duration INTEGER DEFAULT 0,
|
||||
decay_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
decay_days INTEGER DEFAULT 30,
|
||||
notify_user INTEGER NOT NULL DEFAULT 1,
|
||||
notify_channel_id TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scheduled_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
guild_id TEXT NOT NULL,
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
const { Events, EmbedBuilder } = require("discord.js");
|
||||
const db = require("../db");
|
||||
const { sendLog } = require("../fonctions/sendLog");
|
||||
const antiraid = require("../fonctions/antiraid");
|
||||
|
||||
module.exports = {
|
||||
name: Events.GuildMemberAdd,
|
||||
async execute(client, member) {
|
||||
// ===== ANTI-RAID CHECKS =====
|
||||
await antiraid.checkMemberJoin(member, client);
|
||||
|
||||
// ===== LOG MEMBRE REJOINT =====
|
||||
const accountAge = Math.floor((Date.now() - member.user.createdTimestamp) / (1000 * 60 * 60 * 24));
|
||||
const accountAgeStr = accountAge < 1 ? 'Moins d\'un jour' :
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
const { Events, EmbedBuilder } = require("discord.js");
|
||||
const db = require("../db");
|
||||
const antiraid = require("../fonctions/antiraid");
|
||||
|
||||
module.exports = {
|
||||
name: Events.MessageCreate,
|
||||
async execute(client, message) {
|
||||
if (message.author.bot) return;
|
||||
if (!message.guild) return;
|
||||
|
||||
// ===== ANTI-RAID CHECKS =====
|
||||
await antiraid.checkMessage(message, client);
|
||||
|
||||
// Si le message a été supprimé par l'antiraid, ne pas continuer
|
||||
try {
|
||||
await message.fetch();
|
||||
} catch {
|
||||
return; // Message supprimé
|
||||
}
|
||||
|
||||
const guildId = message.guild.id;
|
||||
const userId = message.author.id;
|
||||
|
||||
@@ -0,0 +1,657 @@
|
||||
const { EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
const db = require('../db');
|
||||
|
||||
// Cache pour le tracking
|
||||
const spamTracker = new Map(); // guildId_oderId -> [timestamps]
|
||||
const dupeTracker = new Map(); // guildId_oderId -> [{content, timestamp}]
|
||||
const joinTracker = new Map(); // guildId -> [timestamps]
|
||||
const warningTracker = new Map(); // guildId_oderId_type -> count
|
||||
|
||||
/**
|
||||
* Récupère la config anti-raid d'un serveur
|
||||
*/
|
||||
async function getConfig(guildId) {
|
||||
try {
|
||||
return await db.getAsync("SELECT * FROM antiraid_config WHERE guild_id = ?", [guildId]);
|
||||
} catch (err) {
|
||||
console.error('Erreur récupération config antiraid:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un membre est exclu (rôle ou salon)
|
||||
*/
|
||||
function isExcluded(member, channelId, excludeChannels, excludeRoles) {
|
||||
try {
|
||||
const channels = JSON.parse(excludeChannels || '[]');
|
||||
const roles = JSON.parse(excludeRoles || '[]');
|
||||
|
||||
if (channels.includes(channelId)) return true;
|
||||
if (member && roles.some(roleId => member.roles.cache.has(roleId))) return true;
|
||||
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un log dans le salon de logs anti-raid
|
||||
*/
|
||||
async function sendLog(client, guildId, config, embed) {
|
||||
if (!config.log_channel_id) return;
|
||||
|
||||
try {
|
||||
const guild = client.guilds.cache.get(guildId);
|
||||
const channel = guild?.channels.cache.get(config.log_channel_id);
|
||||
if (channel) {
|
||||
await channel.send({ embeds: [embed] });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur envoi log antiraid:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applique une action sur un membre
|
||||
*/
|
||||
async function applyAction(member, action, reason, duration = 10) {
|
||||
try {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
// Juste supprimer le message, pas d'action sur le membre
|
||||
return 'deleted';
|
||||
case 'warn':
|
||||
// Avertissement simple
|
||||
return 'warned';
|
||||
case 'mute':
|
||||
case 'timeout':
|
||||
await member.timeout(duration * 60 * 1000, reason);
|
||||
return 'muted';
|
||||
case 'kick':
|
||||
await member.kick(reason);
|
||||
return 'kicked';
|
||||
case 'ban':
|
||||
await member.ban({ reason, deleteMessageSeconds: 86400 });
|
||||
return 'banned';
|
||||
default:
|
||||
return 'none';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur application action antiraid:', err);
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un embed de log
|
||||
*/
|
||||
function createLogEmbed(type, user, reason, action, details = {}) {
|
||||
const colors = {
|
||||
link: 0x3498DB,
|
||||
invite: 0x9B59B6,
|
||||
spam: 0xE74C3C,
|
||||
duplicate: 0xE67E22,
|
||||
mention: 0xF1C40F,
|
||||
emoji: 0x1ABC9C,
|
||||
caps: 0x95A5A6,
|
||||
newline: 0x2ECC71,
|
||||
bot: 0xE91E63,
|
||||
massjoin: 0x9B59B6,
|
||||
badwords: 0xE74C3C
|
||||
};
|
||||
|
||||
const titles = {
|
||||
link: '🔗 Anti-Link',
|
||||
invite: '📨 Anti-Invite',
|
||||
spam: '⚡ Anti-Spam',
|
||||
duplicate: '📋 Anti-Duplicate',
|
||||
mention: '📢 Anti-Mention',
|
||||
emoji: '😀 Anti-Emoji',
|
||||
caps: '🔠 Anti-Caps',
|
||||
newline: '📄 Anti-Newline',
|
||||
bot: '🤖 Anti-Bot',
|
||||
massjoin: '👥 Anti-Mass Join',
|
||||
badwords: '🤬 Anti-Gros Mots'
|
||||
};
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(colors[type] || 0xED4245)
|
||||
.setTitle(titles[type] || '🛡️ Anti-Raid')
|
||||
.setDescription(reason)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${user} (${user.tag || user.username})`, inline: true },
|
||||
{ name: '⚡ Action', value: action, inline: true }
|
||||
)
|
||||
.setThumbnail(user.displayAvatarURL?.({ size: 64 }) || null)
|
||||
.setTimestamp();
|
||||
|
||||
if (details.content) {
|
||||
embed.addFields({ name: '💬 Contenu', value: details.content.substring(0, 1024), inline: false });
|
||||
}
|
||||
if (details.channel) {
|
||||
embed.addFields({ name: '📁 Salon', value: `<#${details.channel}>`, inline: true });
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
// ===== ANTI-LINK =====
|
||||
async function checkAntiLink(message, config) {
|
||||
if (!config.antilink_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antilink_exclude_channels, config.antilink_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
|
||||
const urlRegex = /https?:\/\/[^\s]+/gi;
|
||||
const links = message.content.match(urlRegex);
|
||||
if (!links) return false;
|
||||
|
||||
const whitelist = JSON.parse(config.antilink_whitelist_domains || '[]');
|
||||
const hasBlockedLink = links.some(link => {
|
||||
try {
|
||||
const url = new URL(link);
|
||||
return !whitelist.some(domain => url.hostname.includes(domain));
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasBlockedLink) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
if (config.antilink_warn_message) {
|
||||
const warnMsg = await message.channel.send(`${message.author} ${config.antilink_warn_message}`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
}
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antilink_action, 'Anti-Link');
|
||||
return { type: 'link', action: actionResult, content: message.content };
|
||||
}
|
||||
|
||||
// ===== ANTI-INVITE =====
|
||||
async function checkAntiInvite(message, config, client) {
|
||||
if (!config.antiinvite_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antiinvite_exclude_channels, config.antiinvite_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageGuild)) return false;
|
||||
|
||||
const inviteRegex = /(discord\.(gg|io|me|li)|discordapp\.com\/invite|discord\.com\/invite)\/[a-zA-Z0-9]+/gi;
|
||||
const invites = message.content.match(inviteRegex);
|
||||
if (!invites) return false;
|
||||
|
||||
// Vérifier si c'est une invite de ce serveur
|
||||
if (config.antiinvite_allow_own_server) {
|
||||
try {
|
||||
const guildInvites = await message.guild.invites.fetch();
|
||||
const ownInviteCodes = guildInvites.map(i => i.code);
|
||||
const hasExternalInvite = invites.some(invite => {
|
||||
const code = invite.split('/').pop();
|
||||
return !ownInviteCodes.includes(code);
|
||||
});
|
||||
if (!hasExternalInvite) return false;
|
||||
} catch {
|
||||
// Si on ne peut pas fetch les invites, bloquer par défaut
|
||||
}
|
||||
}
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
const warnMsg = await message.channel.send(`${message.author} ⚠️ Les invitations Discord ne sont pas autorisées.`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antiinvite_action, 'Anti-Invite');
|
||||
return { type: 'invite', action: actionResult, content: message.content };
|
||||
}
|
||||
|
||||
// ===== ANTI-SPAM =====
|
||||
async function checkAntiSpam(message, config) {
|
||||
if (!config.antispam_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antispam_exclude_channels, config.antispam_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
|
||||
const key = `${message.guild.id}_${message.author.id}`;
|
||||
const now = Date.now();
|
||||
const interval = config.antispam_interval_seconds * 1000;
|
||||
|
||||
if (!spamTracker.has(key)) {
|
||||
spamTracker.set(key, []);
|
||||
}
|
||||
|
||||
const timestamps = spamTracker.get(key).filter(t => now - t < interval);
|
||||
timestamps.push(now);
|
||||
spamTracker.set(key, timestamps);
|
||||
|
||||
if (timestamps.length < config.antispam_max_messages) return false;
|
||||
|
||||
// Spam détecté
|
||||
spamTracker.delete(key);
|
||||
|
||||
// Supprimer les messages récents de l'utilisateur
|
||||
try {
|
||||
const messages = await message.channel.messages.fetch({ limit: 20 });
|
||||
const userMessages = messages.filter(m => m.author.id === message.author.id && now - m.createdTimestamp < interval);
|
||||
await message.channel.bulkDelete(userMessages).catch(() => {});
|
||||
} catch {}
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antispam_action, 'Anti-Spam', config.antispam_mute_duration_minutes);
|
||||
return { type: 'spam', action: actionResult, content: `${timestamps.length} messages en ${config.antispam_interval_seconds}s` };
|
||||
}
|
||||
|
||||
// ===== ANTI-DUPLICATE =====
|
||||
async function checkAntiDuplicate(message, config) {
|
||||
if (!config.antidupe_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antidupe_exclude_channels, config.antidupe_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
if (message.content.length < 5) return false;
|
||||
|
||||
const key = `${message.guild.id}_${message.author.id}`;
|
||||
const now = Date.now();
|
||||
const interval = config.antidupe_interval_seconds * 1000;
|
||||
|
||||
if (!dupeTracker.has(key)) {
|
||||
dupeTracker.set(key, []);
|
||||
}
|
||||
|
||||
const history = dupeTracker.get(key).filter(h => now - h.timestamp < interval);
|
||||
const duplicates = history.filter(h => h.content === message.content).length;
|
||||
|
||||
history.push({ content: message.content, timestamp: now });
|
||||
dupeTracker.set(key, history.slice(-20));
|
||||
|
||||
if (duplicates < config.antidupe_max_duplicates - 1) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
const warnMsg = await message.channel.send(`${message.author} ⚠️ Arrêtez de répéter le même message.`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antidupe_action, 'Anti-Duplicate');
|
||||
return { type: 'duplicate', action: actionResult, content: message.content };
|
||||
}
|
||||
|
||||
// ===== ANTI-MASS MENTION =====
|
||||
async function checkAntiMention(message, config) {
|
||||
if (!config.antimention_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antimention_exclude_channels, config.antimention_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.MentionEveryone)) return false;
|
||||
|
||||
const mentionCount = message.mentions.users.size + message.mentions.roles.size;
|
||||
if (mentionCount < config.antimention_max_mentions) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
const warnMsg = await message.channel.send(`${message.author} ⚠️ Trop de mentions dans votre message.`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antimention_action, 'Anti-Mass Mention');
|
||||
return { type: 'mention', action: actionResult, content: `${mentionCount} mentions` };
|
||||
}
|
||||
|
||||
// ===== ANTI-EMOJI =====
|
||||
async function checkAntiEmoji(message, config) {
|
||||
if (!config.antiemoji_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antiemoji_exclude_channels, config.antiemoji_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
|
||||
const emojiRegex = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]|<a?:\w+:\d+>)/g;
|
||||
const emojis = message.content.match(emojiRegex);
|
||||
if (!emojis || emojis.length < config.antiemoji_max_emojis) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
const warnMsg = await message.channel.send(`${message.author} ⚠️ Trop d'emojis dans votre message.`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antiemoji_action, 'Anti-Emoji');
|
||||
return { type: 'emoji', action: actionResult, content: `${emojis.length} emojis` };
|
||||
}
|
||||
|
||||
// ===== ANTI-CAPS =====
|
||||
async function checkAntiCaps(message, config) {
|
||||
if (!config.anticaps_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.anticaps_exclude_channels, config.anticaps_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
|
||||
const text = message.content.replace(/[^a-zA-Z]/g, '');
|
||||
if (text.length < config.anticaps_min_length) return false;
|
||||
|
||||
const capsCount = (text.match(/[A-Z]/g) || []).length;
|
||||
const capsPercent = (capsCount / text.length) * 100;
|
||||
|
||||
if (capsPercent < config.anticaps_max_percent) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
const warnMsg = await message.channel.send(`${message.author} ⚠️ Trop de majuscules dans votre message.`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
|
||||
const actionResult = await applyAction(message.member, config.anticaps_action, 'Anti-Caps');
|
||||
return { type: 'caps', action: actionResult, content: `${Math.round(capsPercent)}% majuscules` };
|
||||
}
|
||||
|
||||
// ===== ANTI-NEWLINE =====
|
||||
async function checkAntiNewline(message, config) {
|
||||
if (!config.antinewline_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antinewline_exclude_channels, config.antinewline_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
|
||||
const lines = message.content.split('\n').length;
|
||||
if (lines < config.antinewline_max_lines) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
const warnMsg = await message.channel.send(`${message.author} ⚠️ Trop de lignes dans votre message.`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antinewline_action, 'Anti-Newline');
|
||||
return { type: 'newline', action: actionResult, content: `${lines} lignes` };
|
||||
}
|
||||
|
||||
// ===== ANTI-BADWORDS =====
|
||||
async function checkAntiBadwords(message, config) {
|
||||
if (!config.antibadwords_enabled) return false;
|
||||
if (isExcluded(message.member, message.channel.id, config.antibadwords_exclude_channels, config.antibadwords_exclude_roles)) return false;
|
||||
if (message.member?.permissions.has(PermissionFlagsBits.ManageMessages)) return false;
|
||||
|
||||
let badwordsList = [];
|
||||
try {
|
||||
badwordsList = JSON.parse(config.antibadwords_words || '[]');
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (badwordsList.length === 0) return false;
|
||||
|
||||
// Normaliser le message (enlever accents, mettre en minuscules)
|
||||
const normalizedContent = message.content
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/[^a-z0-9\s]/g, ' ');
|
||||
|
||||
// Vérifier chaque gros mot
|
||||
const foundBadword = badwordsList.find(word => {
|
||||
const normalizedWord = word.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
// Vérifier le mot entier ou avec des variations
|
||||
const regex = new RegExp(`\\b${normalizedWord}\\b|${normalizedWord.split('').join('[^a-z]*')}`, 'i');
|
||||
return regex.test(normalizedContent);
|
||||
});
|
||||
|
||||
if (!foundBadword) return false;
|
||||
|
||||
await message.delete().catch(() => {});
|
||||
if (config.antibadwords_warn_message) {
|
||||
const warnMsg = await message.channel.send(`${message.author} ${config.antibadwords_warn_message}`);
|
||||
setTimeout(() => warnMsg.delete().catch(() => {}), 5000);
|
||||
}
|
||||
|
||||
const actionResult = await applyAction(message.member, config.antibadwords_action, 'Anti-Gros Mots');
|
||||
return { type: 'badwords', action: actionResult, content: '[Contenu censuré]' };
|
||||
}
|
||||
|
||||
// ===== ANTI-BOT JOIN =====
|
||||
async function checkAntiBot(member, config) {
|
||||
if (!config.antibot_enabled) return false;
|
||||
|
||||
const now = Date.now();
|
||||
const accountAge = Math.floor((now - member.user.createdTimestamp) / (1000 * 60 * 60 * 24));
|
||||
const reasons = [];
|
||||
|
||||
// Compte trop récent
|
||||
if (accountAge < config.antibot_min_account_age_days) {
|
||||
reasons.push(`Compte créé il y a ${accountAge} jours (min: ${config.antibot_min_account_age_days})`);
|
||||
}
|
||||
|
||||
// Pas d'avatar
|
||||
if (config.antibot_no_avatar_action && !member.user.avatar) {
|
||||
reasons.push('Pas d\'avatar');
|
||||
}
|
||||
|
||||
// Nom suspect (caractères bizarres, pattern de bot)
|
||||
if (config.antibot_suspicious_name_action) {
|
||||
const suspiciousPattern = /^[a-z]{4,8}\d{4}$/i; // Pattern comme "user1234"
|
||||
const weirdChars = /[\u200B-\u200D\uFEFF]/; // Caractères invisibles
|
||||
if (suspiciousPattern.test(member.user.username) || weirdChars.test(member.user.username)) {
|
||||
reasons.push('Nom suspect');
|
||||
}
|
||||
}
|
||||
|
||||
if (reasons.length === 0) return false;
|
||||
|
||||
const actionResult = await applyAction(member, config.antibot_action, 'Anti-Bot: ' + reasons.join(', '));
|
||||
return { type: 'bot', action: actionResult, reasons };
|
||||
}
|
||||
|
||||
// ===== ANTI-MASS JOIN =====
|
||||
async function checkAntiMassJoin(member, config) {
|
||||
if (!config.antimassj_enabled) return false;
|
||||
|
||||
const guildId = member.guild.id;
|
||||
const now = Date.now();
|
||||
const interval = config.antimassj_interval_seconds * 1000;
|
||||
|
||||
if (!joinTracker.has(guildId)) {
|
||||
joinTracker.set(guildId, []);
|
||||
}
|
||||
|
||||
const joins = joinTracker.get(guildId).filter(t => now - t < interval);
|
||||
joins.push(now);
|
||||
joinTracker.set(guildId, joins);
|
||||
|
||||
if (joins.length < config.antimassj_max_joins) return false;
|
||||
|
||||
// Raid détecté - appliquer l'action sur ce membre
|
||||
const actionResult = await applyAction(member, config.antimassj_action, 'Anti-Mass Join: Raid détecté');
|
||||
|
||||
return { type: 'massjoin', action: actionResult, joinCount: joins.length };
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie tous les filtres anti-raid pour un message
|
||||
*/
|
||||
async function checkMessage(message, client) {
|
||||
if (!message.guild) return;
|
||||
if (message.author.bot) return;
|
||||
|
||||
const config = await getConfig(message.guild.id);
|
||||
if (!config || !config.enabled) return;
|
||||
|
||||
// Vérifier chaque filtre
|
||||
const checks = [
|
||||
checkAntiLink(message, config),
|
||||
checkAntiInvite(message, config, client),
|
||||
checkAntiSpam(message, config),
|
||||
checkAntiDuplicate(message, config),
|
||||
checkAntiMention(message, config),
|
||||
checkAntiEmoji(message, config),
|
||||
checkAntiCaps(message, config),
|
||||
checkAntiNewline(message, config),
|
||||
checkAntiBadwords(message, config)
|
||||
];
|
||||
|
||||
for (const check of checks) {
|
||||
const result = await check;
|
||||
if (result) {
|
||||
// Toujours ajouter un warn automatique lors d'une violation anti-raid
|
||||
await addAutoWarn(message.guild.id, message.author.id, client.user.id, `Anti-Raid: ${result.type}`, result.type, client);
|
||||
|
||||
const embed = createLogEmbed(result.type, message.author, `Violation détectée`, result.action, {
|
||||
content: result.content,
|
||||
channel: message.channel.id
|
||||
});
|
||||
await sendLog(client, message.guild.id, config, embed);
|
||||
break; // Une seule action par message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un warn automatique et vérifie les sanctions
|
||||
*/
|
||||
async function addAutoWarn(guildId, userId, moderatorId, reason, source, client) {
|
||||
try {
|
||||
// Ajouter le warn
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"INSERT INTO warnings (guild_id, user_id, moderator_id, reason, source) VALUES (?, ?, ?, ?, ?)",
|
||||
[guildId, userId, moderatorId, reason, source],
|
||||
function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve(this.lastID);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Vérifier les sanctions automatiques
|
||||
await checkWarningSanctions(guildId, userId, client);
|
||||
} catch (err) {
|
||||
console.error('Erreur ajout auto warn:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie et applique les sanctions automatiques basées sur le nombre de warns
|
||||
*/
|
||||
async function checkWarningSanctions(guildId, userId, client) {
|
||||
try {
|
||||
// Récupérer la config des warns
|
||||
const warnConfig = await db.getAsync("SELECT * FROM warnings_config WHERE guild_id = ?", [guildId]);
|
||||
if (!warnConfig || !warnConfig.enabled) return;
|
||||
|
||||
// Compter les warns actifs (avec decay si activé)
|
||||
let countQuery = "SELECT COUNT(*) as count FROM warnings WHERE guild_id = ? AND user_id = ?";
|
||||
const params = [guildId, userId];
|
||||
|
||||
if (warnConfig.decay_enabled && warnConfig.decay_days > 0) {
|
||||
const decayTimestamp = Math.floor(Date.now() / 1000) - (warnConfig.decay_days * 86400);
|
||||
countQuery += " AND created_at > ?";
|
||||
params.push(decayTimestamp);
|
||||
}
|
||||
|
||||
const result = await db.getAsync(countQuery, params);
|
||||
const warnCount = result.count;
|
||||
|
||||
// Déterminer l'action à appliquer
|
||||
let action = 'none';
|
||||
let duration = 0;
|
||||
|
||||
if (warnCount >= 5 && warnConfig.warn5_action !== 'none') {
|
||||
action = warnConfig.warn5_action;
|
||||
duration = warnConfig.warn5_duration;
|
||||
} else if (warnCount >= 4 && warnConfig.warn4_action !== 'none') {
|
||||
action = warnConfig.warn4_action;
|
||||
duration = warnConfig.warn4_duration;
|
||||
} else if (warnCount >= 3 && warnConfig.warn3_action !== 'none') {
|
||||
action = warnConfig.warn3_action;
|
||||
duration = warnConfig.warn3_duration;
|
||||
} else if (warnCount >= 2 && warnConfig.warn2_action !== 'none') {
|
||||
action = warnConfig.warn2_action;
|
||||
duration = warnConfig.warn2_duration;
|
||||
} else if (warnCount >= 1 && warnConfig.warn1_action !== 'none') {
|
||||
action = warnConfig.warn1_action;
|
||||
duration = warnConfig.warn1_duration;
|
||||
}
|
||||
|
||||
if (action === 'none') return;
|
||||
|
||||
// Appliquer la sanction
|
||||
const guild = client.guilds.cache.get(guildId);
|
||||
if (!guild) return;
|
||||
|
||||
const member = await guild.members.fetch(userId).catch(() => null);
|
||||
if (!member) return;
|
||||
|
||||
const reason = `Sanction automatique: ${warnCount} avertissement(s)`;
|
||||
|
||||
switch (action) {
|
||||
case 'mute':
|
||||
await member.timeout(duration * 60 * 1000, reason).catch(() => {});
|
||||
break;
|
||||
case 'kick':
|
||||
await member.kick(reason).catch(() => {});
|
||||
break;
|
||||
case 'ban':
|
||||
await member.ban({ reason, deleteMessageSeconds: 86400 }).catch(() => {});
|
||||
break;
|
||||
}
|
||||
|
||||
// Notifier si configuré
|
||||
if (warnConfig.notify_channel_id) {
|
||||
const channel = guild.channels.cache.get(warnConfig.notify_channel_id);
|
||||
if (channel) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xED4245)
|
||||
.setTitle('⚠️ Sanction automatique')
|
||||
.setDescription(`**${member.user.tag}** a reçu une sanction automatique.`)
|
||||
.addFields(
|
||||
{ name: '👤 Utilisateur', value: `${member}`, inline: true },
|
||||
{ name: '📊 Avertissements', value: `${warnCount}`, inline: true },
|
||||
{ name: '⚡ Action', value: action, inline: true }
|
||||
)
|
||||
.setTimestamp();
|
||||
await channel.send({ embeds: [embed] }).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erreur check warning sanctions:', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie les filtres anti-raid pour un nouveau membre
|
||||
*/
|
||||
async function checkMemberJoin(member, client) {
|
||||
if (member.user.bot) return;
|
||||
|
||||
const config = await getConfig(member.guild.id);
|
||||
if (!config || !config.enabled) return;
|
||||
|
||||
// Anti-bot
|
||||
const botResult = await checkAntiBot(member, config);
|
||||
if (botResult) {
|
||||
// Ajouter un warn automatique
|
||||
await addAutoWarn(member.guild.id, member.user.id, client.user.id, `Anti-Raid: Compte suspect (${botResult.reasons.join(', ')})`, 'antibot', client);
|
||||
|
||||
const embed = createLogEmbed('bot', member.user, `Compte suspect détecté: ${botResult.reasons.join(', ')}`, botResult.action);
|
||||
await sendLog(client, member.guild.id, config, embed);
|
||||
}
|
||||
|
||||
// Anti-mass join
|
||||
const massJoinResult = await checkAntiMassJoin(member, config);
|
||||
if (massJoinResult) {
|
||||
// Ajouter un warn automatique
|
||||
await addAutoWarn(member.guild.id, member.user.id, client.user.id, `Anti-Raid: Mass join détecté`, 'massjoin', client);
|
||||
|
||||
const embed = createLogEmbed('massjoin', member.user, `Raid potentiel détecté: ${massJoinResult.joinCount} joins rapides`, massJoinResult.action);
|
||||
await sendLog(client, member.guild.id, config, embed);
|
||||
}
|
||||
}
|
||||
|
||||
// Nettoyage périodique des trackers
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
const maxAge = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
for (const [key, timestamps] of spamTracker) {
|
||||
const filtered = timestamps.filter(t => now - t < maxAge);
|
||||
if (filtered.length === 0) spamTracker.delete(key);
|
||||
else spamTracker.set(key, filtered);
|
||||
}
|
||||
|
||||
for (const [key, history] of dupeTracker) {
|
||||
const filtered = history.filter(h => now - h.timestamp < maxAge);
|
||||
if (filtered.length === 0) dupeTracker.delete(key);
|
||||
else dupeTracker.set(key, filtered);
|
||||
}
|
||||
|
||||
for (const [key, joins] of joinTracker) {
|
||||
const filtered = joins.filter(t => now - t < maxAge);
|
||||
if (filtered.length === 0) joinTracker.delete(key);
|
||||
else joinTracker.set(key, filtered);
|
||||
}
|
||||
}, 60000);
|
||||
|
||||
module.exports = {
|
||||
getConfig,
|
||||
checkMessage,
|
||||
checkMemberJoin,
|
||||
createLogEmbed,
|
||||
sendLog,
|
||||
addAutoWarn,
|
||||
checkWarningSanctions
|
||||
};
|
||||
+123
-3
@@ -439,11 +439,11 @@ body {
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
display: none;
|
||||
min-height: 1.5em;
|
||||
}
|
||||
|
||||
.status-message.show {
|
||||
display: block;
|
||||
.status-message:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.status-message.success {
|
||||
@@ -1166,3 +1166,123 @@ body {
|
||||
background: #c0392b;
|
||||
}
|
||||
|
||||
/* ===== Anti-Raid Styles ===== */
|
||||
.antiraid-modules {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.antiraid-module {
|
||||
background: var(--bg-dark);
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: hidden;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.antiraid-module:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.antiraid-module-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-md);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.antiraid-module-header:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.antiraid-module-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.antiraid-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.antiraid-module-body {
|
||||
padding: var(--spacing-md);
|
||||
border-top: 1px solid var(--border-color);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.antiraid-module-body .text-muted {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.checkbox-label:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.multi-select {
|
||||
min-height: 80px;
|
||||
padding: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.multi-select option {
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.multi-select option:checked {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Status messages */
|
||||
.status-message {
|
||||
font-size: 0.9rem;
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.status-message.success {
|
||||
color: #57F287;
|
||||
background: rgba(87, 242, 135, 0.1);
|
||||
}
|
||||
|
||||
.status-message.error {
|
||||
color: var(--error-color);
|
||||
background: rgba(237, 66, 69, 0.1);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@
|
||||
<span class="nav-item-icon">📜</span>
|
||||
Logs
|
||||
</a>
|
||||
<a class="nav-item" data-section="antiraid">
|
||||
<span class="nav-item-icon">🛡️</span>
|
||||
Anti-Raid
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1177,6 +1181,723 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section: Anti-Raid -->
|
||||
<section class="config-section" id="section-antiraid">
|
||||
<div class="config-card">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-title">
|
||||
<span class="icon">🛡️</span>
|
||||
<h3>Système Anti-Raid</h3>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antiraid-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="config-card-body">
|
||||
<p class="text-muted" style="margin-bottom: 1.5rem;">
|
||||
Protégez votre serveur contre les raids, le spam et les comportements malveillants. Configurez chaque module individuellement.
|
||||
</p>
|
||||
|
||||
<!-- Salon de logs anti-raid -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">📜 Salon de logs Anti-Raid</label>
|
||||
<select class="form-select" id="antiraid-log-channel">
|
||||
<option value="">Aucun (pas de logs)</option>
|
||||
</select>
|
||||
<small class="text-muted">Les actions de l'anti-raid seront envoyées dans ce salon.</small>
|
||||
</div>
|
||||
|
||||
<!-- Modules Anti-Raid -->
|
||||
<div class="antiraid-modules">
|
||||
|
||||
<!-- Anti-Link -->
|
||||
<div class="antiraid-module" id="module-antilink">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">🔗</span>
|
||||
<span>Anti-Link</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antilink-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les liens externes. Vous pouvez autoriser certains domaines.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antilink-action">
|
||||
<option value="delete">Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">✅ Domaines autorisés</label>
|
||||
<input type="text" class="form-input" id="antilink-whitelist" placeholder="youtube.com, twitter.com">
|
||||
<small class="text-muted">Séparés par des virgules</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">💬 Message d'avertissement</label>
|
||||
<input type="text" class="form-input" id="antilink-warn-message" placeholder="⚠️ Les liens ne sont pas autorisés ici." value="⚠️ Les liens ne sont pas autorisés ici.">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antilink-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antilink-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Invite -->
|
||||
<div class="antiraid-module" id="module-antiinvite">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">📨</span>
|
||||
<span>Anti-Invite</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antiinvite-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les invitations Discord d'autres serveurs.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antiinvite-action">
|
||||
<option value="delete">Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" style="display: flex; align-items: center; gap: 0.5rem;">
|
||||
<input type="checkbox" id="antiinvite-allow-own">
|
||||
Autoriser les invites de ce serveur
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antiinvite-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antiinvite-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Spam -->
|
||||
<div class="antiraid-module" id="module-antispam">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">⚡</span>
|
||||
<span>Anti-Spam</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antispam-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Détecte les utilisateurs qui envoient trop de messages rapidement.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">📊 Messages max</label>
|
||||
<input type="number" class="form-input" id="antispam-max-messages" min="3" max="20" value="5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Intervalle (secondes)</label>
|
||||
<input type="number" class="form-input" id="antispam-interval" min="1" max="30" value="5">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antispam-action">
|
||||
<option value="delete">Supprimer les messages</option>
|
||||
<option value="mute" selected>Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">🔇 Durée du mute (minutes)</label>
|
||||
<input type="number" class="form-input" id="antispam-mute-duration" min="1" max="1440" value="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antispam-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antispam-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Duplicate -->
|
||||
<div class="antiraid-module" id="module-antidupe">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">📋</span>
|
||||
<span>Anti-Duplicate</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antidupe-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les utilisateurs qui répètent le même message.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🔄 Répétitions max</label>
|
||||
<input type="number" class="form-input" id="antidupe-max" min="2" max="10" value="3">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Intervalle (secondes)</label>
|
||||
<input type="number" class="form-input" id="antidupe-interval" min="5" max="120" value="30">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antidupe-action">
|
||||
<option value="delete" selected>Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antidupe-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antidupe-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Mention -->
|
||||
<div class="antiraid-module" id="module-antimention">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">📢</span>
|
||||
<span>Anti-Mass Mention</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antimention-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les messages avec trop de mentions.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">📊 Mentions max</label>
|
||||
<input type="number" class="form-input" id="antimention-max" min="2" max="30" value="5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antimention-action">
|
||||
<option value="delete" selected>Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antimention-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antimention-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Emoji -->
|
||||
<div class="antiraid-module" id="module-antiemoji">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">😀</span>
|
||||
<span>Anti-Emoji Spam</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antiemoji-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les messages avec trop d'emojis.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">😀 Emojis max</label>
|
||||
<input type="number" class="form-input" id="antiemoji-max" min="3" max="50" value="10">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antiemoji-action">
|
||||
<option value="delete" selected>Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antiemoji-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antiemoji-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Caps -->
|
||||
<div class="antiraid-module" id="module-anticaps">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">🔠</span>
|
||||
<span>Anti-Caps</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="anticaps-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les messages avec trop de MAJUSCULES.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">📊 % de majuscules max</label>
|
||||
<input type="number" class="form-input" id="anticaps-max-percent" min="30" max="100" value="70">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">📏 Longueur min (caractères)</label>
|
||||
<input type="number" class="form-input" id="anticaps-min-length" min="5" max="50" value="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="anticaps-action">
|
||||
<option value="delete" selected>Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="anticaps-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="anticaps-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Newline -->
|
||||
<div class="antiraid-module" id="module-antinewline">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">📄</span>
|
||||
<span>Anti-Newline Spam</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antinewline-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les messages avec trop de sauts de ligne.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">📊 Lignes max</label>
|
||||
<input type="number" class="form-input" id="antinewline-max" min="5" max="50" value="15">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antinewline-action">
|
||||
<option value="delete" selected>Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antinewline-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antinewline-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Bot Join -->
|
||||
<div class="antiraid-module" id="module-antibot">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">🤖</span>
|
||||
<span>Anti-Bot / Compte suspect</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antibot-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Détecte les comptes suspects (nouveaux comptes, sans avatar, noms suspects).</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">📅 Âge min du compte (jours)</label>
|
||||
<input type="number" class="form-input" id="antibot-min-age" min="0" max="365" value="7">
|
||||
<small class="text-muted">0 = désactivé</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antibot-action">
|
||||
<option value="warn">Avertir (log uniquement)</option>
|
||||
<option value="kick" selected>Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">🔍 Critères de détection</label>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="antibot-no-avatar">
|
||||
Compte sans avatar personnalisé
|
||||
</label>
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="antibot-suspicious-name">
|
||||
Nom suspect (pattern de bot type "user1234")
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Mass Join -->
|
||||
<div class="antiraid-module" id="module-antimassj">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">👥</span>
|
||||
<span>Anti-Mass Join (Raid)</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antimassj-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Détecte quand trop de membres rejoignent en peu de temps (raid).</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">👥 Membres max</label>
|
||||
<input type="number" class="form-input" id="antimassj-max" min="3" max="50" value="10">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Intervalle (secondes)</label>
|
||||
<input type="number" class="form-input" id="antimassj-interval" min="5" max="120" value="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action sur les nouveaux membres pendant le raid</label>
|
||||
<select class="form-select" id="antimassj-action">
|
||||
<option value="warn">Log uniquement</option>
|
||||
<option value="kick" selected>Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Anti-Badwords -->
|
||||
<div class="antiraid-module" id="module-antibadwords">
|
||||
<div class="antiraid-module-header">
|
||||
<div class="antiraid-module-title">
|
||||
<span class="antiraid-icon">🤬</span>
|
||||
<span>Anti-Gros Mots</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="antibadwords-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="antiraid-module-body">
|
||||
<p class="text-muted">Bloque les messages contenant des insultes ou gros mots.</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">📝 Liste des mots interdits</label>
|
||||
<textarea class="form-input" id="antibadwords-words" rows="3" placeholder="Entrez les mots interdits, un par ligne ou séparés par des virgules"></textarea>
|
||||
<small class="text-muted">Le système détecte aussi les variantes avec caractères spéciaux (ex: m3rde)</small>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="antibadwords-action">
|
||||
<option value="delete" selected>Supprimer le message</option>
|
||||
<option value="warn">Avertir l'utilisateur</option>
|
||||
<option value="mute">Mute temporaire</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">💬 Message d'avertissement</label>
|
||||
<input type="text" class="form-input" id="antibadwords-warn-message" placeholder="⚠️ Les insultes ne sont pas autorisées." value="⚠️ Les insultes et gros mots ne sont pas autorisés.">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">🚫 Salons exclus</label>
|
||||
<select class="form-select multi-select" id="antibadwords-exclude-channels" multiple></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">👑 Rôles exclus</label>
|
||||
<select class="form-select multi-select" id="antibadwords-exclude-roles" multiple></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Section Sanctions automatiques -->
|
||||
<div class="config-card" style="margin-top: 1.5rem;">
|
||||
<div class="config-card-header">
|
||||
<div class="config-card-title">
|
||||
<span class="icon">⚠️</span>
|
||||
<h3>Sanctions automatiques (Warns)</h3>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="warnings-enabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="config-card-body">
|
||||
<p class="text-muted" style="margin-bottom: 1.5rem;">
|
||||
Configurez les sanctions automatiques en fonction du nombre d'avertissements.
|
||||
</p>
|
||||
|
||||
<div class="warnings-config">
|
||||
<!-- Warn 1 -->
|
||||
<div class="warning-level">
|
||||
<div class="warning-level-header">
|
||||
<span class="warning-badge">1 Warn</span>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="warn1-action">
|
||||
<option value="none" selected>Aucune</option>
|
||||
<option value="mute">Mute</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Durée (minutes)</label>
|
||||
<input type="number" class="form-input" id="warn1-duration" min="1" max="10080" value="10">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warn 2 -->
|
||||
<div class="warning-level">
|
||||
<div class="warning-level-header">
|
||||
<span class="warning-badge">2 Warns</span>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="warn2-action">
|
||||
<option value="none" selected>Aucune</option>
|
||||
<option value="mute">Mute</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Durée (minutes)</label>
|
||||
<input type="number" class="form-input" id="warn2-duration" min="1" max="10080" value="30">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warn 3 -->
|
||||
<div class="warning-level">
|
||||
<div class="warning-level-header">
|
||||
<span class="warning-badge">3 Warns</span>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="warn3-action">
|
||||
<option value="none">Aucune</option>
|
||||
<option value="mute" selected>Mute</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Durée (minutes)</label>
|
||||
<input type="number" class="form-input" id="warn3-duration" min="1" max="10080" value="60">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warn 4 -->
|
||||
<div class="warning-level">
|
||||
<div class="warning-level-header">
|
||||
<span class="warning-badge">4 Warns</span>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="warn4-action">
|
||||
<option value="none">Aucune</option>
|
||||
<option value="mute">Mute</option>
|
||||
<option value="kick" selected>Expulser</option>
|
||||
<option value="ban">Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Durée (minutes)</label>
|
||||
<input type="number" class="form-input" id="warn4-duration" min="1" max="10080" value="0" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warn 5+ -->
|
||||
<div class="warning-level">
|
||||
<div class="warning-level-header">
|
||||
<span class="warning-badge warning-badge-danger">5+ Warns</span>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">⚡ Action</label>
|
||||
<select class="form-select" id="warn5-action">
|
||||
<option value="none">Aucune</option>
|
||||
<option value="mute">Mute</option>
|
||||
<option value="kick">Expulser</option>
|
||||
<option value="ban" selected>Bannir</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">⏱️ Durée (minutes)</label>
|
||||
<input type="number" class="form-input" id="warn5-duration" min="1" max="10080" value="0" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Options supplémentaires -->
|
||||
<div class="form-group" style="margin-top: 1.5rem;">
|
||||
<label class="form-label">⚙️ Options</label>
|
||||
<div class="checkbox-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="warnings-decay-enabled">
|
||||
Expiration des warns après un certain temps
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="warnings-decay-options" style="display: none;">
|
||||
<div class="form-group">
|
||||
<label class="form-label">📅 Expiration après (jours)</label>
|
||||
<input type="number" class="form-input" id="warnings-decay-days" min="1" max="365" value="30">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">📜 Salon de notifications</label>
|
||||
<select class="form-select" id="warnings-notify-channel">
|
||||
<option value="">Aucun (pas de notification)</option>
|
||||
</select>
|
||||
<small class="text-muted">Les sanctions automatiques seront notifiées dans ce salon.</small>
|
||||
</div>
|
||||
|
||||
<div class="config-card-footer" style="margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border-color);">
|
||||
<div id="status-warnings-form" class="status-message"></div>
|
||||
<button type="button" class="btn btn-primary" id="warnings-save-btn">
|
||||
💾 Sauvegarder les sanctions
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="config-card-footer">
|
||||
<div id="status-antiraid-form" class="status-message"></div>
|
||||
<button type="button" class="btn btn-primary" id="antiraid-save-btn">
|
||||
💾 Sauvegarder
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1198,5 +1919,6 @@
|
||||
<script src="/guild/sendMessageForm.js"></script>
|
||||
<script src="/guild/botAppearanceForm.js"></script>
|
||||
<script src="/guild/logsForm.js"></script>
|
||||
<script src="/guild/antiraidForm.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
// =============================================
|
||||
// ========== ANTI-RAID FORM ===================
|
||||
// =============================================
|
||||
|
||||
(function() {
|
||||
let config = {};
|
||||
let warningsConfig = {};
|
||||
let channels = [];
|
||||
let roles = [];
|
||||
|
||||
// Charger la config anti-raid
|
||||
async function loadAntiraidConfig() {
|
||||
try {
|
||||
const res = await fetch(`/api/bot/get-antiraid-config?guildId=${guildId}`);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
config = data.config;
|
||||
channels = data.channels || [];
|
||||
roles = data.roles || [];
|
||||
|
||||
populateSelects();
|
||||
fillForm();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur chargement config antiraid:', err);
|
||||
}
|
||||
|
||||
// Charger aussi la config des warnings
|
||||
try {
|
||||
const res = await fetch(`/api/bot/get-warnings-config?guildId=${guildId}`);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
warningsConfig = data.config;
|
||||
fillWarningsForm();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur chargement config warnings:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Remplir les selects avec les salons et rôles
|
||||
function populateSelects() {
|
||||
// Salon de logs
|
||||
const logChannelSelect = document.getElementById('antiraid-log-channel');
|
||||
logChannelSelect.innerHTML = '<option value="">Aucun (pas de logs)</option>';
|
||||
channels.forEach(ch => {
|
||||
logChannelSelect.innerHTML += `<option value="${ch.id}"># ${ch.name}</option>`;
|
||||
});
|
||||
|
||||
// Salon notifications warnings
|
||||
const notifyChannelSelect = document.getElementById('warnings-notify-channel');
|
||||
if (notifyChannelSelect) {
|
||||
notifyChannelSelect.innerHTML = '<option value="">Aucun (pas de notification)</option>';
|
||||
channels.forEach(ch => {
|
||||
notifyChannelSelect.innerHTML += `<option value="${ch.id}"># ${ch.name}</option>`;
|
||||
});
|
||||
}
|
||||
|
||||
// Multi-selects pour les exclusions
|
||||
const channelSelects = [
|
||||
'antilink-exclude-channels',
|
||||
'antiinvite-exclude-channels',
|
||||
'antispam-exclude-channels',
|
||||
'antidupe-exclude-channels',
|
||||
'antimention-exclude-channels',
|
||||
'antiemoji-exclude-channels',
|
||||
'anticaps-exclude-channels',
|
||||
'antinewline-exclude-channels',
|
||||
'antibadwords-exclude-channels'
|
||||
];
|
||||
|
||||
const roleSelects = [
|
||||
'antilink-exclude-roles',
|
||||
'antiinvite-exclude-roles',
|
||||
'antispam-exclude-roles',
|
||||
'antidupe-exclude-roles',
|
||||
'antimention-exclude-roles',
|
||||
'antiemoji-exclude-roles',
|
||||
'anticaps-exclude-roles',
|
||||
'antinewline-exclude-roles',
|
||||
'antibadwords-exclude-roles'
|
||||
];
|
||||
|
||||
channelSelects.forEach(selectId => {
|
||||
const select = document.getElementById(selectId);
|
||||
if (select) {
|
||||
select.innerHTML = '';
|
||||
channels.forEach(ch => {
|
||||
select.innerHTML += `<option value="${ch.id}"># ${ch.name}</option>`;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
roleSelects.forEach(selectId => {
|
||||
const select = document.getElementById(selectId);
|
||||
if (select) {
|
||||
select.innerHTML = '';
|
||||
roles.forEach(r => {
|
||||
select.innerHTML += `<option value="${r.id}" style="color: ${r.color}">@ ${r.name}</option>`;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remplir le formulaire avec la config
|
||||
function fillForm() {
|
||||
// Global
|
||||
document.getElementById('antiraid-enabled').checked = config.enabled;
|
||||
document.getElementById('antiraid-log-channel').value = config.log_channel_id || '';
|
||||
|
||||
// Anti-Link
|
||||
document.getElementById('antilink-enabled').checked = config.antilink_enabled;
|
||||
document.getElementById('antilink-action').value = config.antilink_action || 'delete';
|
||||
document.getElementById('antilink-warn-message').value = config.antilink_warn_message || '⚠️ Les liens ne sont pas autorisés ici.';
|
||||
try {
|
||||
const whitelist = JSON.parse(config.antilink_whitelist_domains || '[]');
|
||||
document.getElementById('antilink-whitelist').value = whitelist.join(', ');
|
||||
} catch { document.getElementById('antilink-whitelist').value = ''; }
|
||||
setMultiSelectValues('antilink-exclude-channels', config.antilink_exclude_channels);
|
||||
setMultiSelectValues('antilink-exclude-roles', config.antilink_exclude_roles);
|
||||
|
||||
// Anti-Invite
|
||||
document.getElementById('antiinvite-enabled').checked = config.antiinvite_enabled;
|
||||
document.getElementById('antiinvite-action').value = config.antiinvite_action || 'delete';
|
||||
document.getElementById('antiinvite-allow-own').checked = config.antiinvite_allow_own_server;
|
||||
setMultiSelectValues('antiinvite-exclude-channels', config.antiinvite_exclude_channels);
|
||||
setMultiSelectValues('antiinvite-exclude-roles', config.antiinvite_exclude_roles);
|
||||
|
||||
// Anti-Spam
|
||||
document.getElementById('antispam-enabled').checked = config.antispam_enabled;
|
||||
document.getElementById('antispam-max-messages').value = config.antispam_max_messages || 5;
|
||||
document.getElementById('antispam-interval').value = config.antispam_interval_seconds || 5;
|
||||
document.getElementById('antispam-action').value = config.antispam_action || 'mute';
|
||||
document.getElementById('antispam-mute-duration').value = config.antispam_mute_duration_minutes || 10;
|
||||
setMultiSelectValues('antispam-exclude-channels', config.antispam_exclude_channels);
|
||||
setMultiSelectValues('antispam-exclude-roles', config.antispam_exclude_roles);
|
||||
|
||||
// Anti-Duplicate
|
||||
document.getElementById('antidupe-enabled').checked = config.antidupe_enabled;
|
||||
document.getElementById('antidupe-max').value = config.antidupe_max_duplicates || 3;
|
||||
document.getElementById('antidupe-interval').value = config.antidupe_interval_seconds || 30;
|
||||
document.getElementById('antidupe-action').value = config.antidupe_action || 'delete';
|
||||
setMultiSelectValues('antidupe-exclude-channels', config.antidupe_exclude_channels);
|
||||
setMultiSelectValues('antidupe-exclude-roles', config.antidupe_exclude_roles);
|
||||
|
||||
// Anti-Mention
|
||||
document.getElementById('antimention-enabled').checked = config.antimention_enabled;
|
||||
document.getElementById('antimention-max').value = config.antimention_max_mentions || 5;
|
||||
document.getElementById('antimention-action').value = config.antimention_action || 'delete';
|
||||
setMultiSelectValues('antimention-exclude-channels', config.antimention_exclude_channels);
|
||||
setMultiSelectValues('antimention-exclude-roles', config.antimention_exclude_roles);
|
||||
|
||||
// Anti-Emoji
|
||||
document.getElementById('antiemoji-enabled').checked = config.antiemoji_enabled;
|
||||
document.getElementById('antiemoji-max').value = config.antiemoji_max_emojis || 10;
|
||||
document.getElementById('antiemoji-action').value = config.antiemoji_action || 'delete';
|
||||
setMultiSelectValues('antiemoji-exclude-channels', config.antiemoji_exclude_channels);
|
||||
setMultiSelectValues('antiemoji-exclude-roles', config.antiemoji_exclude_roles);
|
||||
|
||||
// Anti-Caps
|
||||
document.getElementById('anticaps-enabled').checked = config.anticaps_enabled;
|
||||
document.getElementById('anticaps-max-percent').value = config.anticaps_max_percent || 70;
|
||||
document.getElementById('anticaps-min-length').value = config.anticaps_min_length || 10;
|
||||
document.getElementById('anticaps-action').value = config.anticaps_action || 'delete';
|
||||
setMultiSelectValues('anticaps-exclude-channels', config.anticaps_exclude_channels);
|
||||
setMultiSelectValues('anticaps-exclude-roles', config.anticaps_exclude_roles);
|
||||
|
||||
// Anti-Newline
|
||||
document.getElementById('antinewline-enabled').checked = config.antinewline_enabled;
|
||||
document.getElementById('antinewline-max').value = config.antinewline_max_lines || 15;
|
||||
document.getElementById('antinewline-action').value = config.antinewline_action || 'delete';
|
||||
setMultiSelectValues('antinewline-exclude-channels', config.antinewline_exclude_channels);
|
||||
setMultiSelectValues('antinewline-exclude-roles', config.antinewline_exclude_roles);
|
||||
|
||||
// Anti-Bot
|
||||
document.getElementById('antibot-enabled').checked = config.antibot_enabled;
|
||||
document.getElementById('antibot-min-age').value = config.antibot_min_account_age_days || 7;
|
||||
document.getElementById('antibot-action').value = config.antibot_action || 'kick';
|
||||
document.getElementById('antibot-no-avatar').checked = config.antibot_no_avatar_action;
|
||||
document.getElementById('antibot-suspicious-name').checked = config.antibot_suspicious_name_action;
|
||||
|
||||
// Anti-Mass Join
|
||||
document.getElementById('antimassj-enabled').checked = config.antimassj_enabled;
|
||||
document.getElementById('antimassj-max').value = config.antimassj_max_joins || 10;
|
||||
document.getElementById('antimassj-interval').value = config.antimassj_interval_seconds || 10;
|
||||
document.getElementById('antimassj-action').value = config.antimassj_action || 'kick';
|
||||
|
||||
// Anti-Badwords
|
||||
document.getElementById('antibadwords-enabled').checked = config.antibadwords_enabled;
|
||||
document.getElementById('antibadwords-action').value = config.antibadwords_action || 'delete';
|
||||
document.getElementById('antibadwords-warn-message').value = config.antibadwords_warn_message || '⚠️ Les insultes et gros mots ne sont pas autorisés.';
|
||||
try {
|
||||
const words = JSON.parse(config.antibadwords_words || '[]');
|
||||
document.getElementById('antibadwords-words').value = words.join('\n');
|
||||
} catch { document.getElementById('antibadwords-words').value = ''; }
|
||||
setMultiSelectValues('antibadwords-exclude-channels', config.antibadwords_exclude_channels);
|
||||
setMultiSelectValues('antibadwords-exclude-roles', config.antibadwords_exclude_roles);
|
||||
|
||||
updateModulesVisibility();
|
||||
}
|
||||
|
||||
// Remplir le formulaire des warnings
|
||||
function fillWarningsForm() {
|
||||
document.getElementById('warnings-enabled').checked = warningsConfig.enabled;
|
||||
document.getElementById('warn1-action').value = warningsConfig.warn1_action || 'none';
|
||||
document.getElementById('warn1-duration').value = warningsConfig.warn1_duration || 10;
|
||||
document.getElementById('warn2-action').value = warningsConfig.warn2_action || 'none';
|
||||
document.getElementById('warn2-duration').value = warningsConfig.warn2_duration || 30;
|
||||
document.getElementById('warn3-action').value = warningsConfig.warn3_action || 'mute';
|
||||
document.getElementById('warn3-duration').value = warningsConfig.warn3_duration || 60;
|
||||
document.getElementById('warn4-action').value = warningsConfig.warn4_action || 'kick';
|
||||
document.getElementById('warn4-duration').value = warningsConfig.warn4_duration || 0;
|
||||
document.getElementById('warn5-action').value = warningsConfig.warn5_action || 'ban';
|
||||
document.getElementById('warn5-duration').value = warningsConfig.warn5_duration || 0;
|
||||
document.getElementById('warnings-decay-enabled').checked = warningsConfig.decay_enabled;
|
||||
document.getElementById('warnings-decay-days').value = warningsConfig.decay_days || 30;
|
||||
document.getElementById('warnings-notify-channel').value = warningsConfig.notify_channel_id || '';
|
||||
|
||||
// Afficher/masquer les options de decay
|
||||
document.getElementById('warnings-decay-options').style.display =
|
||||
warningsConfig.decay_enabled ? 'grid' : 'none';
|
||||
|
||||
updateWarningDurationFields();
|
||||
}
|
||||
|
||||
// Mettre à jour les champs de durée selon l'action
|
||||
function updateWarningDurationFields() {
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const action = document.getElementById(`warn${i}-action`).value;
|
||||
const durationField = document.getElementById(`warn${i}-duration`);
|
||||
durationField.disabled = action !== 'mute';
|
||||
}
|
||||
}
|
||||
|
||||
// Helper pour les multi-selects
|
||||
function setMultiSelectValues(selectId, jsonValue) {
|
||||
const select = document.getElementById(selectId);
|
||||
if (!select) return;
|
||||
|
||||
try {
|
||||
const values = JSON.parse(jsonValue || '[]');
|
||||
Array.from(select.options).forEach(opt => {
|
||||
opt.selected = values.includes(opt.value);
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function getMultiSelectValues(selectId) {
|
||||
const select = document.getElementById(selectId);
|
||||
if (!select) return [];
|
||||
return Array.from(select.selectedOptions).map(opt => opt.value);
|
||||
}
|
||||
|
||||
// Mise à jour de la visibilité des modules selon l'état global
|
||||
function updateModulesVisibility() {
|
||||
const enabled = document.getElementById('antiraid-enabled').checked;
|
||||
const modules = document.querySelectorAll('.antiraid-module');
|
||||
modules.forEach(m => {
|
||||
m.style.opacity = enabled ? '1' : '0.5';
|
||||
m.style.pointerEvents = enabled ? 'auto' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Sauvegarder la config
|
||||
async function saveAntiraidConfig() {
|
||||
const statusEl = document.getElementById('status-antiraid-form');
|
||||
const saveBtn = document.getElementById('antiraid-save-btn');
|
||||
|
||||
saveBtn.disabled = true;
|
||||
statusEl.textContent = 'Sauvegarde en cours...';
|
||||
statusEl.className = 'status-message';
|
||||
|
||||
// Parser la whitelist
|
||||
const whitelistText = document.getElementById('antilink-whitelist').value;
|
||||
const whitelist = whitelistText.split(',').map(d => d.trim()).filter(d => d);
|
||||
|
||||
const configData = {
|
||||
enabled: document.getElementById('antiraid-enabled').checked,
|
||||
log_channel_id: document.getElementById('antiraid-log-channel').value || null,
|
||||
|
||||
// Anti-Link
|
||||
antilink_enabled: document.getElementById('antilink-enabled').checked,
|
||||
antilink_action: document.getElementById('antilink-action').value,
|
||||
antilink_whitelist_domains: whitelist,
|
||||
antilink_exclude_channels: getMultiSelectValues('antilink-exclude-channels'),
|
||||
antilink_exclude_roles: getMultiSelectValues('antilink-exclude-roles'),
|
||||
antilink_warn_message: document.getElementById('antilink-warn-message').value,
|
||||
|
||||
// Anti-Invite
|
||||
antiinvite_enabled: document.getElementById('antiinvite-enabled').checked,
|
||||
antiinvite_action: document.getElementById('antiinvite-action').value,
|
||||
antiinvite_allow_own_server: document.getElementById('antiinvite-allow-own').checked,
|
||||
antiinvite_exclude_channels: getMultiSelectValues('antiinvite-exclude-channels'),
|
||||
antiinvite_exclude_roles: getMultiSelectValues('antiinvite-exclude-roles'),
|
||||
|
||||
// Anti-Spam
|
||||
antispam_enabled: document.getElementById('antispam-enabled').checked,
|
||||
antispam_max_messages: parseInt(document.getElementById('antispam-max-messages').value) || 5,
|
||||
antispam_interval_seconds: parseInt(document.getElementById('antispam-interval').value) || 5,
|
||||
antispam_action: document.getElementById('antispam-action').value,
|
||||
antispam_mute_duration_minutes: parseInt(document.getElementById('antispam-mute-duration').value) || 10,
|
||||
antispam_exclude_channels: getMultiSelectValues('antispam-exclude-channels'),
|
||||
antispam_exclude_roles: getMultiSelectValues('antispam-exclude-roles'),
|
||||
|
||||
// Anti-Duplicate
|
||||
antidupe_enabled: document.getElementById('antidupe-enabled').checked,
|
||||
antidupe_max_duplicates: parseInt(document.getElementById('antidupe-max').value) || 3,
|
||||
antidupe_interval_seconds: parseInt(document.getElementById('antidupe-interval').value) || 30,
|
||||
antidupe_action: document.getElementById('antidupe-action').value,
|
||||
antidupe_exclude_channels: getMultiSelectValues('antidupe-exclude-channels'),
|
||||
antidupe_exclude_roles: getMultiSelectValues('antidupe-exclude-roles'),
|
||||
|
||||
// Anti-Mention
|
||||
antimention_enabled: document.getElementById('antimention-enabled').checked,
|
||||
antimention_max_mentions: parseInt(document.getElementById('antimention-max').value) || 5,
|
||||
antimention_action: document.getElementById('antimention-action').value,
|
||||
antimention_exclude_channels: getMultiSelectValues('antimention-exclude-channels'),
|
||||
antimention_exclude_roles: getMultiSelectValues('antimention-exclude-roles'),
|
||||
|
||||
// Anti-Emoji
|
||||
antiemoji_enabled: document.getElementById('antiemoji-enabled').checked,
|
||||
antiemoji_max_emojis: parseInt(document.getElementById('antiemoji-max').value) || 10,
|
||||
antiemoji_action: document.getElementById('antiemoji-action').value,
|
||||
antiemoji_exclude_channels: getMultiSelectValues('antiemoji-exclude-channels'),
|
||||
antiemoji_exclude_roles: getMultiSelectValues('antiemoji-exclude-roles'),
|
||||
|
||||
// Anti-Caps
|
||||
anticaps_enabled: document.getElementById('anticaps-enabled').checked,
|
||||
anticaps_max_percent: parseInt(document.getElementById('anticaps-max-percent').value) || 70,
|
||||
anticaps_min_length: parseInt(document.getElementById('anticaps-min-length').value) || 10,
|
||||
anticaps_action: document.getElementById('anticaps-action').value,
|
||||
anticaps_exclude_channels: getMultiSelectValues('anticaps-exclude-channels'),
|
||||
anticaps_exclude_roles: getMultiSelectValues('anticaps-exclude-roles'),
|
||||
|
||||
// Anti-Newline
|
||||
antinewline_enabled: document.getElementById('antinewline-enabled').checked,
|
||||
antinewline_max_lines: parseInt(document.getElementById('antinewline-max').value) || 15,
|
||||
antinewline_action: document.getElementById('antinewline-action').value,
|
||||
antinewline_exclude_channels: getMultiSelectValues('antinewline-exclude-channels'),
|
||||
antinewline_exclude_roles: getMultiSelectValues('antinewline-exclude-roles'),
|
||||
|
||||
// Anti-Bot
|
||||
antibot_enabled: document.getElementById('antibot-enabled').checked,
|
||||
antibot_min_account_age_days: parseInt(document.getElementById('antibot-min-age').value) || 7,
|
||||
antibot_no_avatar_action: document.getElementById('antibot-no-avatar').checked,
|
||||
antibot_suspicious_name_action: document.getElementById('antibot-suspicious-name').checked,
|
||||
antibot_action: document.getElementById('antibot-action').value,
|
||||
|
||||
// Anti-Mass Join
|
||||
antimassj_enabled: document.getElementById('antimassj-enabled').checked,
|
||||
antimassj_max_joins: parseInt(document.getElementById('antimassj-max').value) || 10,
|
||||
antimassj_interval_seconds: parseInt(document.getElementById('antimassj-interval').value) || 10,
|
||||
antimassj_action: document.getElementById('antimassj-action').value,
|
||||
|
||||
// Anti-Badwords
|
||||
antibadwords_enabled: document.getElementById('antibadwords-enabled').checked,
|
||||
antibadwords_words: document.getElementById('antibadwords-words').value.split('\n').map(w => w.trim()).filter(w => w),
|
||||
antibadwords_action: document.getElementById('antibadwords-action').value,
|
||||
antibadwords_warn_message: document.getElementById('antibadwords-warn-message').value,
|
||||
antibadwords_exclude_channels: getMultiSelectValues('antibadwords-exclude-channels'),
|
||||
antibadwords_exclude_roles: getMultiSelectValues('antibadwords-exclude-roles')
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/bot/save-antiraid-config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ guildId, config: configData })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
statusEl.textContent = '✅ Configuration sauvegardée !';
|
||||
statusEl.className = 'status-message success';
|
||||
} else {
|
||||
statusEl.textContent = '❌ ' + (data.error || 'Erreur inconnue');
|
||||
statusEl.className = 'status-message error';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur sauvegarde antiraid:', err);
|
||||
statusEl.textContent = '❌ Erreur de connexion';
|
||||
statusEl.className = 'status-message error';
|
||||
}
|
||||
|
||||
saveBtn.disabled = false;
|
||||
setTimeout(() => {
|
||||
statusEl.textContent = '';
|
||||
statusEl.className = 'status-message';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
document.getElementById('antiraid-enabled').addEventListener('change', updateModulesVisibility);
|
||||
document.getElementById('antiraid-save-btn').addEventListener('click', saveAntiraidConfig);
|
||||
|
||||
// Warnings event listeners
|
||||
document.getElementById('warnings-decay-enabled').addEventListener('change', (e) => {
|
||||
document.getElementById('warnings-decay-options').style.display = e.target.checked ? 'grid' : 'none';
|
||||
});
|
||||
|
||||
// Update duration fields when action changes
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
document.getElementById(`warn${i}-action`).addEventListener('change', updateWarningDurationFields);
|
||||
}
|
||||
|
||||
document.getElementById('warnings-save-btn').addEventListener('click', saveWarningsConfig);
|
||||
|
||||
// Sauvegarder la config warnings
|
||||
async function saveWarningsConfig() {
|
||||
const statusEl = document.getElementById('status-warnings-form');
|
||||
const saveBtn = document.getElementById('warnings-save-btn');
|
||||
|
||||
saveBtn.disabled = true;
|
||||
statusEl.textContent = 'Sauvegarde en cours...';
|
||||
statusEl.className = 'status-message';
|
||||
|
||||
const configData = {
|
||||
enabled: document.getElementById('warnings-enabled').checked,
|
||||
warn1_action: document.getElementById('warn1-action').value,
|
||||
warn1_duration: parseInt(document.getElementById('warn1-duration').value) || 10,
|
||||
warn2_action: document.getElementById('warn2-action').value,
|
||||
warn2_duration: parseInt(document.getElementById('warn2-duration').value) || 30,
|
||||
warn3_action: document.getElementById('warn3-action').value,
|
||||
warn3_duration: parseInt(document.getElementById('warn3-duration').value) || 60,
|
||||
warn4_action: document.getElementById('warn4-action').value,
|
||||
warn4_duration: parseInt(document.getElementById('warn4-duration').value) || 0,
|
||||
warn5_action: document.getElementById('warn5-action').value,
|
||||
warn5_duration: parseInt(document.getElementById('warn5-duration').value) || 0,
|
||||
decay_enabled: document.getElementById('warnings-decay-enabled').checked,
|
||||
decay_days: parseInt(document.getElementById('warnings-decay-days').value) || 30,
|
||||
notify_channel_id: document.getElementById('warnings-notify-channel').value || null
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/bot/save-warnings-config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ guildId, config: configData })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
statusEl.textContent = '✅ Configuration sauvegardée !';
|
||||
statusEl.className = 'status-message success';
|
||||
} else {
|
||||
statusEl.textContent = '❌ ' + (data.error || 'Erreur inconnue');
|
||||
statusEl.className = 'status-message error';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erreur sauvegarde warnings:', err);
|
||||
statusEl.textContent = '❌ Erreur de connexion';
|
||||
statusEl.className = 'status-message error';
|
||||
}
|
||||
|
||||
saveBtn.disabled = false;
|
||||
setTimeout(() => {
|
||||
statusEl.textContent = '';
|
||||
statusEl.className = 'status-message';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Toggle modules body visibility when clicking header
|
||||
document.querySelectorAll('.antiraid-module-header').forEach(header => {
|
||||
header.addEventListener('click', (e) => {
|
||||
// Ne pas toggle si on clique sur le switch
|
||||
if (e.target.closest('.toggle-switch')) return;
|
||||
|
||||
const module = header.closest('.antiraid-module');
|
||||
const body = module.querySelector('.antiraid-module-body');
|
||||
body.style.display = body.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Charger au démarrage
|
||||
loadAntiraidConfig();
|
||||
})();
|
||||
@@ -1597,5 +1597,445 @@ module.exports = (app, db, client) => {
|
||||
}
|
||||
});
|
||||
|
||||
// =============================================
|
||||
// ========== ANTI-RAID CONFIG API =============
|
||||
// =============================================
|
||||
|
||||
// Récupérer la config anti-raid
|
||||
router.get("/bot/get-antiraid-config", async (req, res) => {
|
||||
const { guildId } = req.query;
|
||||
|
||||
if (!req.session.guilds) {
|
||||
return res.status(401).json({ success: false, error: "Non connecté" });
|
||||
}
|
||||
|
||||
const isAdmin = req.session.guilds.find(
|
||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: "Permission refusée" });
|
||||
}
|
||||
|
||||
try {
|
||||
let config = await db.getAsync("SELECT * FROM antiraid_config WHERE guild_id = ?", [guildId]);
|
||||
|
||||
if (!config) {
|
||||
// Créer config par défaut
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run("INSERT INTO antiraid_config (guild_id) VALUES (?)", [guildId], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
config = await db.getAsync("SELECT * FROM antiraid_config WHERE guild_id = ?", [guildId]);
|
||||
}
|
||||
|
||||
// Récupérer les salons et rôles du serveur
|
||||
const guild = client.guilds.cache.get(guildId);
|
||||
const channels = guild ? guild.channels.cache
|
||||
.filter(c => c.type === 0)
|
||||
.map(c => ({ id: c.id, name: c.name })) : [];
|
||||
const roles = guild ? guild.roles.cache
|
||||
.filter(r => r.id !== guild.id && !r.managed)
|
||||
.sort((a, b) => b.position - a.position)
|
||||
.map(r => ({ id: r.id, name: r.name, color: r.hexColor })) : [];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
config,
|
||||
channels,
|
||||
roles
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur get antiraid config:", err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Sauvegarder la config anti-raid
|
||||
router.post("/bot/save-antiraid-config", express.json(), async (req, res) => {
|
||||
const { guildId, config } = req.body;
|
||||
|
||||
if (!req.session.guilds) {
|
||||
return res.status(401).json({ success: false, error: "Non connecté" });
|
||||
}
|
||||
|
||||
const isAdmin = req.session.guilds.find(
|
||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: "Permission refusée" });
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(`
|
||||
INSERT INTO antiraid_config (
|
||||
guild_id, enabled, log_channel_id,
|
||||
antilink_enabled, antilink_action, antilink_whitelist_domains, antilink_exclude_channels, antilink_exclude_roles, antilink_warn_message,
|
||||
antiinvite_enabled, antiinvite_action, antiinvite_allow_own_server, antiinvite_exclude_channels, antiinvite_exclude_roles,
|
||||
antispam_enabled, antispam_max_messages, antispam_interval_seconds, antispam_action, antispam_mute_duration_minutes, antispam_exclude_channels, antispam_exclude_roles,
|
||||
antidupe_enabled, antidupe_max_duplicates, antidupe_interval_seconds, antidupe_action, antidupe_exclude_channels, antidupe_exclude_roles,
|
||||
antimention_enabled, antimention_max_mentions, antimention_action, antimention_exclude_channels, antimention_exclude_roles,
|
||||
antiemoji_enabled, antiemoji_max_emojis, antiemoji_action, antiemoji_exclude_channels, antiemoji_exclude_roles,
|
||||
anticaps_enabled, anticaps_max_percent, anticaps_min_length, anticaps_action, anticaps_exclude_channels, anticaps_exclude_roles,
|
||||
antinewline_enabled, antinewline_max_lines, antinewline_action, antinewline_exclude_channels, antinewline_exclude_roles,
|
||||
antibot_enabled, antibot_min_account_age_days, antibot_no_avatar_action, antibot_suspicious_name_action, antibot_action,
|
||||
antimassj_enabled, antimassj_max_joins, antimassj_interval_seconds, antimassj_action,
|
||||
antibadwords_enabled, antibadwords_action, antibadwords_words, antibadwords_exclude_channels, antibadwords_exclude_roles, antibadwords_warn_message
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(guild_id) DO UPDATE SET
|
||||
enabled = excluded.enabled,
|
||||
log_channel_id = excluded.log_channel_id,
|
||||
antilink_enabled = excluded.antilink_enabled,
|
||||
antilink_action = excluded.antilink_action,
|
||||
antilink_whitelist_domains = excluded.antilink_whitelist_domains,
|
||||
antilink_exclude_channels = excluded.antilink_exclude_channels,
|
||||
antilink_exclude_roles = excluded.antilink_exclude_roles,
|
||||
antilink_warn_message = excluded.antilink_warn_message,
|
||||
antiinvite_enabled = excluded.antiinvite_enabled,
|
||||
antiinvite_action = excluded.antiinvite_action,
|
||||
antiinvite_allow_own_server = excluded.antiinvite_allow_own_server,
|
||||
antiinvite_exclude_channels = excluded.antiinvite_exclude_channels,
|
||||
antiinvite_exclude_roles = excluded.antiinvite_exclude_roles,
|
||||
antispam_enabled = excluded.antispam_enabled,
|
||||
antispam_max_messages = excluded.antispam_max_messages,
|
||||
antispam_interval_seconds = excluded.antispam_interval_seconds,
|
||||
antispam_action = excluded.antispam_action,
|
||||
antispam_mute_duration_minutes = excluded.antispam_mute_duration_minutes,
|
||||
antispam_exclude_channels = excluded.antispam_exclude_channels,
|
||||
antispam_exclude_roles = excluded.antispam_exclude_roles,
|
||||
antidupe_enabled = excluded.antidupe_enabled,
|
||||
antidupe_max_duplicates = excluded.antidupe_max_duplicates,
|
||||
antidupe_interval_seconds = excluded.antidupe_interval_seconds,
|
||||
antidupe_action = excluded.antidupe_action,
|
||||
antidupe_exclude_channels = excluded.antidupe_exclude_channels,
|
||||
antidupe_exclude_roles = excluded.antidupe_exclude_roles,
|
||||
antimention_enabled = excluded.antimention_enabled,
|
||||
antimention_max_mentions = excluded.antimention_max_mentions,
|
||||
antimention_action = excluded.antimention_action,
|
||||
antimention_exclude_channels = excluded.antimention_exclude_channels,
|
||||
antimention_exclude_roles = excluded.antimention_exclude_roles,
|
||||
antiemoji_enabled = excluded.antiemoji_enabled,
|
||||
antiemoji_max_emojis = excluded.antiemoji_max_emojis,
|
||||
antiemoji_action = excluded.antiemoji_action,
|
||||
antiemoji_exclude_channels = excluded.antiemoji_exclude_channels,
|
||||
antiemoji_exclude_roles = excluded.antiemoji_exclude_roles,
|
||||
anticaps_enabled = excluded.anticaps_enabled,
|
||||
anticaps_max_percent = excluded.anticaps_max_percent,
|
||||
anticaps_min_length = excluded.anticaps_min_length,
|
||||
anticaps_action = excluded.anticaps_action,
|
||||
anticaps_exclude_channels = excluded.anticaps_exclude_channels,
|
||||
anticaps_exclude_roles = excluded.anticaps_exclude_roles,
|
||||
antinewline_enabled = excluded.antinewline_enabled,
|
||||
antinewline_max_lines = excluded.antinewline_max_lines,
|
||||
antinewline_action = excluded.antinewline_action,
|
||||
antinewline_exclude_channels = excluded.antinewline_exclude_channels,
|
||||
antinewline_exclude_roles = excluded.antinewline_exclude_roles,
|
||||
antibot_enabled = excluded.antibot_enabled,
|
||||
antibot_min_account_age_days = excluded.antibot_min_account_age_days,
|
||||
antibot_no_avatar_action = excluded.antibot_no_avatar_action,
|
||||
antibot_suspicious_name_action = excluded.antibot_suspicious_name_action,
|
||||
antibot_action = excluded.antibot_action,
|
||||
antimassj_enabled = excluded.antimassj_enabled,
|
||||
antimassj_max_joins = excluded.antimassj_max_joins,
|
||||
antimassj_interval_seconds = excluded.antimassj_interval_seconds,
|
||||
antimassj_action = excluded.antimassj_action,
|
||||
antibadwords_enabled = excluded.antibadwords_enabled,
|
||||
antibadwords_action = excluded.antibadwords_action,
|
||||
antibadwords_words = excluded.antibadwords_words,
|
||||
antibadwords_exclude_channels = excluded.antibadwords_exclude_channels,
|
||||
antibadwords_exclude_roles = excluded.antibadwords_exclude_roles,
|
||||
antibadwords_warn_message = excluded.antibadwords_warn_message
|
||||
`, [
|
||||
guildId,
|
||||
config.enabled ? 1 : 0,
|
||||
config.log_channel_id || null,
|
||||
// Anti-Link
|
||||
config.antilink_enabled ? 1 : 0,
|
||||
config.antilink_action || 'delete',
|
||||
JSON.stringify(config.antilink_whitelist_domains || []),
|
||||
JSON.stringify(config.antilink_exclude_channels || []),
|
||||
JSON.stringify(config.antilink_exclude_roles || []),
|
||||
config.antilink_warn_message || '⚠️ Les liens ne sont pas autorisés ici.',
|
||||
// Anti-Invite
|
||||
config.antiinvite_enabled ? 1 : 0,
|
||||
config.antiinvite_action || 'delete',
|
||||
config.antiinvite_allow_own_server ? 1 : 0,
|
||||
JSON.stringify(config.antiinvite_exclude_channels || []),
|
||||
JSON.stringify(config.antiinvite_exclude_roles || []),
|
||||
// Anti-Spam
|
||||
config.antispam_enabled ? 1 : 0,
|
||||
config.antispam_max_messages || 5,
|
||||
config.antispam_interval_seconds || 5,
|
||||
config.antispam_action || 'mute',
|
||||
config.antispam_mute_duration_minutes || 10,
|
||||
JSON.stringify(config.antispam_exclude_channels || []),
|
||||
JSON.stringify(config.antispam_exclude_roles || []),
|
||||
// Anti-Duplicate
|
||||
config.antidupe_enabled ? 1 : 0,
|
||||
config.antidupe_max_duplicates || 3,
|
||||
config.antidupe_interval_seconds || 30,
|
||||
config.antidupe_action || 'delete',
|
||||
JSON.stringify(config.antidupe_exclude_channels || []),
|
||||
JSON.stringify(config.antidupe_exclude_roles || []),
|
||||
// Anti-Mention
|
||||
config.antimention_enabled ? 1 : 0,
|
||||
config.antimention_max_mentions || 5,
|
||||
config.antimention_action || 'delete',
|
||||
JSON.stringify(config.antimention_exclude_channels || []),
|
||||
JSON.stringify(config.antimention_exclude_roles || []),
|
||||
// Anti-Emoji
|
||||
config.antiemoji_enabled ? 1 : 0,
|
||||
config.antiemoji_max_emojis || 10,
|
||||
config.antiemoji_action || 'delete',
|
||||
JSON.stringify(config.antiemoji_exclude_channels || []),
|
||||
JSON.stringify(config.antiemoji_exclude_roles || []),
|
||||
// Anti-Caps
|
||||
config.anticaps_enabled ? 1 : 0,
|
||||
config.anticaps_max_percent || 70,
|
||||
config.anticaps_min_length || 10,
|
||||
config.anticaps_action || 'delete',
|
||||
JSON.stringify(config.anticaps_exclude_channels || []),
|
||||
JSON.stringify(config.anticaps_exclude_roles || []),
|
||||
// Anti-Newline
|
||||
config.antinewline_enabled ? 1 : 0,
|
||||
config.antinewline_max_lines || 15,
|
||||
config.antinewline_action || 'delete',
|
||||
JSON.stringify(config.antinewline_exclude_channels || []),
|
||||
JSON.stringify(config.antinewline_exclude_roles || []),
|
||||
// Anti-Bot
|
||||
config.antibot_enabled ? 1 : 0,
|
||||
config.antibot_min_account_age_days || 7,
|
||||
config.antibot_no_avatar_action ? 1 : 0,
|
||||
config.antibot_suspicious_name_action ? 1 : 0,
|
||||
config.antibot_action || 'kick',
|
||||
// Anti-Mass Join
|
||||
config.antimassj_enabled ? 1 : 0,
|
||||
config.antimassj_max_joins || 10,
|
||||
config.antimassj_interval_seconds || 10,
|
||||
config.antimassj_action || 'kick',
|
||||
// Anti-Badwords
|
||||
config.antibadwords_enabled ? 1 : 0,
|
||||
config.antibadwords_action || 'delete',
|
||||
JSON.stringify(config.antibadwords_words || []),
|
||||
JSON.stringify(config.antibadwords_exclude_channels || []),
|
||||
JSON.stringify(config.antibadwords_exclude_roles || []),
|
||||
config.antibadwords_warn_message || '⚠️ Les insultes et gros mots ne sont pas autorisés.'
|
||||
], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
|
||||
res.json({ success: true });
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur save antiraid config:", err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// =============================================
|
||||
// ========== WARNINGS CONFIG API ==============
|
||||
// =============================================
|
||||
|
||||
// Récupérer la config des warnings
|
||||
router.get("/bot/get-warnings-config", async (req, res) => {
|
||||
const { guildId } = req.query;
|
||||
|
||||
if (!req.session.guilds) {
|
||||
return res.status(401).json({ success: false, error: "Non connecté" });
|
||||
}
|
||||
|
||||
const isAdmin = req.session.guilds.find(
|
||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: "Permission refusée" });
|
||||
}
|
||||
|
||||
try {
|
||||
let config = await db.getAsync("SELECT * FROM warnings_config WHERE guild_id = ?", [guildId]);
|
||||
|
||||
if (!config) {
|
||||
// Créer config par défaut
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run("INSERT INTO warnings_config (guild_id) VALUES (?)", [guildId], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
config = await db.getAsync("SELECT * FROM warnings_config WHERE guild_id = ?", [guildId]);
|
||||
}
|
||||
|
||||
// Récupérer les salons du serveur
|
||||
const guild = client.guilds.cache.get(guildId);
|
||||
const channels = guild ? guild.channels.cache
|
||||
.filter(c => c.type === 0)
|
||||
.map(c => ({ id: c.id, name: c.name })) : [];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
config,
|
||||
channels
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur get warnings config:", err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Sauvegarder la config des warnings
|
||||
router.post("/bot/save-warnings-config", express.json(), async (req, res) => {
|
||||
const { guildId, config } = req.body;
|
||||
|
||||
if (!req.session.guilds) {
|
||||
return res.status(401).json({ success: false, error: "Non connecté" });
|
||||
}
|
||||
|
||||
const isAdmin = req.session.guilds.find(
|
||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: "Permission refusée" });
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run(`
|
||||
INSERT INTO warnings_config (
|
||||
guild_id, enabled,
|
||||
warn1_action, warn1_duration,
|
||||
warn2_action, warn2_duration,
|
||||
warn3_action, warn3_duration,
|
||||
warn4_action, warn4_duration,
|
||||
warn5_action, warn5_duration,
|
||||
decay_enabled, decay_days,
|
||||
notify_user, notify_channel_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(guild_id) DO UPDATE SET
|
||||
enabled = excluded.enabled,
|
||||
warn1_action = excluded.warn1_action,
|
||||
warn1_duration = excluded.warn1_duration,
|
||||
warn2_action = excluded.warn2_action,
|
||||
warn2_duration = excluded.warn2_duration,
|
||||
warn3_action = excluded.warn3_action,
|
||||
warn3_duration = excluded.warn3_duration,
|
||||
warn4_action = excluded.warn4_action,
|
||||
warn4_duration = excluded.warn4_duration,
|
||||
warn5_action = excluded.warn5_action,
|
||||
warn5_duration = excluded.warn5_duration,
|
||||
decay_enabled = excluded.decay_enabled,
|
||||
decay_days = excluded.decay_days,
|
||||
notify_user = excluded.notify_user,
|
||||
notify_channel_id = excluded.notify_channel_id
|
||||
`, [
|
||||
guildId,
|
||||
config.enabled ? 1 : 0,
|
||||
config.warn1_action || 'none',
|
||||
config.warn1_duration || 10,
|
||||
config.warn2_action || 'none',
|
||||
config.warn2_duration || 30,
|
||||
config.warn3_action || 'mute',
|
||||
config.warn3_duration || 60,
|
||||
config.warn4_action || 'kick',
|
||||
config.warn4_duration || 0,
|
||||
config.warn5_action || 'ban',
|
||||
config.warn5_duration || 0,
|
||||
config.decay_enabled ? 1 : 0,
|
||||
config.decay_days || 30,
|
||||
config.notify_user ? 1 : 0,
|
||||
config.notify_channel_id || null
|
||||
], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
|
||||
res.json({ success: true });
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur save warnings config:", err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Récupérer les warnings d'un serveur
|
||||
router.get("/bot/get-warnings-list", async (req, res) => {
|
||||
const { guildId, userId } = req.query;
|
||||
|
||||
if (!req.session.guilds) {
|
||||
return res.status(401).json({ success: false, error: "Non connecté" });
|
||||
}
|
||||
|
||||
const isAdmin = req.session.guilds.find(
|
||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: "Permission refusée" });
|
||||
}
|
||||
|
||||
try {
|
||||
let warnings;
|
||||
if (userId) {
|
||||
warnings = await db.allAsync(
|
||||
"SELECT * FROM warnings WHERE guild_id = ? AND user_id = ? ORDER BY created_at DESC LIMIT 50",
|
||||
[guildId, userId]
|
||||
);
|
||||
} else {
|
||||
warnings = await db.allAsync(
|
||||
"SELECT * FROM warnings WHERE guild_id = ? ORDER BY created_at DESC LIMIT 100",
|
||||
[guildId]
|
||||
);
|
||||
}
|
||||
|
||||
res.json({ success: true, warnings: warnings || [] });
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur get warnings list:", err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Supprimer un warning
|
||||
router.post("/bot/delete-warning", express.json(), async (req, res) => {
|
||||
const { guildId, warnId } = req.body;
|
||||
|
||||
if (!req.session.guilds) {
|
||||
return res.status(401).json({ success: false, error: "Non connecté" });
|
||||
}
|
||||
|
||||
const isAdmin = req.session.guilds.find(
|
||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return res.status(403).json({ success: false, error: "Permission refusée" });
|
||||
}
|
||||
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
db.run("DELETE FROM warnings WHERE id = ? AND guild_id = ?", [warnId, guildId], function(err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
|
||||
res.json({ success: true });
|
||||
|
||||
} catch (err) {
|
||||
console.error("Erreur delete warning:", err);
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.use("/api", router);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user