import { SlashCommandBuilder, EmbedBuilder } from 'discord.js'; import { withConn } from '../db/pool.js'; export const data = new SlashCommandBuilder() .setName('profile') .setDescription('Show XP profile') .addUserOption(o => o.setName('user').setDescription('User to view') ); export async function execute(interaction) { if (!interaction.guildId) return interaction.reply({ content: 'Guild only command.', ephemeral: true, }); const target = interaction.options.getUser('user') || interaction.user; const data = await withConn(async conn => { const rows = await conn.query( 'SELECT xp FROM user_xp WHERE guild_id = ? AND user_id = ? LIMIT 1', [BigInt(interaction.guildId), BigInt(target.id)] ); const xp = Number(rows[0]?.xp ?? 0); const rankRows = await conn.query( 'SELECT COUNT(*) AS ahead FROM user_xp WHERE guild_id = ? AND xp > ?', [BigInt(interaction.guildId), xp] ); const rank = Number(rankRows[0]?.ahead ?? 0) + 1; return { xp, rank }; }); const XP_PER_LEVEL = 500; // match visual example const level = Math.floor(data.xp / XP_PER_LEVEL); const currentLevelXp = data.xp % XP_PER_LEVEL; const progress = Math.min( 100, Math.round((currentLevelXp / XP_PER_LEVEL) * 1000) / 10 ); const embed = new EmbedBuilder() .setAuthor({ name: `${target.username}'s Profile`, iconURL: target.displayAvatarURL(), }) .setColor(0x5865f2) .setDescription(`Progress to next level: **${progress}%**`) .addFields( { name: '🏅 Level', value: `${level}`, inline: true }, { name: '💎 XP', value: `${currentLevelXp} / ${XP_PER_LEVEL}`, inline: true }, { name: '🏆 Rank', value: `#${data.rank}`, inline: true } ) .setFooter({ text: `Server: ${interaction.guild?.name || 'Unknown'}`, }) .setTimestamp(); await interaction.reply({ embeds: [embed], allowedMentions: { parse: [] }, }); }