Introduces new commands like `/automod`, `/giveaway`, `/rolepanel`, and `/schedule`. Enhances existing commands such as `/announce`, `/help`, `/leaderboard`, `/profile`, and `/serverinfo` with new features and improved embed designs. Updates welcome and goodbye listeners with rich embeds. Fixes a critical issue in the `/rolepanel` command regarding channel fetching. Adds interaction handling for role buttons and giveaway entries. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: eefee140-1301-4b6f-9439-2b0b883aa40a Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/qAaysIh Replit-Helium-Checkpoint-Created: true
424 lines
13 KiB
JavaScript
424 lines
13 KiB
JavaScript
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] });
|
|
}
|