const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('automod') .setDescription('Configure auto-moderation settings') .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild) .addSubcommand(sub => sub.setName('status') .setDescription('View current auto-mod settings') ) .addSubcommand(sub => sub.setName('links') .setDescription('Toggle link filtering') .addBooleanOption(option => option.setName('enabled') .setDescription('Enable or disable link filtering') .setRequired(true) ) .addStringOption(option => option.setName('action') .setDescription('Action to take') .setRequired(false) .addChoices( { name: 'Delete only', value: 'delete' }, { name: 'Delete + Warn', value: 'warn' }, { name: 'Delete + Timeout (5min)', value: 'timeout' } ) ) ) .addSubcommand(sub => sub.setName('spam') .setDescription('Toggle spam detection') .addBooleanOption(option => option.setName('enabled') .setDescription('Enable or disable spam detection') .setRequired(true) ) .addIntegerOption(option => option.setName('threshold') .setDescription('Messages per 5 seconds to trigger') .setRequired(false) .setMinValue(3) .setMaxValue(20) ) ) .addSubcommand(sub => sub.setName('badwords') .setDescription('Manage banned words') .addStringOption(option => option.setName('action') .setDescription('Add or remove words') .setRequired(true) .addChoices( { name: 'Add word', value: 'add' }, { name: 'Remove word', value: 'remove' }, { name: 'List words', value: 'list' }, { name: 'Clear all', value: 'clear' } ) ) .addStringOption(option => option.setName('word') .setDescription('Word to add/remove') .setRequired(false) ) ) .addSubcommand(sub => sub.setName('invites') .setDescription('Toggle Discord invite filtering') .addBooleanOption(option => option.setName('enabled') .setDescription('Enable or disable invite filtering') .setRequired(true) ) ) .addSubcommand(sub => sub.setName('mentions') .setDescription('Toggle mass mention detection') .addBooleanOption(option => option.setName('enabled') .setDescription('Enable or disable mention spam detection') .setRequired(true) ) .addIntegerOption(option => option.setName('limit') .setDescription('Maximum mentions per message') .setRequired(false) .setMinValue(3) .setMaxValue(50) ) ) .addSubcommand(sub => sub.setName('exempt') .setDescription('Exempt a role from auto-mod') .addRoleOption(option => option.setName('role') .setDescription('Role to exempt') .setRequired(true) ) .addBooleanOption(option => option.setName('exempt') .setDescription('Exempt or un-exempt') .setRequired(true) ) ), async execute(interaction, supabase, client) { const subcommand = interaction.options.getSubcommand(); switch (subcommand) { case 'status': await handleStatus(interaction, supabase); break; case 'links': await handleLinks(interaction, supabase, client); break; case 'spam': await handleSpam(interaction, supabase, client); break; case 'badwords': await handleBadwords(interaction, supabase, client); break; case 'invites': await handleInvites(interaction, supabase, client); break; case 'mentions': await handleMentions(interaction, supabase, client); break; case 'exempt': await handleExempt(interaction, supabase, client); break; } }, }; async function getAutomodConfig(guildId, supabase) { if (!supabase) return getDefaultConfig(); try { const { data, error } = await supabase .from('automod_config') .select('*') .eq('guild_id', guildId) .single(); if (error || !data) return getDefaultConfig(); return data; } catch { return getDefaultConfig(); } } function getDefaultConfig() { return { links_enabled: false, links_action: 'delete', spam_enabled: false, spam_threshold: 5, badwords: [], invites_enabled: false, mentions_enabled: false, mentions_limit: 5, exempt_roles: [] }; } async function saveAutomodConfig(guildId, config, supabase) { if (!supabase) return; try { await supabase.from('automod_config').upsert({ guild_id: guildId, ...config, updated_at: new Date().toISOString() }); } catch (error) { console.error('Save automod config error:', error); } } async function handleStatus(interaction, supabase) { await interaction.deferReply({ ephemeral: true }); const config = await getAutomodConfig(interaction.guildId, supabase); const embed = new EmbedBuilder() .setColor(0x7c3aed) .setTitle('🛡️ Auto-Moderation Settings') .addFields( { name: '🔗 Link Filter', value: config.links_enabled ? `✅ Enabled (${config.links_action})` : '❌ Disabled', inline: true }, { name: '📨 Spam Detection', value: config.spam_enabled ? `✅ Enabled (${config.spam_threshold} msg/5s)` : '❌ Disabled', inline: true }, { name: '🚫 Bad Words', value: config.badwords?.length > 0 ? `✅ ${config.badwords.length} words` : '❌ None set', inline: true }, { name: '📩 Invite Filter', value: config.invites_enabled ? '✅ Enabled' : '❌ Disabled', inline: true }, { name: '📢 Mass Mentions', value: config.mentions_enabled ? `✅ Enabled (max ${config.mentions_limit})` : '❌ Disabled', inline: true }, { name: '🎭 Exempt Roles', value: config.exempt_roles?.length > 0 ? config.exempt_roles.map(r => `<@&${r}>`).join(', ') : 'None', inline: true } ) .setFooter({ text: 'Use /automod [setting] to configure' }) .setTimestamp(); await interaction.editReply({ embeds: [embed] }); } async function handleLinks(interaction, supabase, client) { await interaction.deferReply({ ephemeral: true }); const enabled = interaction.options.getBoolean('enabled'); const action = interaction.options.getString('action') || 'delete'; const config = await getAutomodConfig(interaction.guildId, supabase); config.links_enabled = enabled; config.links_action = action; await saveAutomodConfig(interaction.guildId, config, supabase); client.automodConfig = client.automodConfig || new Map(); client.automodConfig.set(interaction.guildId, config); const embed = new EmbedBuilder() .setColor(enabled ? 0x22c55e : 0xef4444) .setTitle(enabled ? '✅ Link Filter Enabled' : '❌ Link Filter Disabled') .setDescription(enabled ? `Links will be ${action === 'delete' ? 'deleted' : action === 'warn' ? 'deleted and user warned' : 'deleted and user timed out'}` : 'Links will no longer be filtered') .setTimestamp(); await interaction.editReply({ embeds: [embed] }); } async function handleSpam(interaction, supabase, client) { await interaction.deferReply({ ephemeral: true }); const enabled = interaction.options.getBoolean('enabled'); const threshold = interaction.options.getInteger('threshold') || 5; const config = await getAutomodConfig(interaction.guildId, supabase); config.spam_enabled = enabled; config.spam_threshold = threshold; await saveAutomodConfig(interaction.guildId, config, supabase); client.automodConfig = client.automodConfig || new Map(); client.automodConfig.set(interaction.guildId, config); const embed = new EmbedBuilder() .setColor(enabled ? 0x22c55e : 0xef4444) .setTitle(enabled ? '✅ Spam Detection Enabled' : '❌ Spam Detection Disabled') .setDescription(enabled ? `Spam threshold set to ${threshold} messages per 5 seconds` : 'Spam will no longer be detected') .setTimestamp(); await interaction.editReply({ embeds: [embed] }); } async function handleBadwords(interaction, supabase, client) { await interaction.deferReply({ ephemeral: true }); const action = interaction.options.getString('action'); const word = interaction.options.getString('word')?.toLowerCase(); const config = await getAutomodConfig(interaction.guildId, supabase); config.badwords = config.badwords || []; let embed; switch (action) { case 'add': if (!word) { return interaction.editReply({ content: 'Please provide a word to add.' }); } if (!config.badwords.includes(word)) { config.badwords.push(word); } embed = new EmbedBuilder() .setColor(0x22c55e) .setTitle('✅ Word Added') .setDescription(`Added "${word}" to the filter list.`) .setTimestamp(); break; case 'remove': if (!word) { return interaction.editReply({ content: 'Please provide a word to remove.' }); } config.badwords = config.badwords.filter(w => w !== word); embed = new EmbedBuilder() .setColor(0x22c55e) .setTitle('✅ Word Removed') .setDescription(`Removed "${word}" from the filter list.`) .setTimestamp(); break; case 'list': embed = new EmbedBuilder() .setColor(0x7c3aed) .setTitle('🚫 Banned Words') .setDescription(config.badwords.length > 0 ? config.badwords.map(w => `\`${w}\``).join(', ') : 'No words in the filter list.') .setTimestamp(); break; case 'clear': config.badwords = []; embed = new EmbedBuilder() .setColor(0x22c55e) .setTitle('✅ List Cleared') .setDescription('All banned words have been removed.') .setTimestamp(); break; } await saveAutomodConfig(interaction.guildId, config, supabase); client.automodConfig = client.automodConfig || new Map(); client.automodConfig.set(interaction.guildId, config); await interaction.editReply({ embeds: [embed] }); } async function handleInvites(interaction, supabase, client) { await interaction.deferReply({ ephemeral: true }); const enabled = interaction.options.getBoolean('enabled'); const config = await getAutomodConfig(interaction.guildId, supabase); config.invites_enabled = enabled; await saveAutomodConfig(interaction.guildId, config, supabase); client.automodConfig = client.automodConfig || new Map(); client.automodConfig.set(interaction.guildId, config); const embed = new EmbedBuilder() .setColor(enabled ? 0x22c55e : 0xef4444) .setTitle(enabled ? '✅ Invite Filter Enabled' : '❌ Invite Filter Disabled') .setDescription(enabled ? 'Discord invites will be automatically deleted' : 'Discord invites will no longer be filtered') .setTimestamp(); await interaction.editReply({ embeds: [embed] }); } async function handleMentions(interaction, supabase, client) { await interaction.deferReply({ ephemeral: true }); const enabled = interaction.options.getBoolean('enabled'); const limit = interaction.options.getInteger('limit') || 5; const config = await getAutomodConfig(interaction.guildId, supabase); config.mentions_enabled = enabled; config.mentions_limit = limit; await saveAutomodConfig(interaction.guildId, config, supabase); client.automodConfig = client.automodConfig || new Map(); client.automodConfig.set(interaction.guildId, config); const embed = new EmbedBuilder() .setColor(enabled ? 0x22c55e : 0xef4444) .setTitle(enabled ? '✅ Mass Mention Detection Enabled' : '❌ Mass Mention Detection Disabled') .setDescription(enabled ? `Messages with more than ${limit} mentions will be deleted` : 'Mass mentions will no longer be filtered') .setTimestamp(); await interaction.editReply({ embeds: [embed] }); } async function handleExempt(interaction, supabase, client) { await interaction.deferReply({ ephemeral: true }); const role = interaction.options.getRole('role'); const exempt = interaction.options.getBoolean('exempt'); const config = await getAutomodConfig(interaction.guildId, supabase); config.exempt_roles = config.exempt_roles || []; if (exempt) { if (!config.exempt_roles.includes(role.id)) { config.exempt_roles.push(role.id); } } else { config.exempt_roles = config.exempt_roles.filter(r => r !== role.id); } await saveAutomodConfig(interaction.guildId, config, supabase); client.automodConfig = client.automodConfig || new Map(); client.automodConfig.set(interaction.guildId, config); const embed = new EmbedBuilder() .setColor(0x22c55e) .setTitle(exempt ? '✅ Role Exempted' : '✅ Role Un-exempted') .setDescription(exempt ? `${role} is now exempt from auto-moderation` : `${role} is no longer exempt from auto-moderation`) .setTimestamp(); await interaction.editReply({ embeds: [embed] }); }