Adds new subcommands to `xp-settings.js` for configuring level-up channel, message, DM notifications, and embed usage. Updates `xpTracker.js` to use these configurations, including placeholder support and embed customization, and introduces a `sendLevelUpAnnouncement` function. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: df3297ab-2de4-4d8a-99e3-9f5a31ff4ac6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/yTaZipL Replit-Helium-Checkpoint-Created: true
806 lines
27 KiB
JavaScript
806 lines
27 KiB
JavaScript
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('xp-settings')
|
|
.setDescription('Configure XP settings for your server')
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
|
.addSubcommand(sub =>
|
|
sub.setName('view')
|
|
.setDescription('View current XP settings'))
|
|
.addSubcommand(sub =>
|
|
sub.setName('message-xp')
|
|
.setDescription('Set XP earned per message')
|
|
.addIntegerOption(opt =>
|
|
opt.setName('amount')
|
|
.setDescription('XP per message (1-50)')
|
|
.setRequired(true)
|
|
.setMinValue(1)
|
|
.setMaxValue(50)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('cooldown')
|
|
.setDescription('Set cooldown between XP gains (seconds)')
|
|
.addIntegerOption(opt =>
|
|
opt.setName('seconds')
|
|
.setDescription('Cooldown in seconds (10-300)')
|
|
.setRequired(true)
|
|
.setMinValue(10)
|
|
.setMaxValue(300)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('multiplier-role')
|
|
.setDescription('Add/remove a role with XP multiplier')
|
|
.addRoleOption(opt =>
|
|
opt.setName('role')
|
|
.setDescription('The role to add/remove multiplier')
|
|
.setRequired(true))
|
|
.addNumberOption(opt =>
|
|
opt.setName('multiplier')
|
|
.setDescription('XP multiplier (1.1-5.0, or 0 to remove)')
|
|
.setRequired(true)
|
|
.setMinValue(0)
|
|
.setMaxValue(5)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('bonus-channel')
|
|
.setDescription('Add/remove a channel with bonus XP')
|
|
.addChannelOption(opt =>
|
|
opt.setName('channel')
|
|
.setDescription('The channel to add/remove bonus')
|
|
.setRequired(true))
|
|
.addNumberOption(opt =>
|
|
opt.setName('multiplier')
|
|
.setDescription('XP multiplier (1.1-5.0, or 0 to remove)')
|
|
.setRequired(true)
|
|
.setMinValue(0)
|
|
.setMaxValue(5)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('toggle')
|
|
.setDescription('Enable or disable XP system')
|
|
.addBooleanOption(opt =>
|
|
opt.setName('enabled')
|
|
.setDescription('Enable or disable XP')
|
|
.setRequired(true)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('level-curve')
|
|
.setDescription('Set leveling difficulty curve')
|
|
.addStringOption(opt =>
|
|
opt.setName('curve')
|
|
.setDescription('Leveling curve type')
|
|
.setRequired(true)
|
|
.addChoices(
|
|
{ name: 'Easy - Faster leveling', value: 'easy' },
|
|
{ name: 'Normal - Standard leveling', value: 'normal' },
|
|
{ name: 'Hard - Slower leveling', value: 'hard' }
|
|
)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('reaction-xp')
|
|
.setDescription('Configure XP for reactions')
|
|
.addIntegerOption(opt =>
|
|
opt.setName('received')
|
|
.setDescription('XP for receiving a reaction (0-20)')
|
|
.setRequired(true)
|
|
.setMinValue(0)
|
|
.setMaxValue(20))
|
|
.addIntegerOption(opt =>
|
|
opt.setName('given')
|
|
.setDescription('XP for giving a reaction (0-10)')
|
|
.setRequired(true)
|
|
.setMinValue(0)
|
|
.setMaxValue(10)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('reaction-cooldown')
|
|
.setDescription('Set cooldown between reaction XP gains')
|
|
.addIntegerOption(opt =>
|
|
opt.setName('seconds')
|
|
.setDescription('Cooldown in seconds (5-120)')
|
|
.setRequired(true)
|
|
.setMinValue(5)
|
|
.setMaxValue(120)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('reaction-toggle')
|
|
.setDescription('Enable or disable reaction XP')
|
|
.addBooleanOption(opt =>
|
|
opt.setName('enabled')
|
|
.setDescription('Enable or disable reaction XP')
|
|
.setRequired(true)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('voice-xp')
|
|
.setDescription('Set XP earned per minute in voice channels')
|
|
.addIntegerOption(opt =>
|
|
opt.setName('amount')
|
|
.setDescription('XP per minute (1-20)')
|
|
.setRequired(true)
|
|
.setMinValue(1)
|
|
.setMaxValue(20)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('voice-cooldown')
|
|
.setDescription('Set cooldown between voice XP grants')
|
|
.addIntegerOption(opt =>
|
|
opt.setName('seconds')
|
|
.setDescription('Cooldown in seconds (30-300)')
|
|
.setRequired(true)
|
|
.setMinValue(30)
|
|
.setMaxValue(300)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('voice-toggle')
|
|
.setDescription('Enable or disable voice XP')
|
|
.addBooleanOption(opt =>
|
|
opt.setName('enabled')
|
|
.setDescription('Enable or disable voice XP')
|
|
.setRequired(true)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('levelup-channel')
|
|
.setDescription('Set a dedicated channel for level-up announcements')
|
|
.addChannelOption(opt =>
|
|
opt.setName('channel')
|
|
.setDescription('Channel for level-ups (leave empty to use current channel)')
|
|
.setRequired(false)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('levelup-message')
|
|
.setDescription('Set custom level-up message (use {user}, {level}, {xp}, {server})')
|
|
.addStringOption(opt =>
|
|
opt.setName('message')
|
|
.setDescription('Custom message with placeholders')
|
|
.setRequired(true)
|
|
.setMaxLength(500)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('levelup-dm')
|
|
.setDescription('Toggle DM notifications for level-ups instead of channel')
|
|
.addBooleanOption(opt =>
|
|
opt.setName('enabled')
|
|
.setDescription('Send level-up via DM')
|
|
.setRequired(true)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('levelup-embed')
|
|
.setDescription('Toggle using embeds for level-up announcements')
|
|
.addBooleanOption(opt =>
|
|
opt.setName('enabled')
|
|
.setDescription('Use embed for level-ups')
|
|
.setRequired(true))
|
|
.addStringOption(opt =>
|
|
opt.setName('color')
|
|
.setDescription('Embed color (hex, e.g., #5865F2)')
|
|
.setRequired(false)))
|
|
.addSubcommand(sub =>
|
|
sub.setName('levelup-reset')
|
|
.setDescription('Reset level-up settings to defaults')),
|
|
|
|
async execute(interaction, client, supabase) {
|
|
if (!supabase) {
|
|
return interaction.reply({
|
|
content: '❌ Database not configured. XP settings require Supabase.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
const guildId = interaction.guildId;
|
|
const subcommand = interaction.options.getSubcommand();
|
|
|
|
// Get current config or create default
|
|
let config = await getXpConfig(supabase, guildId);
|
|
|
|
switch (subcommand) {
|
|
case 'view':
|
|
return handleView(interaction, config);
|
|
case 'message-xp':
|
|
return handleMessageXp(interaction, supabase, guildId, config);
|
|
case 'cooldown':
|
|
return handleCooldown(interaction, supabase, guildId, config);
|
|
case 'multiplier-role':
|
|
return handleMultiplierRole(interaction, supabase, guildId, config);
|
|
case 'bonus-channel':
|
|
return handleBonusChannel(interaction, supabase, guildId, config);
|
|
case 'toggle':
|
|
return handleToggle(interaction, supabase, guildId, config);
|
|
case 'level-curve':
|
|
return handleLevelCurve(interaction, supabase, guildId, config);
|
|
case 'reaction-xp':
|
|
return handleReactionXp(interaction, supabase, guildId, config);
|
|
case 'reaction-cooldown':
|
|
return handleReactionCooldown(interaction, supabase, guildId, config);
|
|
case 'reaction-toggle':
|
|
return handleReactionToggle(interaction, supabase, guildId, config);
|
|
case 'voice-xp':
|
|
return handleVoiceXp(interaction, supabase, guildId, config);
|
|
case 'voice-cooldown':
|
|
return handleVoiceCooldown(interaction, supabase, guildId, config);
|
|
case 'voice-toggle':
|
|
return handleVoiceToggle(interaction, supabase, guildId, config);
|
|
case 'levelup-channel':
|
|
return handleLevelupChannel(interaction, supabase, guildId, config);
|
|
case 'levelup-message':
|
|
return handleLevelupMessage(interaction, supabase, guildId, config);
|
|
case 'levelup-dm':
|
|
return handleLevelupDm(interaction, supabase, guildId, config);
|
|
case 'levelup-embed':
|
|
return handleLevelupEmbed(interaction, supabase, guildId, config);
|
|
case 'levelup-reset':
|
|
return handleLevelupReset(interaction, supabase, guildId, config);
|
|
}
|
|
}
|
|
};
|
|
|
|
async function getXpConfig(supabase, guildId) {
|
|
try {
|
|
const { data, error } = await supabase
|
|
.from('xp_config')
|
|
.select('*')
|
|
.eq('guild_id', guildId)
|
|
.maybeSingle();
|
|
|
|
if (error) throw error;
|
|
|
|
if (!data) {
|
|
// Return default config
|
|
return {
|
|
guild_id: guildId,
|
|
message_xp: 5,
|
|
message_cooldown: 60,
|
|
multiplier_roles: [],
|
|
bonus_channels: [],
|
|
level_curve: 'normal',
|
|
xp_enabled: true,
|
|
reaction_xp_enabled: true,
|
|
reaction_xp_received: 3,
|
|
reaction_xp_given: 1,
|
|
reaction_cooldown: 30,
|
|
voice_xp_enabled: true,
|
|
voice_xp: 2,
|
|
voice_cooldown: 60,
|
|
levelup_channel_id: null,
|
|
levelup_message: '🎉 Congratulations {user}! You reached **Level {level}**!',
|
|
levelup_dm: false,
|
|
levelup_embed: false,
|
|
levelup_embed_color: '#5865F2'
|
|
};
|
|
}
|
|
|
|
return data;
|
|
} catch (e) {
|
|
console.error('Failed to get XP config:', e.message);
|
|
return {
|
|
guild_id: guildId,
|
|
message_xp: 5,
|
|
message_cooldown: 60,
|
|
multiplier_roles: [],
|
|
bonus_channels: [],
|
|
level_curve: 'normal',
|
|
xp_enabled: true,
|
|
reaction_xp_enabled: true,
|
|
reaction_xp_received: 3,
|
|
reaction_xp_given: 1,
|
|
reaction_cooldown: 30,
|
|
voice_xp_enabled: true,
|
|
voice_xp: 2,
|
|
voice_cooldown: 60,
|
|
levelup_channel_id: null,
|
|
levelup_message: '🎉 Congratulations {user}! You reached **Level {level}**!',
|
|
levelup_dm: false,
|
|
levelup_embed: false,
|
|
levelup_embed_color: '#5865F2'
|
|
};
|
|
}
|
|
}
|
|
|
|
async function saveXpConfig(supabase, guildId, config) {
|
|
try {
|
|
const { error } = await supabase
|
|
.from('xp_config')
|
|
.upsert({
|
|
guild_id: guildId,
|
|
message_xp: config.message_xp,
|
|
message_cooldown: config.message_cooldown,
|
|
multiplier_roles: config.multiplier_roles,
|
|
bonus_channels: config.bonus_channels,
|
|
level_curve: config.level_curve,
|
|
xp_enabled: config.xp_enabled,
|
|
reaction_xp_enabled: config.reaction_xp_enabled,
|
|
reaction_xp_received: config.reaction_xp_received,
|
|
reaction_xp_given: config.reaction_xp_given,
|
|
reaction_cooldown: config.reaction_cooldown,
|
|
voice_xp_enabled: config.voice_xp_enabled,
|
|
voice_xp: config.voice_xp,
|
|
voice_cooldown: config.voice_cooldown,
|
|
levelup_channel_id: config.levelup_channel_id,
|
|
levelup_message: config.levelup_message,
|
|
levelup_dm: config.levelup_dm,
|
|
levelup_embed: config.levelup_embed,
|
|
levelup_embed_color: config.levelup_embed_color,
|
|
updated_at: new Date().toISOString()
|
|
});
|
|
|
|
if (error) throw error;
|
|
return true;
|
|
} catch (e) {
|
|
console.error('Failed to save XP config:', e.message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function handleView(interaction, config) {
|
|
const multiplierRoles = config.multiplier_roles || [];
|
|
const bonusChannels = config.bonus_channels || [];
|
|
|
|
const rolesText = multiplierRoles.length > 0
|
|
? multiplierRoles.map(r => `<@&${r.role_id}> → ${r.multiplier}x`).join('\n')
|
|
: 'None configured';
|
|
|
|
const channelsText = bonusChannels.length > 0
|
|
? bonusChannels.map(c => `<#${c.channel_id}> → ${c.multiplier}x`).join('\n')
|
|
: 'None configured';
|
|
|
|
const curveInfo = {
|
|
easy: 'Easy (50 XP per level²)',
|
|
normal: 'Normal (100 XP per level²)',
|
|
hard: 'Hard (200 XP per level²)'
|
|
};
|
|
|
|
const reactionEnabled = config.reaction_xp_enabled !== false;
|
|
const reactionReceived = config.reaction_xp_received ?? 3;
|
|
const reactionGiven = config.reaction_xp_given ?? 1;
|
|
const reactionCooldown = config.reaction_cooldown ?? 30;
|
|
|
|
const voiceEnabled = config.voice_xp_enabled !== false;
|
|
const voiceXp = config.voice_xp ?? 2;
|
|
const voiceCooldown = config.voice_cooldown ?? 60;
|
|
|
|
const levelupChannel = config.levelup_channel_id ? `<#${config.levelup_channel_id}>` : 'Current channel';
|
|
const levelupMessage = config.levelup_message || '🎉 Congratulations {user}! You reached **Level {level}**!';
|
|
const levelupDm = config.levelup_dm === true;
|
|
const levelupEmbed = config.levelup_embed === true;
|
|
const levelupColor = config.levelup_embed_color || '#5865F2';
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setTitle('⚙️ XP Settings')
|
|
.setColor(config.xp_enabled ? 0x00ff88 : 0xff4444)
|
|
.addFields(
|
|
{ name: '📊 Status', value: config.xp_enabled ? '✅ Enabled' : '❌ Disabled', inline: true },
|
|
{ name: '💬 Message XP', value: `${config.message_xp} XP`, inline: true },
|
|
{ name: '⏱️ Cooldown', value: `${config.message_cooldown}s`, inline: true },
|
|
{ name: '📈 Level Curve', value: curveInfo[config.level_curve] || 'Normal', inline: false },
|
|
{ name: '🎭 Multiplier Roles', value: rolesText, inline: false },
|
|
{ name: '📢 Bonus Channels', value: channelsText, inline: false },
|
|
{ name: '😀 Reaction XP', value: reactionEnabled ? '✅ Enabled' : '❌ Disabled', inline: true },
|
|
{ name: '📥 Receive Reaction', value: `${reactionReceived} XP`, inline: true },
|
|
{ name: '📤 Give Reaction', value: `${reactionGiven} XP`, inline: true },
|
|
{ name: '⏳ Reaction Cooldown', value: `${reactionCooldown}s`, inline: true },
|
|
{ name: '🎤 Voice XP', value: voiceEnabled ? '✅ Enabled' : '❌ Disabled', inline: true },
|
|
{ name: '🔊 XP per Minute', value: `${voiceXp} XP`, inline: true },
|
|
{ name: '⏰ Voice Cooldown', value: `${voiceCooldown}s`, inline: true },
|
|
{ name: '\u200B', value: '**Level-Up Announcements**', inline: false },
|
|
{ name: '📣 Channel', value: levelupChannel, inline: true },
|
|
{ name: '💌 DM Mode', value: levelupDm ? '✅ Enabled' : '❌ Disabled', inline: true },
|
|
{ name: '🖼️ Use Embed', value: levelupEmbed ? `✅ (${levelupColor})` : '❌ Disabled', inline: true },
|
|
{ name: '💬 Message', value: `\`${levelupMessage.slice(0, 100)}${levelupMessage.length > 100 ? '...' : ''}\``, inline: false }
|
|
)
|
|
.setFooter({ text: 'Use /xp-settings subcommands to modify | Placeholders: {user}, {level}, {xp}, {server}' })
|
|
.setTimestamp();
|
|
|
|
return interaction.reply({ embeds: [embed] });
|
|
}
|
|
|
|
async function handleMessageXp(interaction, supabase, guildId, config) {
|
|
const amount = interaction.options.getInteger('amount');
|
|
config.message_xp = amount;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Message XP set to **${amount} XP** per message.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleCooldown(interaction, supabase, guildId, config) {
|
|
const seconds = interaction.options.getInteger('seconds');
|
|
config.message_cooldown = seconds;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ XP cooldown set to **${seconds} seconds**.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleMultiplierRole(interaction, supabase, guildId, config) {
|
|
const role = interaction.options.getRole('role');
|
|
const multiplier = interaction.options.getNumber('multiplier');
|
|
|
|
let roles = config.multiplier_roles || [];
|
|
|
|
if (multiplier === 0) {
|
|
// Remove role
|
|
roles = roles.filter(r => r.role_id !== role.id);
|
|
config.multiplier_roles = roles;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Removed XP multiplier from ${role}.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
// Add or update role
|
|
const existing = roles.findIndex(r => r.role_id === role.id);
|
|
if (existing >= 0) {
|
|
roles[existing].multiplier = multiplier;
|
|
} else {
|
|
roles.push({ role_id: role.id, multiplier });
|
|
}
|
|
|
|
config.multiplier_roles = roles;
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ ${role} now has a **${multiplier}x** XP multiplier.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleBonusChannel(interaction, supabase, guildId, config) {
|
|
const channel = interaction.options.getChannel('channel');
|
|
const multiplier = interaction.options.getNumber('multiplier');
|
|
|
|
let channels = config.bonus_channels || [];
|
|
|
|
if (multiplier === 0) {
|
|
// Remove channel
|
|
channels = channels.filter(c => c.channel_id !== channel.id);
|
|
config.bonus_channels = channels;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Removed bonus XP from ${channel}.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
// Add or update channel
|
|
const existing = channels.findIndex(c => c.channel_id === channel.id);
|
|
if (existing >= 0) {
|
|
channels[existing].multiplier = multiplier;
|
|
} else {
|
|
channels.push({ channel_id: channel.id, multiplier });
|
|
}
|
|
|
|
config.bonus_channels = channels;
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ ${channel} now has a **${multiplier}x** XP bonus.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleToggle(interaction, supabase, guildId, config) {
|
|
const enabled = interaction.options.getBoolean('enabled');
|
|
config.xp_enabled = enabled;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: enabled
|
|
? '✅ XP system is now **enabled**.'
|
|
: '❌ XP system is now **disabled**.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleLevelCurve(interaction, supabase, guildId, config) {
|
|
const curve = interaction.options.getString('curve');
|
|
config.level_curve = curve;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
const curveNames = {
|
|
easy: 'Easy (faster leveling)',
|
|
normal: 'Normal (standard leveling)',
|
|
hard: 'Hard (slower leveling)'
|
|
};
|
|
|
|
return interaction.reply({
|
|
content: `✅ Level curve set to **${curveNames[curve]}**.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleReactionXp(interaction, supabase, guildId, config) {
|
|
const received = interaction.options.getInteger('received');
|
|
const given = interaction.options.getInteger('given');
|
|
|
|
config.reaction_xp_received = received;
|
|
config.reaction_xp_given = given;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Reaction XP set: **${received} XP** for receiving, **${given} XP** for giving.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleReactionCooldown(interaction, supabase, guildId, config) {
|
|
const seconds = interaction.options.getInteger('seconds');
|
|
config.reaction_cooldown = seconds;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Reaction XP cooldown set to **${seconds} seconds**.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleReactionToggle(interaction, supabase, guildId, config) {
|
|
const enabled = interaction.options.getBoolean('enabled');
|
|
config.reaction_xp_enabled = enabled;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: enabled
|
|
? '✅ Reaction XP is now **enabled**.'
|
|
: '❌ Reaction XP is now **disabled**.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleVoiceXp(interaction, supabase, guildId, config) {
|
|
const amount = interaction.options.getInteger('amount');
|
|
config.voice_xp = amount;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Voice XP set to **${amount} XP** per minute.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleVoiceCooldown(interaction, supabase, guildId, config) {
|
|
const seconds = interaction.options.getInteger('seconds');
|
|
config.voice_cooldown = seconds;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Voice XP cooldown set to **${seconds} seconds**.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleVoiceToggle(interaction, supabase, guildId, config) {
|
|
const enabled = interaction.options.getBoolean('enabled');
|
|
config.voice_xp_enabled = enabled;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: enabled
|
|
? '✅ Voice XP is now **enabled**.'
|
|
: '❌ Voice XP is now **disabled**.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleLevelupChannel(interaction, supabase, guildId, config) {
|
|
const channel = interaction.options.getChannel('channel');
|
|
|
|
if (!channel) {
|
|
config.levelup_channel_id = null;
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
return interaction.reply({
|
|
content: '✅ Level-up announcements will now appear in the channel where the user leveled up.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
config.levelup_channel_id = channel.id;
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: `✅ Level-up announcements will now be sent to ${channel}.`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleLevelupMessage(interaction, supabase, guildId, config) {
|
|
const message = interaction.options.getString('message');
|
|
config.levelup_message = message;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
const preview = message
|
|
.replace(/{user}/g, interaction.user.toString())
|
|
.replace(/{username}/g, interaction.user.username)
|
|
.replace(/{level}/g, '10')
|
|
.replace(/{xp}/g, '10,000')
|
|
.replace(/{server}/g, interaction.guild.name);
|
|
|
|
return interaction.reply({
|
|
content: `✅ Level-up message updated!\n\n**Preview:**\n${preview}`,
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleLevelupDm(interaction, supabase, guildId, config) {
|
|
const enabled = interaction.options.getBoolean('enabled');
|
|
config.levelup_dm = enabled;
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: enabled
|
|
? '✅ Level-up notifications will now be sent via **DM** to users.'
|
|
: '✅ Level-up notifications will now be sent in the **channel**.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
async function handleLevelupEmbed(interaction, supabase, guildId, config) {
|
|
const enabled = interaction.options.getBoolean('enabled');
|
|
const color = interaction.options.getString('color');
|
|
|
|
config.levelup_embed = enabled;
|
|
|
|
if (color) {
|
|
if (!/^#[0-9A-Fa-f]{6}$/.test(color)) {
|
|
return interaction.reply({
|
|
content: '❌ Invalid color format. Please use hex format like `#5865F2`.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
config.levelup_embed_color = color;
|
|
}
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
if (enabled) {
|
|
return interaction.reply({
|
|
content: `✅ Level-up announcements will now use **embeds** with color \`${config.levelup_embed_color}\`.`,
|
|
ephemeral: true
|
|
});
|
|
} else {
|
|
return interaction.reply({
|
|
content: '✅ Level-up announcements will now use **plain text** messages.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
}
|
|
|
|
async function handleLevelupReset(interaction, supabase, guildId, config) {
|
|
config.levelup_channel_id = null;
|
|
config.levelup_message = '🎉 Congratulations {user}! You reached **Level {level}**!';
|
|
config.levelup_dm = false;
|
|
config.levelup_embed = false;
|
|
config.levelup_embed_color = '#5865F2';
|
|
|
|
const saved = await saveXpConfig(supabase, guildId, config);
|
|
if (!saved) {
|
|
return interaction.reply({
|
|
content: '❌ Failed to save settings. Please try again.',
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
return interaction.reply({
|
|
content: '✅ Level-up announcement settings have been reset to defaults.',
|
|
ephemeral: true
|
|
});
|
|
}
|