Introduces customizable welcome cards using the canvas library and enhances the XP tracking system to incorporate active seasonal events with multipliers and bonus XP. Also adds presets and custom creation for seasonal events. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 6d6bb71e-5e8e-4841-9c13-fd5e76c7d9e6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/auRwRay Replit-Helium-Checkpoint-Created: true
221 lines
9 KiB
JavaScript
221 lines
9 KiB
JavaScript
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, AttachmentBuilder } = require('discord.js');
|
|
const { generateWelcomeCard } = require('../utils/welcomeCard');
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('welcome-card')
|
|
.setDescription('Configure custom welcome image cards')
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
|
.addSubcommand(sub => sub.setName('enable').setDescription('Enable welcome image cards'))
|
|
.addSubcommand(sub => sub.setName('disable').setDescription('Disable welcome image cards (use text only)'))
|
|
.addSubcommand(sub => sub.setName('preview').setDescription('Preview the welcome card for yourself'))
|
|
.addSubcommand(sub => sub.setName('settings').setDescription('View current welcome card settings'))
|
|
.addSubcommand(sub => sub.setName('title').setDescription('Set the welcome title text')
|
|
.addStringOption(opt => opt.setName('text').setDescription('Title text (e.g., WELCOME, HELLO, etc.)').setRequired(true).setMaxLength(20)))
|
|
.addSubcommand(sub => sub.setName('colors').setDescription('Customize card colors')
|
|
.addStringOption(opt => opt.setName('background').setDescription('Background color (hex, e.g., #1a1a2e)'))
|
|
.addStringOption(opt => opt.setName('accent').setDescription('Accent color (hex, e.g., #7c3aed)'))
|
|
.addStringOption(opt => opt.setName('text').setDescription('Text color (hex, e.g., #ffffff)'))
|
|
.addStringOption(opt => opt.setName('subtext').setDescription('Subtext color (hex, e.g., #a0a0a0)'))),
|
|
|
|
async execute(interaction, supabase) {
|
|
if (!supabase) {
|
|
return interaction.reply({ content: 'Database not available.', ephemeral: true });
|
|
}
|
|
|
|
const subcommand = interaction.options.getSubcommand();
|
|
|
|
const { data: config } = await supabase
|
|
.from('server_config')
|
|
.select('welcome_card_enabled, welcome_card_title, welcome_card_bg_color, welcome_card_accent_color, welcome_card_text_color, welcome_card_subtext_color')
|
|
.eq('guild_id', interaction.guildId)
|
|
.maybeSingle();
|
|
|
|
const safeConfig = config || {};
|
|
|
|
if (subcommand === 'enable') {
|
|
const { error } = await supabase
|
|
.from('server_config')
|
|
.upsert({
|
|
guild_id: interaction.guildId,
|
|
welcome_card_enabled: true,
|
|
updated_at: new Date().toISOString()
|
|
}, { onConflict: 'guild_id' });
|
|
|
|
if (error) {
|
|
console.error('Welcome card enable error:', error);
|
|
return interaction.reply({ content: 'Failed to enable welcome cards.', ephemeral: true });
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('Welcome Cards Enabled')
|
|
.setDescription('New members will now receive custom welcome image cards!')
|
|
.setFooter({ text: 'Use /welcome-card preview to see how it looks' })
|
|
.setTimestamp();
|
|
|
|
await interaction.reply({ embeds: [embed] });
|
|
}
|
|
|
|
if (subcommand === 'disable') {
|
|
const { error } = await supabase
|
|
.from('server_config')
|
|
.upsert({
|
|
guild_id: interaction.guildId,
|
|
welcome_card_enabled: false,
|
|
updated_at: new Date().toISOString()
|
|
}, { onConflict: 'guild_id' });
|
|
|
|
if (error) {
|
|
return interaction.reply({ content: 'Failed to disable welcome cards.', ephemeral: true });
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0xef4444)
|
|
.setTitle('Welcome Cards Disabled')
|
|
.setDescription('Welcome messages will now be text-only embeds.')
|
|
.setTimestamp();
|
|
|
|
await interaction.reply({ embeds: [embed] });
|
|
}
|
|
|
|
if (subcommand === 'preview') {
|
|
await interaction.deferReply();
|
|
|
|
try {
|
|
const cardConfig = {
|
|
title: safeConfig.welcome_card_title || 'WELCOME',
|
|
background_color: safeConfig.welcome_card_bg_color || '#1a1a2e',
|
|
accent_color: safeConfig.welcome_card_accent_color || '#7c3aed',
|
|
text_color: safeConfig.welcome_card_text_color || '#ffffff',
|
|
subtext_color: safeConfig.welcome_card_subtext_color || '#a0a0a0'
|
|
};
|
|
|
|
const buffer = await generateWelcomeCard(interaction.member, cardConfig);
|
|
const attachment = new AttachmentBuilder(buffer, { name: 'welcome-preview.png' });
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x7c3aed)
|
|
.setTitle('Welcome Card Preview')
|
|
.setDescription('This is how welcome cards will look for new members.')
|
|
.setImage('attachment://welcome-preview.png')
|
|
.setTimestamp();
|
|
|
|
await interaction.editReply({ embeds: [embed], files: [attachment] });
|
|
} catch (err) {
|
|
console.error('Welcome card preview error:', err);
|
|
await interaction.editReply({ content: 'Failed to generate preview. Please try again.' });
|
|
}
|
|
}
|
|
|
|
if (subcommand === 'settings') {
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x7c3aed)
|
|
.setTitle('Welcome Card Settings')
|
|
.addFields(
|
|
{ name: 'Enabled', value: safeConfig.welcome_card_enabled ? '✅ Yes' : '❌ No', inline: true },
|
|
{ name: 'Title', value: safeConfig.welcome_card_title || 'WELCOME', inline: true },
|
|
{ name: '\u200B', value: '\u200B', inline: true },
|
|
{ name: 'Background Color', value: safeConfig.welcome_card_bg_color || '#1a1a2e', inline: true },
|
|
{ name: 'Accent Color', value: safeConfig.welcome_card_accent_color || '#7c3aed', inline: true },
|
|
{ name: 'Text Color', value: safeConfig.welcome_card_text_color || '#ffffff', inline: true },
|
|
{ name: 'Subtext Color', value: safeConfig.welcome_card_subtext_color || '#a0a0a0', inline: true }
|
|
)
|
|
.setFooter({ text: 'Use /welcome-card commands to customize' })
|
|
.setTimestamp();
|
|
|
|
await interaction.reply({ embeds: [embed] });
|
|
}
|
|
|
|
if (subcommand === 'title') {
|
|
const titleText = interaction.options.getString('text').toUpperCase();
|
|
|
|
const { error } = await supabase
|
|
.from('server_config')
|
|
.upsert({
|
|
guild_id: interaction.guildId,
|
|
welcome_card_title: titleText,
|
|
updated_at: new Date().toISOString()
|
|
}, { onConflict: 'guild_id' });
|
|
|
|
if (error) {
|
|
return interaction.reply({ content: 'Failed to update title.', ephemeral: true });
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(0x22c55e)
|
|
.setTitle('Title Updated')
|
|
.setDescription(`Welcome card title set to: **${titleText}**`)
|
|
.setTimestamp();
|
|
|
|
await interaction.reply({ embeds: [embed] });
|
|
}
|
|
|
|
if (subcommand === 'colors') {
|
|
const bgColor = interaction.options.getString('background');
|
|
const accentColor = interaction.options.getString('accent');
|
|
const textColor = interaction.options.getString('text');
|
|
const subtextColor = interaction.options.getString('subtext');
|
|
|
|
if (!bgColor && !accentColor && !textColor && !subtextColor) {
|
|
return interaction.reply({ content: 'Please provide at least one color to update.', ephemeral: true });
|
|
}
|
|
|
|
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
const updates = { updated_at: new Date().toISOString() };
|
|
const changes = [];
|
|
|
|
if (bgColor) {
|
|
if (!hexRegex.test(bgColor)) {
|
|
return interaction.reply({ content: 'Invalid background color format. Use hex (e.g., #1a1a2e)', ephemeral: true });
|
|
}
|
|
updates.welcome_card_bg_color = bgColor;
|
|
changes.push(`Background: ${bgColor}`);
|
|
}
|
|
|
|
if (accentColor) {
|
|
if (!hexRegex.test(accentColor)) {
|
|
return interaction.reply({ content: 'Invalid accent color format. Use hex (e.g., #7c3aed)', ephemeral: true });
|
|
}
|
|
updates.welcome_card_accent_color = accentColor;
|
|
changes.push(`Accent: ${accentColor}`);
|
|
}
|
|
|
|
if (textColor) {
|
|
if (!hexRegex.test(textColor)) {
|
|
return interaction.reply({ content: 'Invalid text color format. Use hex (e.g., #ffffff)', ephemeral: true });
|
|
}
|
|
updates.welcome_card_text_color = textColor;
|
|
changes.push(`Text: ${textColor}`);
|
|
}
|
|
|
|
if (subtextColor) {
|
|
if (!hexRegex.test(subtextColor)) {
|
|
return interaction.reply({ content: 'Invalid subtext color format. Use hex (e.g., #a0a0a0)', ephemeral: true });
|
|
}
|
|
updates.welcome_card_subtext_color = subtextColor;
|
|
changes.push(`Subtext: ${subtextColor}`);
|
|
}
|
|
|
|
const { error } = await supabase
|
|
.from('server_config')
|
|
.upsert({
|
|
guild_id: interaction.guildId,
|
|
...updates
|
|
}, { onConflict: 'guild_id' });
|
|
|
|
if (error) {
|
|
return interaction.reply({ content: 'Failed to update colors.', ephemeral: true });
|
|
}
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(parseInt((accentColor || safeConfig.welcome_card_accent_color || '#7c3aed').replace('#', ''), 16))
|
|
.setTitle('Colors Updated')
|
|
.setDescription(`Updated:\n${changes.join('\n')}`)
|
|
.setFooter({ text: 'Use /welcome-card preview to see changes' })
|
|
.setTimestamp();
|
|
|
|
await interaction.reply({ embeds: [embed] });
|
|
}
|
|
},
|
|
};
|