Replaced `.single()` with `.maybeSingle()` in multiple command files to handle cases where no record is found, and added a new /pricing route and navigation links to the UI. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: e91d020a-35a6-4add-9945-887dd3ecae9f Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/tdDjujk Replit-Helium-Checkpoint-Created: true
159 lines
5.9 KiB
JavaScript
159 lines
5.9 KiB
JavaScript
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.' });
|
|
}
|
|
},
|
|
};
|