AeThex-Bot-Master/aethex-bot/commands/branding.js
2026-03-05 14:34:11 -07:00

479 lines
18 KiB
JavaScript

const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder } = require('discord.js');
const { getBranding, updateBranding, claimHandle, hasFeature, BRANDING_TIERS, createBrandedEmbed } = require('../utils/brandingManager');
module.exports = {
data: new SlashCommandBuilder()
.setName('branding')
.setDescription('Configure white-label branding for your server')
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addSubcommand(sub => sub
.setName('view')
.setDescription('View current branding settings')
)
.addSubcommand(sub => sub
.setName('name')
.setDescription('Set custom bot name (Basic tier+)')
.addStringOption(opt => opt
.setName('name')
.setDescription('Custom bot name (max 80 chars)')
.setRequired(true)
.setMaxLength(80)
)
)
.addSubcommand(sub => sub
.setName('avatar')
.setDescription('Set custom bot avatar (Pro tier+)')
.addStringOption(opt => opt
.setName('url')
.setDescription('Avatar image URL (must be https)')
.setRequired(true)
)
)
.addSubcommand(sub => sub
.setName('footer')
.setDescription('Set custom embed footer (Basic tier+)')
.addStringOption(opt => opt
.setName('text')
.setDescription('Footer text (max 200 chars)')
.setRequired(true)
.setMaxLength(200)
)
)
.addSubcommand(sub => sub
.setName('color')
.setDescription('Set custom embed color (Basic tier+)')
.addStringOption(opt => opt
.setName('hex')
.setDescription('Hex color (e.g., #ff5500)')
.setRequired(true)
)
)
.addSubcommand(sub => sub
.setName('handle')
.setDescription('Claim your custom URL handle (Pro tier+)')
.addStringOption(opt => opt
.setName('handle')
.setDescription('Your custom handle (aethex.bot/YOUR-HANDLE)')
.setRequired(true)
.setMinLength(3)
.setMaxLength(30)
)
)
.addSubcommand(sub => sub
.setName('landing')
.setDescription('Configure your landing page (Pro tier+)')
.addStringOption(opt => opt
.setName('title')
.setDescription('Page title')
.setMaxLength(100)
)
.addStringOption(opt => opt
.setName('description')
.setDescription('Page description')
.setMaxLength(500)
)
.addStringOption(opt => opt
.setName('banner')
.setDescription('Banner image URL')
)
.addStringOption(opt => opt
.setName('invite')
.setDescription('Discord invite URL')
)
)
.addSubcommand(sub => sub
.setName('toggle')
.setDescription('Enable or disable branding')
.addBooleanOption(opt => opt
.setName('enabled')
.setDescription('Enable custom branding?')
.setRequired(true)
)
)
.addSubcommand(sub => sub
.setName('preview')
.setDescription('Preview your branded embeds')
)
.addSubcommand(sub => sub
.setName('tiers')
.setDescription('View branding tier pricing and features')
)
.addSubcommand(sub => sub
.setName('reset')
.setDescription('Reset branding to defaults')
),
async execute(interaction, supabase) {
if (!supabase) {
return interaction.reply({ content: 'Database not available.', ephemeral: true });
}
const subcommand = interaction.options.getSubcommand();
const guildId = interaction.guildId;
await interaction.deferReply({ ephemeral: true });
try {
const branding = await getBranding(supabase, guildId);
// View current branding
if (subcommand === 'view') {
const tierInfo = BRANDING_TIERS[branding.tier || 'free'];
const embed = new EmbedBuilder()
.setColor(branding.custom_embed_color || '#6366f1')
.setTitle('🏷️ White-Label Branding Settings')
.setDescription(
`**Current Tier:** ${tierInfo.name} (${tierInfo.price > 0 ? `$${tierInfo.price}/mo` : 'Free'})\n` +
`**Status:** ${branding.branding_enabled ? '✅ Enabled' : '❌ Disabled'}`
)
.addFields(
{ name: 'Custom Bot Name', value: branding.custom_bot_name || '*Not set*', inline: true },
{ name: 'Custom Footer', value: branding.custom_footer_text || '*Not set*', inline: true },
{ name: 'Embed Color', value: branding.custom_embed_color || '*Default*', inline: true },
{ name: 'Custom Avatar', value: branding.custom_bot_avatar_url ? '✅ Set' : '*Not set*', inline: true },
{ name: 'Custom Handle', value: branding.custom_handle ? `aethex.bot/${branding.custom_handle}` : '*Not claimed*', inline: true },
{ name: 'Landing Page', value: branding.landing_title ? '✅ Configured' : '*Not set*', inline: true }
)
.setFooter({ text: 'Use /branding tiers to see upgrade options' })
.setTimestamp();
if (branding.custom_bot_avatar_url) {
embed.setThumbnail(branding.custom_bot_avatar_url);
}
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('Manage on Dashboard')
.setURL(`https://aethex.bot/dashboard?guild=${guildId}&page=branding`)
.setStyle(ButtonStyle.Link),
new ButtonBuilder()
.setLabel('Upgrade Tier')
.setURL('https://aethex.bot/pricing#branding')
.setStyle(ButtonStyle.Link)
);
return interaction.editReply({ embeds: [embed], components: [row] });
}
// View pricing tiers
if (subcommand === 'tiers') {
const embed = new EmbedBuilder()
.setColor('#6366f1')
.setTitle('🏷️ White-Label Branding Tiers')
.setDescription('Customize Warden to match your community brand!')
.addFields(
{
name: '🆓 Free',
value: '• Default AeThex | Warden branding\n• All bot features included\n• No customization',
inline: true
},
{
name: '💎 Basic - $15/mo',
value: '• Custom bot name\n• Custom footer text\n• Custom embed color\n• Removes "AeThex" branding',
inline: true
},
{
name: '⭐ Pro - $35/mo',
value: '• Everything in Basic\n• Custom bot avatar\n• Custom URL handle\n• Landing page\n• `aethex.bot/your-name`',
inline: true
},
{
name: '🏆 Enterprise - $75/mo',
value: '• Everything in Pro\n• Analytics dashboard\n• Priority support\n• Custom domain support\n• White-glove setup',
inline: false
}
)
.setFooter({ text: 'All tiers include full bot functionality • Payments via Stripe' });
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('Subscribe Now')
.setURL('https://aethex.bot/pricing#branding')
.setStyle(ButtonStyle.Link)
.setEmoji('💳')
);
return interaction.editReply({ embeds: [embed], components: [row] });
}
// Set custom name
if (subcommand === 'name') {
if (!hasFeature(branding.tier, 'custom_name')) {
return interaction.editReply({
content: '❌ Custom bot names require **Basic tier** or higher. Use `/branding tiers` to see pricing.',
ephemeral: true
});
}
const name = interaction.options.getString('name');
const result = await updateBranding(supabase, guildId, { custom_bot_name: name }, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor('#22c55e')
.setTitle('✅ Bot Name Updated')
.setDescription(`Bot will now appear as **${name}** in branded messages.`)
.setFooter({ text: 'Make sure branding is enabled with /branding toggle' })
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
// Set custom avatar
if (subcommand === 'avatar') {
if (!hasFeature(branding.tier, 'custom_avatar')) {
return interaction.editReply({
content: '❌ Custom avatars require **Pro tier** or higher. Use `/branding tiers` to see pricing.',
ephemeral: true
});
}
const url = interaction.options.getString('url');
// Basic URL validation
if (!url.startsWith('https://')) {
return interaction.editReply({ content: '❌ Avatar URL must start with https://' });
}
const result = await updateBranding(supabase, guildId, { custom_bot_avatar_url: url }, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor('#22c55e')
.setTitle('✅ Bot Avatar Updated')
.setDescription('Bot will now use your custom avatar in branded messages.')
.setThumbnail(url)
.setFooter({ text: 'Webhook-based messages will show custom avatar' })
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
// Set custom footer
if (subcommand === 'footer') {
if (!hasFeature(branding.tier, 'custom_footer')) {
return interaction.editReply({
content: '❌ Custom footers require **Basic tier** or higher. Use `/branding tiers` to see pricing.',
ephemeral: true
});
}
const text = interaction.options.getString('text');
const result = await updateBranding(supabase, guildId, { custom_footer_text: text }, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor('#22c55e')
.setTitle('✅ Footer Updated')
.setDescription('Embeds will now show your custom footer.')
.setFooter({ text: text })
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
// Set custom color
if (subcommand === 'color') {
if (!hasFeature(branding.tier, 'custom_color')) {
return interaction.editReply({
content: '❌ Custom colors require **Basic tier** or higher. Use `/branding tiers` to see pricing.',
ephemeral: true
});
}
let hex = interaction.options.getString('hex');
// Validate and normalize hex color
if (!hex.startsWith('#')) hex = '#' + hex;
if (!/^#[0-9A-Fa-f]{6}$/.test(hex)) {
return interaction.editReply({ content: '❌ Invalid hex color. Use format: #ff5500' });
}
const result = await updateBranding(supabase, guildId, { custom_embed_color: hex }, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor(hex)
.setTitle('✅ Embed Color Updated')
.setDescription(`Embeds will now use **${hex}** as the accent color.`)
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
// Claim custom handle
if (subcommand === 'handle') {
const handle = interaction.options.getString('handle');
const result = await claimHandle(supabase, guildId, handle, branding.tier);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor('#22c55e')
.setTitle('✅ Handle Claimed!')
.setDescription(`Your community page is now live at:\n\n🔗 **https://aethex.bot/${result.handle}**`)
.addFields(
{ name: 'Configure Your Page', value: 'Use `/branding landing` to customize your page content.' }
)
.setFooter({ text: 'Share this link to promote your server!' })
]
});
}
return interaction.editReply({ content: `${result.error}` });
}
// Configure landing page
if (subcommand === 'landing') {
if (!hasFeature(branding.tier, 'landing_page')) {
return interaction.editReply({
content: '❌ Landing pages require **Pro tier** or higher. Use `/branding tiers` to see pricing.',
ephemeral: true
});
}
if (!branding.custom_handle) {
return interaction.editReply({
content: '❌ You must claim a handle first with `/branding handle`',
ephemeral: true
});
}
const updates = {};
const title = interaction.options.getString('title');
const description = interaction.options.getString('description');
const banner = interaction.options.getString('banner');
const invite = interaction.options.getString('invite');
if (title) updates.landing_title = title;
if (description) updates.landing_description = description;
if (banner) updates.landing_banner_url = banner;
if (invite) updates.landing_invite_url = invite;
if (Object.keys(updates).length === 0) {
return interaction.editReply({
content: 'Please provide at least one option to update.',
ephemeral: true
});
}
const result = await updateBranding(supabase, guildId, updates, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor('#22c55e')
.setTitle('✅ Landing Page Updated')
.setDescription(`View your page at:\n🔗 **https://aethex.bot/${branding.custom_handle}**`)
.addFields(
Object.entries(updates).map(([key, value]) => ({
name: key.replace('landing_', '').replace('_', ' '),
value: value.length > 50 ? value.substring(0, 50) + '...' : value,
inline: true
}))
)
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
// Toggle branding
if (subcommand === 'toggle') {
const enabled = interaction.options.getBoolean('enabled');
if (enabled && branding.tier === 'free') {
return interaction.editReply({
content: '❌ Custom branding requires a paid tier. Use `/branding tiers` to see pricing.',
ephemeral: true
});
}
const result = await updateBranding(supabase, guildId, { branding_enabled: enabled }, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor(enabled ? '#22c55e' : '#ef4444')
.setTitle(enabled ? '✅ Branding Enabled' : '❌ Branding Disabled')
.setDescription(
enabled
? 'Bot messages will now use your custom branding!'
: 'Bot messages will show default AeThex | Warden branding.'
)
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
// Preview branded embed
if (subcommand === 'preview') {
const previewEmbed = await createBrandedEmbed(supabase, guildId, {
title: '🎉 Sample Announcement',
description: 'This is how your branded embeds will look!\n\nNotice the custom color and footer below.',
fields: [
{ name: 'Feature', value: 'Custom branding', inline: true },
{ name: 'Status', value: branding.branding_enabled ? '✅ Active' : '❌ Inactive', inline: true }
],
timestamp: true
});
return interaction.editReply({
content: branding.branding_enabled
? '**Preview of your branded embed:**'
: '**Preview (branding not enabled - showing defaults):**',
embeds: [previewEmbed]
});
}
// Reset branding
if (subcommand === 'reset') {
const result = await updateBranding(supabase, guildId, {
custom_bot_name: null,
custom_bot_avatar_url: null,
custom_footer_text: null,
custom_embed_color: null,
landing_title: null,
landing_description: null,
landing_banner_url: null,
branding_enabled: false
}, interaction.user.id);
if (result.success) {
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setColor('#f59e0b')
.setTitle('🔄 Branding Reset')
.setDescription('All custom branding has been cleared. Default AeThex | Warden branding restored.')
.setFooter({ text: 'Your custom handle has been preserved' })
]
});
}
return interaction.editReply({ content: `❌ Failed: ${result.error}` });
}
} catch (error) {
console.error('[Branding Command] Error:', error);
return interaction.editReply({ content: '❌ An error occurred. Please try again.' });
}
}
};