Introduces new commands like `/automod`, `/giveaway`, `/rolepanel`, and `/schedule`. Enhances existing commands such as `/announce`, `/help`, `/leaderboard`, `/profile`, and `/serverinfo` with new features and improved embed designs. Updates welcome and goodbye listeners with rich embeds. Fixes a critical issue in the `/rolepanel` command regarding channel fetching. Adds interaction handling for role buttons and giveaway entries. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: eefee140-1301-4b6f-9439-2b0b883aa40a Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/qAaysIh Replit-Helium-Checkpoint-Created: true
394 lines
12 KiB
JavaScript
394 lines
12 KiB
JavaScript
const {
|
|
SlashCommandBuilder,
|
|
EmbedBuilder,
|
|
PermissionFlagsBits,
|
|
ActionRowBuilder,
|
|
ButtonBuilder,
|
|
ButtonStyle,
|
|
ChannelType
|
|
} = require('discord.js');
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('rolepanel')
|
|
.setDescription('Create and manage role button panels')
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles)
|
|
.addSubcommand(sub =>
|
|
sub.setName('create')
|
|
.setDescription('Create a new role panel')
|
|
.addChannelOption(option =>
|
|
option.setName('channel')
|
|
.setDescription('Channel to send the panel to')
|
|
.setRequired(true)
|
|
.addChannelTypes(ChannelType.GuildText)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('title')
|
|
.setDescription('Panel title')
|
|
.setRequired(true)
|
|
.setMaxLength(256)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('description')
|
|
.setDescription('Panel description')
|
|
.setRequired(false)
|
|
.setMaxLength(2000)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('color')
|
|
.setDescription('Embed color')
|
|
.setRequired(false)
|
|
.addChoices(
|
|
{ name: '🟣 Purple', value: '7c3aed' },
|
|
{ name: '🟢 Green', value: '22c55e' },
|
|
{ name: '🔵 Blue', value: '3b82f6' },
|
|
{ name: '🟡 Yellow', value: 'eab308' },
|
|
{ name: '🟠 Orange', value: 'f97316' }
|
|
)
|
|
)
|
|
)
|
|
.addSubcommand(sub =>
|
|
sub.setName('addrole')
|
|
.setDescription('Add a role button to a panel')
|
|
.addStringOption(option =>
|
|
option.setName('message_id')
|
|
.setDescription('Message ID of the role panel')
|
|
.setRequired(true)
|
|
)
|
|
.addRoleOption(option =>
|
|
option.setName('role')
|
|
.setDescription('Role to add')
|
|
.setRequired(true)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('label')
|
|
.setDescription('Button label (optional, uses role name if not set)')
|
|
.setRequired(false)
|
|
.setMaxLength(80)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('emoji')
|
|
.setDescription('Button emoji (optional)')
|
|
.setRequired(false)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('style')
|
|
.setDescription('Button style')
|
|
.setRequired(false)
|
|
.addChoices(
|
|
{ name: 'Blue (Primary)', value: 'primary' },
|
|
{ name: 'Gray (Secondary)', value: 'secondary' },
|
|
{ name: 'Green (Success)', value: 'success' },
|
|
{ name: 'Red (Danger)', value: 'danger' }
|
|
)
|
|
)
|
|
)
|
|
.addSubcommand(sub =>
|
|
sub.setName('removerole')
|
|
.setDescription('Remove a role button from a panel')
|
|
.addStringOption(option =>
|
|
option.setName('message_id')
|
|
.setDescription('Message ID of the role panel')
|
|
.setRequired(true)
|
|
)
|
|
.addRoleOption(option =>
|
|
option.setName('role')
|
|
.setDescription('Role to remove')
|
|
.setRequired(true)
|
|
)
|
|
)
|
|
.addSubcommand(sub =>
|
|
sub.setName('list')
|
|
.setDescription('List all role panels in this server')
|
|
),
|
|
|
|
async execute(interaction, supabase, client) {
|
|
const subcommand = interaction.options.getSubcommand();
|
|
|
|
if (subcommand === 'create') {
|
|
await handleCreate(interaction, supabase);
|
|
} else if (subcommand === 'addrole') {
|
|
await handleAddRole(interaction, supabase, client);
|
|
} else if (subcommand === 'removerole') {
|
|
await handleRemoveRole(interaction, supabase, client);
|
|
} else if (subcommand === 'list') {
|
|
await handleList(interaction, supabase);
|
|
}
|
|
},
|
|
};
|
|
|
|
async function handleCreate(interaction, supabase) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
const channel = interaction.options.getChannel('channel');
|
|
const title = interaction.options.getString('title');
|
|
const description = interaction.options.getString('description') || 'Click a button below to get or remove a role!';
|
|
const color = parseInt(interaction.options.getString('color') || '7c3aed', 16);
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(color)
|
|
.setTitle(`🎭 ${title}`)
|
|
.setDescription(description)
|
|
.setFooter({ text: 'Click a button to toggle the role' })
|
|
.setTimestamp();
|
|
|
|
try {
|
|
const message = await channel.send({ embeds: [embed] });
|
|
|
|
if (supabase) {
|
|
await supabase.from('role_panels').insert({
|
|
message_id: message.id,
|
|
channel_id: channel.id,
|
|
guild_id: interaction.guildId,
|
|
title: title,
|
|
description: description,
|
|
color: color.toString(16),
|
|
roles: [],
|
|
created_by: interaction.user.id
|
|
});
|
|
}
|
|
|
|
const successEmbed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('✅ Role Panel Created')
|
|
.setDescription(`Panel created in ${channel}!\n\nUse \`/rolepanel addrole\` with message ID:\n\`${message.id}\``)
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [successEmbed] });
|
|
|
|
} catch (error) {
|
|
console.error('Role panel create error:', error);
|
|
await interaction.editReply({ content: 'Failed to create role panel. Check bot permissions.', ephemeral: true });
|
|
}
|
|
}
|
|
|
|
async function handleAddRole(interaction, supabase, client) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
const messageId = interaction.options.getString('message_id');
|
|
const role = interaction.options.getRole('role');
|
|
const label = interaction.options.getString('label') || role.name;
|
|
const emoji = interaction.options.getString('emoji');
|
|
const styleStr = interaction.options.getString('style') || 'primary';
|
|
|
|
const styleMap = {
|
|
'primary': ButtonStyle.Primary,
|
|
'secondary': ButtonStyle.Secondary,
|
|
'success': ButtonStyle.Success,
|
|
'danger': ButtonStyle.Danger
|
|
};
|
|
const style = styleMap[styleStr];
|
|
|
|
try {
|
|
let channel = null;
|
|
let message = null;
|
|
|
|
if (supabase) {
|
|
const { data: panel } = await supabase
|
|
.from('role_panels')
|
|
.select('channel_id')
|
|
.eq('message_id', messageId)
|
|
.eq('guild_id', interaction.guildId)
|
|
.single();
|
|
|
|
if (panel?.channel_id) {
|
|
channel = await interaction.guild.channels.fetch(panel.channel_id).catch(() => null);
|
|
}
|
|
}
|
|
|
|
if (!channel) {
|
|
channel = interaction.channel;
|
|
}
|
|
|
|
message = await channel.messages.fetch(messageId).catch(() => null);
|
|
|
|
if (!message) {
|
|
return interaction.editReply({ content: 'Could not find that message. Make sure the message ID is correct and the panel exists.' });
|
|
}
|
|
|
|
if (message.author.id !== client.user.id) {
|
|
return interaction.editReply({ content: 'That message was not sent by me. I can only edit my own messages.' });
|
|
}
|
|
|
|
const buttonData = {
|
|
role_id: role.id,
|
|
role_name: role.name,
|
|
label: label,
|
|
emoji: emoji,
|
|
style: styleStr
|
|
};
|
|
|
|
const existingRows = message.components.map(row => ActionRowBuilder.from(row));
|
|
|
|
const button = new ButtonBuilder()
|
|
.setCustomId(`role_${role.id}`)
|
|
.setLabel(label)
|
|
.setStyle(style);
|
|
|
|
if (emoji) {
|
|
button.setEmoji(emoji);
|
|
}
|
|
|
|
let added = false;
|
|
for (const row of existingRows) {
|
|
if (row.components.length < 5) {
|
|
row.addComponents(button);
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!added) {
|
|
if (existingRows.length >= 5) {
|
|
return interaction.editReply({ content: 'Maximum buttons reached (25). Remove some roles first.' });
|
|
}
|
|
const newRow = new ActionRowBuilder().addComponents(button);
|
|
existingRows.push(newRow);
|
|
}
|
|
|
|
await message.edit({ components: existingRows });
|
|
|
|
if (supabase) {
|
|
const { data: panel } = await supabase
|
|
.from('role_panels')
|
|
.select('roles')
|
|
.eq('message_id', messageId)
|
|
.single();
|
|
|
|
const roles = panel?.roles || [];
|
|
roles.push(buttonData);
|
|
|
|
await supabase
|
|
.from('role_panels')
|
|
.update({ roles: roles })
|
|
.eq('message_id', messageId);
|
|
}
|
|
|
|
const successEmbed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('✅ Role Added')
|
|
.setDescription(`Added ${role} to the panel!`)
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [successEmbed] });
|
|
|
|
} catch (error) {
|
|
console.error('Add role error:', error);
|
|
await interaction.editReply({ content: `Failed to add role: ${error.message}` });
|
|
}
|
|
}
|
|
|
|
async function handleRemoveRole(interaction, supabase, client) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
const messageId = interaction.options.getString('message_id');
|
|
const role = interaction.options.getRole('role');
|
|
|
|
try {
|
|
let channel = null;
|
|
let message = null;
|
|
|
|
if (supabase) {
|
|
const { data: panel } = await supabase
|
|
.from('role_panels')
|
|
.select('channel_id')
|
|
.eq('message_id', messageId)
|
|
.eq('guild_id', interaction.guildId)
|
|
.single();
|
|
|
|
if (panel?.channel_id) {
|
|
channel = await interaction.guild.channels.fetch(panel.channel_id).catch(() => null);
|
|
}
|
|
}
|
|
|
|
if (!channel) {
|
|
channel = interaction.channel;
|
|
}
|
|
|
|
message = await channel.messages.fetch(messageId).catch(() => null);
|
|
|
|
if (!message) {
|
|
return interaction.editReply({ content: 'Could not find that message. Make sure the message ID is correct and the panel exists.' });
|
|
}
|
|
|
|
const existingRows = message.components.map(row => ActionRowBuilder.from(row));
|
|
|
|
for (const row of existingRows) {
|
|
const buttonIndex = row.components.findIndex(btn => btn.data.custom_id === `role_${role.id}`);
|
|
if (buttonIndex !== -1) {
|
|
row.components.splice(buttonIndex, 1);
|
|
}
|
|
}
|
|
|
|
const filteredRows = existingRows.filter(row => row.components.length > 0);
|
|
|
|
await message.edit({ components: filteredRows });
|
|
|
|
if (supabase) {
|
|
const { data: panel } = await supabase
|
|
.from('role_panels')
|
|
.select('roles')
|
|
.eq('message_id', messageId)
|
|
.single();
|
|
|
|
const roles = (panel?.roles || []).filter(r => r.role_id !== role.id);
|
|
|
|
await supabase
|
|
.from('role_panels')
|
|
.update({ roles: roles })
|
|
.eq('message_id', messageId);
|
|
}
|
|
|
|
const successEmbed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('✅ Role Removed')
|
|
.setDescription(`Removed ${role} from the panel!`)
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [successEmbed] });
|
|
|
|
} catch (error) {
|
|
console.error('Remove role error:', error);
|
|
await interaction.editReply({ content: `Failed to remove role: ${error.message}` });
|
|
}
|
|
}
|
|
|
|
async function handleList(interaction, supabase) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
if (!supabase) {
|
|
return interaction.editReply({ content: 'This feature requires database connection.' });
|
|
}
|
|
|
|
try {
|
|
const { data: panels, error } = await supabase
|
|
.from('role_panels')
|
|
.select('*')
|
|
.eq('guild_id', interaction.guildId)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
|
|
if (!panels || panels.length === 0) {
|
|
return interaction.editReply({ content: 'No role panels found in this server.' });
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x7c3aed)
|
|
.setTitle('🎭 Role Panels')
|
|
.setDescription(panels.map(p =>
|
|
`**${p.title}**\n` +
|
|
`Channel: <#${p.channel_id}>\n` +
|
|
`Message ID: \`${p.message_id}\`\n` +
|
|
`Roles: ${p.roles?.length || 0}`
|
|
).join('\n\n'))
|
|
.setFooter({ text: `${panels.length} panel(s)` })
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [embed] });
|
|
|
|
} catch (error) {
|
|
console.error('List panels error:', error);
|
|
await interaction.editReply({ content: 'Failed to fetch panels.' });
|
|
}
|
|
}
|