const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, ChannelType, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const { getServerMode, setServerMode, getEmbedColor, getModeDisplayName, getModeEmoji, EMBED_COLORS } = require('../utils/modeHelper'); module.exports = { data: new SlashCommandBuilder() .setName('config') .setDescription('Configure server settings') .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) .addSubcommand(sub => sub.setName('view') .setDescription('View current server configuration') ) .addSubcommand(sub => sub.setName('welcome') .setDescription('Set the welcome channel') .addChannelOption(opt => opt.setName('channel') .setDescription('Channel for welcome messages') .addChannelTypes(ChannelType.GuildText) .setRequired(true) ) ) .addSubcommand(sub => sub.setName('goodbye') .setDescription('Set the goodbye channel') .addChannelOption(opt => opt.setName('channel') .setDescription('Channel for goodbye messages') .addChannelTypes(ChannelType.GuildText) .setRequired(true) ) ) .addSubcommand(sub => sub.setName('modlog') .setDescription('Set the moderation log channel') .addChannelOption(opt => opt.setName('channel') .setDescription('Channel for mod logs') .addChannelTypes(ChannelType.GuildText) .setRequired(true) ) ) .addSubcommand(sub => sub.setName('levelup') .setDescription('Set the level-up announcement channel') .addChannelOption(opt => opt.setName('channel') .setDescription('Channel for level-up messages') .addChannelTypes(ChannelType.GuildText) .setRequired(true) ) ) .addSubcommand(sub => sub.setName('autorole') .setDescription('Set auto-role for new members') .addRoleOption(opt => opt.setName('role') .setDescription('Role to assign on join') .setRequired(true) ) ) .addSubcommand(sub => sub.setName('levelrole') .setDescription('Add a role reward for reaching a level') .addRoleOption(opt => opt.setName('role') .setDescription('Role to give') .setRequired(true) ) .addIntegerOption(opt => opt.setName('level') .setDescription('Level required') .setRequired(true) .setMinValue(1) .setMaxValue(100) ) ) .addSubcommand(sub => sub.setName('mode') .setDescription('Switch between Federation and Standalone mode') ), async execute(interaction, supabase, client) { if (!supabase) { return interaction.reply({ content: 'Database not configured.', ephemeral: true }); } const subcommand = interaction.options.getSubcommand(); await interaction.deferReply({ ephemeral: true }); try { if (subcommand === 'mode') { const currentMode = await getServerMode(supabase, interaction.guildId); const embed = new EmbedBuilder() .setColor(getEmbedColor(currentMode)) .setTitle('Server Mode Configuration') .setDescription( `${getModeEmoji(currentMode)} Currently running in **${getModeDisplayName(currentMode)}** mode\n\n` + 'Choose your server mode:' ) .addFields( { name: '🌐 Federation Mode', value: '• Unified XP across all AeThex servers\n• Cross-server profiles and leaderboards\n• Realm selection and role sync', inline: true }, { name: 'šŸ  Standalone Mode', value: '• Isolated XP system for this server\n• Local leaderboards only\n• Full moderation features', inline: true } ) .setFooter({ text: 'This affects how XP and profiles work in your server' }); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('config_mode_federated') .setLabel('Federation') .setStyle(currentMode === 'federated' ? ButtonStyle.Primary : ButtonStyle.Secondary) .setEmoji('🌐') .setDisabled(currentMode === 'federated'), new ButtonBuilder() .setCustomId('config_mode_standalone') .setLabel('Standalone') .setStyle(currentMode === 'standalone' ? ButtonStyle.Primary : ButtonStyle.Secondary) .setEmoji('šŸ ') .setDisabled(currentMode === 'standalone') ); const response = await interaction.editReply({ embeds: [embed], components: [row] }); const collector = response.createMessageComponentCollector({ time: 60000 }); collector.on('collect', async (i) => { if (i.user.id !== interaction.user.id) { return i.reply({ content: 'Only the command user can change this.', ephemeral: true }); } const newMode = i.customId === 'config_mode_federated' ? 'federated' : 'standalone'; const success = await setServerMode(supabase, interaction.guildId, newMode); if (success) { const confirmEmbed = new EmbedBuilder() .setColor(getEmbedColor(newMode)) .setTitle(`${getModeEmoji(newMode)} Mode Changed!`) .setDescription( `Server is now running in **${getModeDisplayName(newMode)}** mode.\n\n` + (newMode === 'federated' ? 'XP will now count globally across the AeThex ecosystem.' : 'XP is now tracked locally for this server only.') ) .setTimestamp(); await i.update({ embeds: [confirmEmbed], components: [] }); collector.stop(); } else { await i.reply({ content: 'Failed to change mode. Please try again.', ephemeral: true }); } }); collector.on('end', async (collected, reason) => { if (reason === 'time') { await interaction.editReply({ components: [] }).catch(() => {}); } }); return; } if (subcommand === 'view') { const { data: config } = await supabase .from('server_config') .select('*') .eq('guild_id', interaction.guildId) .single(); const currentMode = config?.mode || 'federated'; const embed = new EmbedBuilder() .setColor(getEmbedColor(currentMode)) .setTitle('Server Configuration') .addFields( { name: 'Server Mode', value: `${getModeEmoji(currentMode)} ${getModeDisplayName(currentMode)}`, inline: true }, { name: 'Welcome Channel', value: config?.welcome_channel ? `<#${config.welcome_channel}>` : 'Not set', inline: true }, { name: 'Goodbye Channel', value: config?.goodbye_channel ? `<#${config.goodbye_channel}>` : 'Not set', inline: true }, { name: 'Mod Log Channel', value: config?.modlog_channel ? `<#${config.modlog_channel}>` : 'Not set', inline: true }, { name: 'Level-Up Channel', value: config?.level_up_channel ? `<#${config.level_up_channel}>` : 'Not set', inline: true }, { name: 'Auto Role', value: config?.auto_role ? `<@&${config.auto_role}>` : 'Not set', inline: true } ) .setTimestamp(); const { data: levelRoles } = await supabase .from('level_roles') .select('role_id, level_required') .eq('guild_id', interaction.guildId) .order('level_required', { ascending: true }); if (levelRoles && levelRoles.length > 0) { const roleText = levelRoles.map(lr => `Level ${lr.level_required}: <@&${lr.role_id}>`).join('\n'); embed.addFields({ name: 'Level Roles', value: roleText }); } return interaction.editReply({ embeds: [embed] }); } const updateField = { welcome: 'welcome_channel', goodbye: 'goodbye_channel', modlog: 'modlog_channel', levelup: 'level_up_channel', autorole: 'auto_role', }; if (subcommand === 'levelrole') { const role = interaction.options.getRole('role'); const level = interaction.options.getInteger('level'); await supabase.from('level_roles').upsert({ guild_id: interaction.guildId, role_id: role.id, level_required: level, }, { onConflict: 'guild_id,role_id' }); if (!client.serverConfigs) client.serverConfigs = new Map(); return interaction.editReply({ embeds: [ new EmbedBuilder() .setColor(0x00ff00) .setDescription(`${role} will now be given at level ${level}!`) ] }); } const fieldName = updateField[subcommand]; let value; if (subcommand === 'autorole') { value = interaction.options.getRole('role').id; } else { value = interaction.options.getChannel('channel').id; } await supabase.from('server_config').upsert({ guild_id: interaction.guildId, [fieldName]: value, updated_at: new Date().toISOString(), }, { onConflict: 'guild_id' }); if (!client.serverConfigs) client.serverConfigs = new Map(); const current = client.serverConfigs.get(interaction.guildId) || {}; current[fieldName] = value; client.serverConfigs.set(interaction.guildId, current); const displayValue = subcommand === 'autorole' ? `<@&${value}>` : `<#${value}>`; await interaction.editReply({ embeds: [ new EmbedBuilder() .setColor(0x00ff00) .setDescription(`${subcommand.charAt(0).toUpperCase() + subcommand.slice(1)} set to ${displayValue}!`) ] }); } catch (error) { console.error('Config error:', error); await interaction.editReply({ content: 'Failed to update configuration.' }); } }, };