AeThex-Bot-Master/aethex-bot/commands/badges.js
sirpiglr 4a39363dfd Implement a new achievement system for tracking user progress and rewards
Adds a comprehensive achievement system to the bot, including new triggers for various user actions, the ability to create, manage, and view achievements, and integration with existing XP and leveling systems. This also involves updating user statistics tracking to support achievement triggers.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 28bc7e36-c36d-4b62-b518-bcc2c649398e
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/yTaZipL
Replit-Helium-Checkpoint-Created: true
2025-12-08 21:47:11 +00:00

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)
.single();
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)
.single();
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.' });
}
},
};