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} `) .addFields( { name: 'ID', value: `\`${scheduleId}\``, inline: true }, { name: 'Send Time', value: ``, 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} `) .addFields( { name: 'ID', value: `\`${scheduleId}\``, inline: true }, { name: 'Send Time', value: ``, 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: `; }).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); } }