/** * Role Command * Manage user roles (for officers) */ import { SlashCommandBuilder, EmbedBuilder, type ChatInputCommandInteraction, } from 'discord.js'; import type { Command } from '../../types/index.ts'; import { PermissionLevel } from '../../types/index.ts'; import type { EllyClient } from '../../client/EllyClient.ts'; export const roleCommand: Command = { data: new SlashCommandBuilder() .setName('role') .setDescription('Manage user roles') .addSubcommand((sub) => sub .setName('add') .setDescription('Add a role to a user') .addUserOption((opt) => opt.setName('user').setDescription('User to add role to').setRequired(true) ) .addRoleOption((opt) => opt.setName('role').setDescription('Role to add').setRequired(true) ) ) .addSubcommand((sub) => sub .setName('remove') .setDescription('Remove a role from a user') .addUserOption((opt) => opt.setName('user').setDescription('User to remove role from').setRequired(true) ) .addRoleOption((opt) => opt.setName('role').setDescription('Role to remove').setRequired(true) ) ) .addSubcommand((sub) => sub .setName('list') .setDescription('List manageable roles') ), permission: PermissionLevel.Officer, cooldown: 3, async execute(interaction: ChatInputCommandInteraction): Promise { const client = interaction.client as EllyClient; const subcommand = interaction.options.getSubcommand(); switch (subcommand) { case 'add': await handleAdd(interaction, client); break; case 'remove': await handleRemove(interaction, client); break; case 'list': await handleList(interaction, client); break; } }, }; /** * Handle adding a role */ async function handleAdd( interaction: ChatInputCommandInteraction, client: EllyClient ): Promise { const targetUser = interaction.options.getUser('user', true); const role = interaction.options.getRole('role', true); // Check if role is manageable const manageableIds = client.config.roles.manageable?.ids ?? []; if (!manageableIds.includes(role.id)) { await interaction.reply({ content: `❌ The role **${role.name}** is not in the list of manageable roles.`, ephemeral: true, }); return; } const member = interaction.guild?.members.cache.get(targetUser.id); if (!member) { await interaction.reply({ content: '❌ User not found in this server.', ephemeral: true, }); return; } // Check if user already has the role if (member.roles.cache.has(role.id)) { await interaction.reply({ content: `❌ ${targetUser.tag} already has the **${role.name}** role.`, ephemeral: true, }); return; } try { await member.roles.add(role.id); await interaction.reply({ embeds: [ new EmbedBuilder() .setColor(client.config.colors.success) .setTitle('✅ Role Added') .setDescription(`Added **${role.name}** to ${targetUser.tag}`) .setFooter({ text: `By ${interaction.user.tag}` }) .setTimestamp(), ], }); // Log to development channel await logRoleChange(client, interaction, targetUser.tag, role.name, 'added'); } catch (error) { await interaction.reply({ content: `❌ Failed to add role. Make sure I have the required permissions.`, ephemeral: true, }); } } /** * Handle removing a role */ async function handleRemove( interaction: ChatInputCommandInteraction, client: EllyClient ): Promise { const targetUser = interaction.options.getUser('user', true); const role = interaction.options.getRole('role', true); // Check if role is manageable const manageableIds = client.config.roles.manageable?.ids ?? []; if (!manageableIds.includes(role.id)) { await interaction.reply({ content: `❌ The role **${role.name}** is not in the list of manageable roles.`, ephemeral: true, }); return; } const member = interaction.guild?.members.cache.get(targetUser.id); if (!member) { await interaction.reply({ content: '❌ User not found in this server.', ephemeral: true, }); return; } // Check if user has the role if (!member.roles.cache.has(role.id)) { await interaction.reply({ content: `❌ ${targetUser.tag} doesn't have the **${role.name}** role.`, ephemeral: true, }); return; } try { await member.roles.remove(role.id); await interaction.reply({ embeds: [ new EmbedBuilder() .setColor(client.config.colors.warning) .setTitle('✅ Role Removed') .setDescription(`Removed **${role.name}** from ${targetUser.tag}`) .setFooter({ text: `By ${interaction.user.tag}` }) .setTimestamp(), ], }); // Log to development channel await logRoleChange(client, interaction, targetUser.tag, role.name, 'removed'); } catch (error) { await interaction.reply({ content: `❌ Failed to remove role. Make sure I have the required permissions.`, ephemeral: true, }); } } /** * Handle listing manageable roles */ async function handleList( interaction: ChatInputCommandInteraction, client: EllyClient ): Promise { const manageableIds = client.config.roles.manageable?.ids ?? []; if (manageableIds.length === 0) { await interaction.reply({ content: '📭 No manageable roles configured.', ephemeral: true, }); return; } const roles = manageableIds .map((id) => interaction.guild?.roles.cache.get(id)) .filter((r) => r !== undefined) .map((r) => `• <@&${r!.id}> (\`${r!.id}\`)`); await interaction.reply({ embeds: [ new EmbedBuilder() .setColor(client.config.colors.primary) .setTitle('📋 Manageable Roles') .setDescription(roles.join('\n') || 'No roles found') .setFooter({ text: `${roles.length} roles can be managed` }) .setTimestamp(), ], ephemeral: true, }); } /** * Log role change to development channel */ async function logRoleChange( client: EllyClient, interaction: ChatInputCommandInteraction, targetTag: string, roleName: string, action: 'added' | 'removed' ): Promise { try { const logChannelName = client.config.channels.developmentLogs; const logChannel = interaction.guild?.channels.cache.find( (c) => c.name === logChannelName && c.isTextBased() ); if (logChannel && logChannel.isTextBased()) { await logChannel.send({ embeds: [ new EmbedBuilder() .setColor(action === 'added' ? 0x57f287 : 0xfee75c) .setTitle(`📝 Role ${action === 'added' ? 'Added' : 'Removed'}`) .addFields( { name: 'User', value: targetTag, inline: true }, { name: 'Role', value: roleName, inline: true }, { name: 'By', value: interaction.user.tag, inline: true } ) .setTimestamp(), ], }); } } catch { // Ignore logging errors } }