const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); const BADGE_INFO = { 'verified': { emoji: '✅', name: 'Verified', description: 'Linked Discord to AeThex' }, 'early_adopter': { emoji: '🌟', name: 'Early Adopter', description: 'Joined during early access' }, 'contributor': { emoji: '💎', name: 'Contributor', description: 'Contributed to the community' }, 'creator': { emoji: '🎨', name: 'Creator', description: 'Published projects on Studio' }, 'supporter': { emoji: '❤️', name: 'Supporter', description: 'Donated to the Foundation' }, 'level_10': { emoji: '🔟', name: 'Level 10', description: 'Reached level 10' }, 'level_25': { emoji: '🏆', name: 'Level 25', description: 'Reached level 25' }, 'level_50': { emoji: '👑', name: 'Level 50', description: 'Reached level 50' }, 'streak_7': { emoji: '🔥', name: 'Week Streak', description: '7 day daily claim streak' }, 'streak_30': { emoji: '💪', name: 'Month Streak', description: '30 day daily claim streak' }, 'helpful': { emoji: '🤝', name: 'Helpful', description: 'Helped 10+ community members' }, 'bug_hunter': { emoji: '🐛', name: 'Bug Hunter', description: 'Reported a valid bug' }, }; async function getServerAchievements(supabase, guildId, userId) { try { const [achievementsResult, earnedResult] = await Promise.all([ supabase.from('achievements').select('*').eq('guild_id', guildId), supabase.from('user_achievements').select('achievement_id').eq('user_id', userId).eq('guild_id', guildId) ]); const allAchievements = achievementsResult.data || []; const earnedIds = new Set((earnedResult.data || []).map(e => e.achievement_id)); return { earned: allAchievements.filter(a => earnedIds.has(a.id)), available: allAchievements.filter(a => !earnedIds.has(a.id) && !a.hidden) }; } catch (e) { return { earned: [], available: [] }; } } module.exports = { data: new SlashCommandBuilder() .setName('badges') .setDescription('View your earned badges across all platforms') .addUserOption(option => option.setName('user') .setDescription('User to view (defaults to yourself)') .setRequired(false) ), async execute(interaction, supabase, client) { if (!supabase) { return interaction.reply({ content: 'Database not configured.', ephemeral: true }); } const target = interaction.options.getUser('user') || interaction.user; await interaction.deferReply(); try { const { data: link } = await supabase .from('discord_links') .select('user_id') .eq('discord_id', target.id) .maybeSingle(); if (!link) { return interaction.editReply({ embeds: [ new EmbedBuilder() .setColor(0xff6b6b) .setDescription(`${target.id === interaction.user.id ? 'You are' : `${target.tag} is`} not linked to AeThex.`) ] }); } const { data: profile } = await supabase .from('user_profiles') .select('username, avatar_url, badges, xp, daily_streak') .eq('id', link.user_id) .maybeSingle(); let earnedBadges = []; if (profile?.badges) { earnedBadges = typeof profile.badges === 'string' ? JSON.parse(profile.badges) : profile.badges; } earnedBadges.push('verified'); const xp = profile?.xp || 0; const level = Math.floor(Math.sqrt(xp / 100)); if (level >= 10) earnedBadges.push('level_10'); if (level >= 25) earnedBadges.push('level_25'); if (level >= 50) earnedBadges.push('level_50'); const streak = profile?.daily_streak || 0; if (streak >= 7) earnedBadges.push('streak_7'); if (streak >= 30) earnedBadges.push('streak_30'); earnedBadges = [...new Set(earnedBadges)]; // Validate avatar URL - must be http/https, not base64 let avatarUrl = target.displayAvatarURL(); if (profile?.avatar_url && profile.avatar_url.startsWith('http')) { avatarUrl = profile.avatar_url; } const embed = new EmbedBuilder() .setColor(0x7c3aed) .setTitle(`${profile?.username || target.tag}'s Badges`) .setThumbnail(avatarUrl) .setTimestamp(); if (earnedBadges.length > 0) { const badgeDisplay = earnedBadges.map(key => { const info = BADGE_INFO[key]; if (info) { return `${info.emoji} **${info.name}**\n${info.description}`; } return `🏅 **${key}**`; }).join('\n\n'); embed.setDescription(badgeDisplay); embed.addFields({ name: 'Total Badges', value: `${earnedBadges.length}`, inline: true }); } else { embed.setDescription('No badges earned yet. Keep engaging to earn badges!'); } const allBadgeKeys = Object.keys(BADGE_INFO); const lockedBadges = allBadgeKeys.filter(k => !earnedBadges.includes(k)); if (lockedBadges.length > 0 && lockedBadges.length <= 6) { const lockedDisplay = lockedBadges.map(key => { const info = BADGE_INFO[key]; return `🔒 ${info.name}`; }).join(', '); embed.addFields({ name: 'Locked Badges', value: lockedDisplay }); } // Add server achievements const serverAchievements = await getServerAchievements(supabase, interaction.guildId, link.user_id); if (serverAchievements.earned.length > 0) { const serverBadgeDisplay = serverAchievements.earned.map(ach => `${ach.icon} **${ach.name}**\n${ach.description}` ).join('\n\n').slice(0, 1024); embed.addFields({ name: `Server Achievements (${serverAchievements.earned.length})`, value: serverBadgeDisplay }); } await interaction.editReply({ embeds: [embed] }); } catch (error) { console.error('Badges error:', error); await interaction.editReply({ content: 'Failed to fetch badge data.' }); } }, };