Files
EllyDiscordBot/src/commands/utility/role.ts

262 lines
7.1 KiB
TypeScript

/**
* 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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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
}
}