AeThex-Bot-Master/aethex-bot/commands/config.js
sirpiglr c2a34f398e Add server mode configuration and dynamic status updates
Introduces a new server mode configuration system (Federation/Standalone) with associated command changes, dynamic status rotation for the bot, and adds new commands and features.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: b08e6ba5-7498-4b9f-b1c9-7dc11b362ddd
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/R9PkDi8
Replit-Helium-Checkpoint-Created: true
2025-12-09 23:26:33 +00:00

272 lines
10 KiB
JavaScript

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.' });
}
},
};