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
331 lines
10 KiB
JavaScript
331 lines
10 KiB
JavaScript
const {
|
|
SlashCommandBuilder,
|
|
EmbedBuilder,
|
|
PermissionFlagsBits,
|
|
ChannelType
|
|
} = require('discord.js');
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('schedule')
|
|
.setDescription('Schedule a message to be sent later')
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
|
|
.addSubcommand(sub =>
|
|
sub.setName('message')
|
|
.setDescription('Schedule a text message')
|
|
.addChannelOption(option =>
|
|
option.setName('channel')
|
|
.setDescription('Channel to send the message to')
|
|
.setRequired(true)
|
|
.addChannelTypes(ChannelType.GuildText, ChannelType.GuildAnnouncement)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('content')
|
|
.setDescription('Message content')
|
|
.setRequired(true)
|
|
.setMaxLength(2000)
|
|
)
|
|
.addIntegerOption(option =>
|
|
option.setName('minutes')
|
|
.setDescription('Minutes from now')
|
|
.setRequired(true)
|
|
.setMinValue(1)
|
|
.setMaxValue(10080)
|
|
)
|
|
.addBooleanOption(option =>
|
|
option.setName('ping_everyone')
|
|
.setDescription('Ping @everyone')
|
|
.setRequired(false)
|
|
)
|
|
)
|
|
.addSubcommand(sub =>
|
|
sub.setName('embed')
|
|
.setDescription('Schedule an embed message')
|
|
.addChannelOption(option =>
|
|
option.setName('channel')
|
|
.setDescription('Channel to send the embed to')
|
|
.setRequired(true)
|
|
.addChannelTypes(ChannelType.GuildText, ChannelType.GuildAnnouncement)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('title')
|
|
.setDescription('Embed title')
|
|
.setRequired(true)
|
|
.setMaxLength(256)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('description')
|
|
.setDescription('Embed description')
|
|
.setRequired(true)
|
|
.setMaxLength(2000)
|
|
)
|
|
.addIntegerOption(option =>
|
|
option.setName('minutes')
|
|
.setDescription('Minutes from now')
|
|
.setRequired(true)
|
|
.setMinValue(1)
|
|
.setMaxValue(10080)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('color')
|
|
.setDescription('Embed color')
|
|
.setRequired(false)
|
|
.addChoices(
|
|
{ name: '🟣 Purple', value: '7c3aed' },
|
|
{ name: '🟢 Green', value: '22c55e' },
|
|
{ name: '🔴 Red', value: 'ef4444' },
|
|
{ name: '🔵 Blue', value: '3b82f6' },
|
|
{ name: '🟡 Yellow', value: 'eab308' }
|
|
)
|
|
)
|
|
)
|
|
.addSubcommand(sub =>
|
|
sub.setName('list')
|
|
.setDescription('List scheduled messages')
|
|
)
|
|
.addSubcommand(sub =>
|
|
sub.setName('cancel')
|
|
.setDescription('Cancel a scheduled message')
|
|
.addStringOption(option =>
|
|
option.setName('id')
|
|
.setDescription('Scheduled message ID')
|
|
.setRequired(true)
|
|
)
|
|
),
|
|
|
|
async execute(interaction, supabase, client) {
|
|
const subcommand = interaction.options.getSubcommand();
|
|
|
|
if (subcommand === 'message') {
|
|
await handleMessage(interaction, supabase, client);
|
|
} else if (subcommand === 'embed') {
|
|
await handleEmbed(interaction, supabase, client);
|
|
} else if (subcommand === 'list') {
|
|
await handleList(interaction, supabase);
|
|
} else if (subcommand === 'cancel') {
|
|
await handleCancel(interaction, supabase, client);
|
|
}
|
|
},
|
|
};
|
|
|
|
async function handleMessage(interaction, supabase, client) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
const channel = interaction.options.getChannel('channel');
|
|
const content = interaction.options.getString('content');
|
|
const minutes = interaction.options.getInteger('minutes');
|
|
const pingEveryone = interaction.options.getBoolean('ping_everyone') || false;
|
|
|
|
const sendTime = Date.now() + (minutes * 60 * 1000);
|
|
const sendTimestamp = Math.floor(sendTime / 1000);
|
|
|
|
const scheduleId = `sched_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
try {
|
|
const scheduleData = {
|
|
id: scheduleId,
|
|
type: 'message',
|
|
channelId: channel.id,
|
|
content: pingEveryone ? `@everyone ${content}` : content,
|
|
sendTime: sendTime
|
|
};
|
|
|
|
client.scheduledMessages = client.scheduledMessages || new Map();
|
|
const timeout = setTimeout(() => sendScheduledMessage(scheduleId, client, supabase), minutes * 60 * 1000);
|
|
client.scheduledMessages.set(scheduleId, { ...scheduleData, timeout });
|
|
|
|
if (supabase) {
|
|
await supabase.from('scheduled_messages').insert({
|
|
id: scheduleId,
|
|
guild_id: interaction.guildId,
|
|
channel_id: channel.id,
|
|
type: 'message',
|
|
content: scheduleData.content,
|
|
send_time: new Date(sendTime).toISOString(),
|
|
created_by: interaction.user.id,
|
|
status: 'pending'
|
|
});
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('✅ Message Scheduled')
|
|
.setDescription(`Message will be sent to ${channel} <t:${sendTimestamp}:R>`)
|
|
.addFields(
|
|
{ name: 'ID', value: `\`${scheduleId}\``, inline: true },
|
|
{ name: 'Send Time', value: `<t:${sendTimestamp}:f>`, inline: true }
|
|
)
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [embed] });
|
|
|
|
} catch (error) {
|
|
console.error('Schedule message error:', error);
|
|
await interaction.editReply({ content: 'Failed to schedule message.' });
|
|
}
|
|
}
|
|
|
|
async function handleEmbed(interaction, supabase, client) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
const channel = interaction.options.getChannel('channel');
|
|
const title = interaction.options.getString('title');
|
|
const description = interaction.options.getString('description');
|
|
const minutes = interaction.options.getInteger('minutes');
|
|
const color = interaction.options.getString('color') || '7c3aed';
|
|
|
|
const sendTime = Date.now() + (minutes * 60 * 1000);
|
|
const sendTimestamp = Math.floor(sendTime / 1000);
|
|
|
|
const scheduleId = `sched_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
try {
|
|
const embedData = {
|
|
title: title,
|
|
description: description,
|
|
color: parseInt(color, 16)
|
|
};
|
|
|
|
const scheduleData = {
|
|
id: scheduleId,
|
|
type: 'embed',
|
|
channelId: channel.id,
|
|
embedData: embedData,
|
|
sendTime: sendTime
|
|
};
|
|
|
|
client.scheduledMessages = client.scheduledMessages || new Map();
|
|
const timeout = setTimeout(() => sendScheduledMessage(scheduleId, client, supabase), minutes * 60 * 1000);
|
|
client.scheduledMessages.set(scheduleId, { ...scheduleData, timeout });
|
|
|
|
if (supabase) {
|
|
await supabase.from('scheduled_messages').insert({
|
|
id: scheduleId,
|
|
guild_id: interaction.guildId,
|
|
channel_id: channel.id,
|
|
type: 'embed',
|
|
embed_data: embedData,
|
|
send_time: new Date(sendTime).toISOString(),
|
|
created_by: interaction.user.id,
|
|
status: 'pending'
|
|
});
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('✅ Embed Scheduled')
|
|
.setDescription(`Embed will be sent to ${channel} <t:${sendTimestamp}:R>`)
|
|
.addFields(
|
|
{ name: 'ID', value: `\`${scheduleId}\``, inline: true },
|
|
{ name: 'Send Time', value: `<t:${sendTimestamp}:f>`, inline: true },
|
|
{ name: 'Embed Title', value: title, inline: false }
|
|
)
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [embed] });
|
|
|
|
} catch (error) {
|
|
console.error('Schedule embed error:', error);
|
|
await interaction.editReply({ content: 'Failed to schedule embed.' });
|
|
}
|
|
}
|
|
|
|
async function handleList(interaction, supabase) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
if (!supabase) {
|
|
return interaction.editReply({ content: 'Database not available.' });
|
|
}
|
|
|
|
try {
|
|
const { data: scheduled, error } = await supabase
|
|
.from('scheduled_messages')
|
|
.select('*')
|
|
.eq('guild_id', interaction.guildId)
|
|
.eq('status', 'pending')
|
|
.order('send_time', { ascending: true });
|
|
|
|
if (error) throw error;
|
|
|
|
if (!scheduled || scheduled.length === 0) {
|
|
return interaction.editReply({ content: 'No scheduled messages.' });
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x7c3aed)
|
|
.setTitle('📅 Scheduled Messages')
|
|
.setDescription(scheduled.map(s => {
|
|
const sendTimestamp = Math.floor(new Date(s.send_time).getTime() / 1000);
|
|
return `**ID:** \`${s.id}\`\nType: ${s.type}\nChannel: <#${s.channel_id}>\nSends: <t:${sendTimestamp}:R>`;
|
|
}).join('\n\n'))
|
|
.setFooter({ text: `${scheduled.length} scheduled` })
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [embed] });
|
|
|
|
} catch (error) {
|
|
console.error('Schedule list error:', error);
|
|
await interaction.editReply({ content: 'Failed to fetch scheduled messages.' });
|
|
}
|
|
}
|
|
|
|
async function handleCancel(interaction, supabase, client) {
|
|
await interaction.deferReply({ ephemeral: true });
|
|
|
|
const scheduleId = interaction.options.getString('id');
|
|
|
|
try {
|
|
const scheduled = client.scheduledMessages?.get(scheduleId);
|
|
if (scheduled?.timeout) {
|
|
clearTimeout(scheduled.timeout);
|
|
client.scheduledMessages.delete(scheduleId);
|
|
}
|
|
|
|
if (supabase) {
|
|
await supabase
|
|
.from('scheduled_messages')
|
|
.update({ status: 'cancelled' })
|
|
.eq('id', scheduleId);
|
|
}
|
|
|
|
await interaction.editReply({ content: '✅ Scheduled message cancelled.' });
|
|
|
|
} catch (error) {
|
|
console.error('Schedule cancel error:', error);
|
|
await interaction.editReply({ content: 'Failed to cancel.' });
|
|
}
|
|
}
|
|
|
|
async function sendScheduledMessage(scheduleId, client, supabase) {
|
|
try {
|
|
const scheduled = client.scheduledMessages?.get(scheduleId);
|
|
if (!scheduled) return;
|
|
|
|
const channel = await client.channels.fetch(scheduled.channelId);
|
|
if (!channel) return;
|
|
|
|
if (scheduled.type === 'message') {
|
|
await channel.send(scheduled.content);
|
|
} else if (scheduled.type === 'embed') {
|
|
const embed = new EmbedBuilder()
|
|
.setColor(scheduled.embedData.color)
|
|
.setTitle(scheduled.embedData.title)
|
|
.setDescription(scheduled.embedData.description)
|
|
.setTimestamp();
|
|
|
|
await channel.send({ embeds: [embed] });
|
|
}
|
|
|
|
client.scheduledMessages.delete(scheduleId);
|
|
|
|
if (supabase) {
|
|
await supabase
|
|
.from('scheduled_messages')
|
|
.update({ status: 'sent' })
|
|
.eq('id', scheduleId);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Send scheduled message error:', error);
|
|
}
|
|
}
|