const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags } = require("discord.js"); const { mClient } = require("../index"); const { getCookieBalance, getUnixTime } = require("./cookieEconomy"); const DATABASE_NAME = process.env.M_DB; const COLLECTIONS = { stats: "stats_profiles", achievements: "items_achievements", badges: "guild_badges", achievementConfig: "config_achievements" }; function getProfileCollections() { if (!DATABASE_NAME) { throw new Error("M_DB is not configured in .env"); } const db = mClient.db(DATABASE_NAME); return { db, stats: db.collection(COLLECTIONS.stats), achievements: db.collection(COLLECTIONS.achievements), badges: db.collection(COLLECTIONS.badges), achievementConfig: db.collection(COLLECTIONS.achievementConfig) }; } async function ensureProfileSystem() { const { stats, achievements, badges, achievementConfig } = getProfileCollections(); await stats.createIndex( { userId: 1, guildId: 1 }, { unique: true } ); await achievements.createIndex( { userId: 1, guildId: 1 }, { unique: true } ); await badges.createIndex( { guildId: 1, badgeId: 1 }, { unique: true } ); await achievementConfig.createIndex( { achievementId: 1 }, { unique: true } ); } function getDefaultStats(userId, guildId) { return { userId, guildId, economy: { cookiesObtainedTotal: 0, cookiesLostTotal: 0, cookiesGivenTotal: 0, stolenTotal: 0, stolenFromMeTotal: 0, vaultStolenTotal: 0 }, unique: { playersStolenFrom: [], playersStolenBy: [] }, games: { total: { played: 0, won: 0, lost: 0, pushed: 0 }, slots: { played: 0, won: 0, lost: 0, pushed: 0, cookiesWon: 0, cookiesLost: 0 }, blackjack: { played: 0, won: 0, lost: 0, pushed: 0, cookiesWon: 0, cookiesLost: 0 }, roulette: { played: 0, won: 0, lost: 0, pushed: 0, cookiesWon: 0, cookiesLost: 0 }, heist: { played: 0, won: 0, lost: 0, pushed: 0, cookiesWon: 0, cookiesLost: 0 } }, streaks: { currentWin: 0, bestWin: 0, currentLoss: 0, bestLoss: 0 } }; } async function ensureStatsDocument(userId, guildId) { const { stats } = getProfileCollections(); await stats.updateOne( { userId, guildId }, { $setOnInsert: getDefaultStats( userId, guildId ) }, { upsert: true } ); } async function ensureAchievementDocument(userId, guildId) { const { achievements } = getProfileCollections(); await achievements.updateOne( { userId, guildId }, { $setOnInsert: { userId, guildId, unlocked: {} } }, { upsert: true } ); } async function getProfileStats(userId, guildId) { const { stats } = getProfileCollections(); return await stats.findOne({ userId, guildId }) ?? getDefaultStats( userId, guildId ); } async function trackProfileEvent( interaction, userId, event ) { const guildId = interaction.guildId; if ( !guildId || !userId || !event?.type ) { return; } await ensureStatsDocument( userId, guildId ); const { stats } = getProfileCollections(); switch (event.type) { case "cookie_gain": await stats.updateOne( { userId, guildId }, { $inc: { "economy.cookiesObtainedTotal": event.amount } } ); break; case "cookie_loss": await stats.updateOne( { userId, guildId }, { $inc: { "economy.cookiesLostTotal": event.amount } } ); break; case "cookies_given": await stats.updateOne( { userId, guildId }, { $inc: { "economy.cookiesGivenTotal": event.amount } } ); break; case "steal_success": await stats.updateOne( { userId, guildId }, { $inc: { "economy.stolenTotal": event.amount }, $addToSet: { "unique.playersStolenFrom": event.targetUserId } } ); break; case "stolen_from_me": await stats.updateOne( { userId, guildId }, { $inc: { "economy.stolenFromMeTotal": event.amount }, $addToSet: { "unique.playersStolenBy": event.thiefUserId } } ); break; case "vault_steal": await stats.updateOne( { userId, guildId }, { $inc: { "economy.vaultStolenTotal": event.amount } } ); break; case "game_result": await trackGameResult( userId, guildId, event ); break; default: return; } const updatedStats = await getProfileStats( userId, guildId ); await evaluateAchievements( interaction, userId, updatedStats ); await evaluateBadges( interaction, userId, updatedStats, event ); return updatedStats; } async function trackGameResult( userId, guildId, event ) { const { stats } = getProfileCollections(); const game = String( event.game ?? "" ).toLowerCase(); if ( ![ "slots", "blackjack", "roulette", "heist" ].includes(game) ) { return; } const result = String( event.result ?? "" ).toLowerCase(); const profit = Number( event.profit ?? 0 ); const increment = { "games.total.played": 1, [`games.${game}.played`]: 1 }; const set = {}; if (result === "win") { increment["games.total.won"] = 1; increment[`games.${game}.won`] = 1; increment["streaks.currentWin"] = 1; set["streaks.currentLoss"] = 0; } else if (result === "loss") { increment["games.total.lost"] = 1; increment[`games.${game}.lost`] = 1; increment["streaks.currentLoss"] = 1; set["streaks.currentWin"] = 0; } else if (result === "push") { increment["games.total.pushed"] = 1; increment[`games.${game}.pushed`] = 1; } else { return; } if (Number.isFinite(profit)) { if (profit > 0) { increment[ `games.${game}.cookiesWon` ] = profit; } else if (profit < 0) { increment[ `games.${game}.cookiesLost` ] = Math.abs(profit); } } await stats.updateOne( { userId, guildId }, { $inc: increment, ...(Object.keys(set).length ? { $set: set } : {}) } ); const current = await stats.findOne({ userId, guildId }); await stats.updateOne( { userId, guildId }, { $max: { "streaks.bestWin": current?.streaks ?.currentWin ?? 0, "streaks.bestLoss": current?.streaks ?.currentLoss ?? 0 } } ); } async function evaluateAchievements( interaction, userId, statsDocument ) { const guildId = interaction.guildId; const { achievements, achievementConfig } = getProfileCollections(); await ensureAchievementDocument( userId, guildId ); const definitions = await achievementConfig .find({ active: { $ne: false } }) .toArray(); let unlockedDoc = await achievements.findOne({ userId, guildId }); let unlocked = unlockedDoc?.unlocked ?? {}; const normalDefinitions = definitions.filter( definition => definition.measure !== "achievementPercentage" ); for (const definition of normalDefinitions) { if (unlocked[definition.achievementId]) { continue; } const currentValue = readStatValue( statsDocument, definition ); if ( currentValue < Number(definition.target) ) { continue; } const unlockedNow = await unlockAchievement( interaction, userId, guildId, definition ); if (unlockedNow) { unlocked[ definition.achievementId ] = getUnixTime(); } } unlockedDoc = await achievements.findOne({ userId, guildId }); unlocked = unlockedDoc?.unlocked ?? {}; const collectorDefinitions = definitions.filter( definition => definition.measure === "achievementPercentage" ); const collectibleDefinitions = definitions.filter( definition => definition.measure !== "achievementPercentage" ); const totalCollectible = collectibleDefinitions.length; const unlockedCollectible = collectibleDefinitions.filter( definition => Boolean( unlocked[ definition.achievementId ] ) ).length; const achievementPercentage = totalCollectible > 0 ? unlockedCollectible / totalCollectible : 0; for (const definition of collectorDefinitions) { if (unlocked[definition.achievementId]) { continue; } if ( achievementPercentage < Number(definition.target) ) { continue; } const unlockedNow = await unlockAchievement( interaction, userId, guildId, definition ); if (unlockedNow) { unlocked[ definition.achievementId ] = getUnixTime(); } } } async function unlockAchievement( interaction, userId, guildId, definition ) { const { achievements } = getProfileCollections(); const field = `unlocked.${definition.achievementId}`; const result = await achievements.updateOne( { userId, guildId, [field]: { $exists: false } }, { $set: { [field]: getUnixTime() } } ); if (result.modifiedCount !== 1) { return false; } await sendAchievementNotification( interaction, definition ); return true; } function readStatValue( document, definition ) { if (!definition.stat) { return 0; } const value = definition.stat .split(".") .reduce( (current, key) => current?.[key], document ); if ( definition.measure === "arrayLength" ) { return Array.isArray(value) ? value.length : 0; } return Number(value ?? 0); } async function sendAchievementNotification( interaction, achievement ) { try { await interaction.followUp({ embeds: [ new EmbedBuilder() .setTitle( "🏆 Achievement Unlocked!" ) .setDescription( [ `${achievement.emoji} **${achievement.name}**`, "", achievement.description ].join("\n") ) ] }); } catch {} } async function evaluateBadges( interaction, userId, statsDocument, event = {} ) { const guildId = interaction.guildId; if (!guildId) { return; } const balance = await getCookieBalance( userId ); if (balance >= 100000) { await tryClaimBadge( interaction, userId, "first_100k_cookies" ); } if (balance >= 1000000) { await tryClaimBadge( interaction, userId, "first_1m_cookies" ); } if ( ( statsDocument ?.economy ?.vaultStolenTotal ?? 0 ) >= 100000 ) { await tryClaimBadge( interaction, userId, "first_100k_vault_stolen" ); } const uniquePlayersStolenFrom = statsDocument ?.unique ?.playersStolenFrom ?? []; const uniqueStealCount = uniquePlayersStolenFrom.length; if (uniqueStealCount >= 5) { await tryClaimBadge( interaction, userId, "first_unique_steals_5" ); } if (uniqueStealCount >= 10) { await tryClaimBadge( interaction, userId, "first_unique_steals_10" ); } if (uniqueStealCount >= 15) { await tryClaimBadge( interaction, userId, "first_unique_steals_15" ); } if ( event.type === "game_result" && event.game === "slots" && event.jackpot === true ) { await tryClaimBadge( interaction, userId, "first_slots_jackpot" ); } if ( event.type === "game_result" && event.game === "blackjack" && event.natural === true && event.result === "win" ) { await tryClaimBadge( interaction, userId, "first_blackjack_natural" ); } if ( event.type === "game_result" && event.game === "blackjack" && event.allIn === true && event.result === "win" ) { await tryClaimBadge( interaction, userId, "first_blackjack_allin_win" ); } if ( event.type === "game_result" && event.game === "roulette" && event.zero === true && event.result === "win" ) { await tryClaimBadge( interaction, userId, "first_roulette_zero" ); } if ( event.type === "game_result" && event.game === "roulette" && event.allIn === true && event.result === "win" ) { await tryClaimBadge( interaction, userId, "first_roulette_allin_win" ); } await evaluateCollectorBadges( interaction, userId ); } async function evaluateCollectorBadges( interaction, userId ) { const guildId = interaction.guildId; const { achievements, achievementConfig } = getProfileCollections(); const activeAchievements = await achievementConfig .find({ active: { $ne: false }, measure: { $ne: "achievementPercentage" } }) .toArray(); if ( activeAchievements.length === 0 ) { return; } const achievementDocument = await achievements.findOne({ userId, guildId }); const unlocked = achievementDocument?.unlocked ?? {}; const unlockedCount = activeAchievements.filter( achievement => Boolean( unlocked[ achievement.achievementId ] ) ).length; const percentage = unlockedCount / activeAchievements.length; if (percentage >= 0.5) { await tryClaimBadge( interaction, userId, "first_achievement_half", { unlockedAchievements: unlockedCount, availableAchievements: activeAchievements.length } ); } if (percentage >= 1) { await tryClaimBadge( interaction, userId, "first_achievement_all", { unlockedAchievements: unlockedCount, availableAchievements: activeAchievements.length } ); } } async function tryClaimBadge( interaction, userId, badgeId, metadata = null ) { const { badges } = getProfileCollections(); const guildId = interaction.guildId; if (!guildId) { return false; } const update = { ownerId: userId, unlockedAt: getUnixTime() }; if ( metadata && typeof metadata === "object" ) { update.metadata = metadata; } const badge = await badges.findOneAndUpdate( { guildId, badgeId, ownerId: null }, { $set: update }, { returnDocument: "after" } ); if ( !badge || badge.ownerId !== userId ) { return false; } const description = [ `${badge.emoji ?? "🏅"} **${badge.name ?? badge.badgeId}**`, "", `<@${userId}> claimed a guild-unique badge!`, "", badge.description ?? "A unique badge has been claimed.", "", "*This badge can no longer be earned by another player.*" ]; if ( badge.metadata ?.unlockedAchievements !== undefined && badge.metadata ?.availableAchievements !== undefined ) { description.splice( description.length - 1, 0, `Claimed at **${badge.metadata.unlockedAchievements}/${badge.metadata.availableAchievements} achievements**.`, "" ); } try { await interaction.channel.send({ embeds: [ new EmbedBuilder() .setTitle( "🏅 Unique Badge Claimed!" ) .setDescription( description.join("\n") ) .setFooter({ text: `badge:${badge.badgeId}` }) .setTimestamp() ] }); } catch (error) { console.error( `[Badge] Failed to announce ${badgeId} for ${userId}:`, error ); } return true; } async function buildProfileOverview( guildId, user, sessionOwnerId = user.id ) { const stats = await getProfileStats( user.id, guildId ); const balance = await getCookieBalance( user.id ); const { achievements, badges, achievementConfig } = getProfileCollections(); const [ achievementDoc, achievementCount, ownedBadges ] = await Promise.all([ achievements.findOne({ userId: user.id, guildId }), achievementConfig.countDocuments({ active: { $ne: false } }), badges.find({ guildId, ownerId: user.id }).toArray() ]); const unlockedCount = Object.keys( achievementDoc?.unlocked ?? {} ).length; return new EmbedBuilder() .setTitle( `👤 ${user.globalName ?? user.username}'s Profile` ) .setThumbnail( user.displayAvatarURL({ size: 256 }) ) .addFields( { name: "🍪 Cookies", value: balance.toLocaleString(), inline: true }, { name: "🏆 Achievements", value: `${unlockedCount} / ${achievementCount}`, inline: true }, { name: "🏅 Unique Badges", value: `${ownedBadges.length}`, inline: true }, { name: "🎮 Games", value: [ `Played: **${stats.games.total.played}**`, `Won: **${stats.games.total.won}**`, `Lost: **${stats.games.total.lost}**`, `Pushes: **${stats.games.total.pushed}**` ].join("\n"), inline: true }, { name: "🔥 Best Streaks", value: [ `Wins: **${stats.streaks.bestWin}**`, `Losses: **${stats.streaks.bestLoss}**` ].join("\n"), inline: true } ) .setFooter({ text: buildProfileFooter( sessionOwnerId, user.id, "overview", 0, 1 ) }); } async function buildStatsEmbed( guildId, user, sessionOwnerId = user.id ) { const stats = await getProfileStats( user.id, guildId ); const gameLines = [ ["🎰 Slots", stats.games.slots], ["🃏 Blackjack", stats.games.blackjack], ["🎡 Roulette", stats.games.roulette], ["🏦 Heist", stats.games.heist] ].map( ([name, game]) => [ `**${name}**`, `Played ${game?.played ?? 0} • Won ${game?.won ?? 0} • Lost ${game?.lost ?? 0} • Push ${game?.pushed ?? 0}`, `Net tracked: **+${(game?.cookiesWon ?? 0).toLocaleString()} / -${(game?.cookiesLost ?? 0).toLocaleString()} 🍪**` ].join("\n") ); return new EmbedBuilder() .setTitle( `📊 ${user.globalName ?? user.username}'s Stats` ) .setThumbnail( user.displayAvatarURL({ size: 256 }) ) .addFields( { name: "🍪 Economy", value: [ `Obtained: **${(stats.economy?.cookiesObtainedTotal ?? 0).toLocaleString()} 🍪**`, `Lost: **${(stats.economy?.cookiesLostTotal ?? 0).toLocaleString()} 🍪**`, `Given: **${(stats.economy?.cookiesGivenTotal ?? 0).toLocaleString()} 🍪**`, `Stolen: **${(stats.economy?.stolenTotal ?? 0).toLocaleString()} 🍪**`, `Stolen from you: **${(stats.economy?.stolenFromMeTotal ?? 0).toLocaleString()} 🍪**`, `Vault stolen: **${(stats.economy?.vaultStolenTotal ?? 0).toLocaleString()} 🍪**` ].join("\n") }, { name: "🥷 Unique Players", value: [ `Stolen from: **${stats.unique?.playersStolenFrom?.length ?? 0}**`, `Stolen by: **${stats.unique?.playersStolenBy?.length ?? 0}**` ].join("\n"), inline: true }, { name: "🔥 Streaks", value: [ `Current wins: **${stats.streaks?.currentWin ?? 0}**`, `Best wins: **${stats.streaks?.bestWin ?? 0}**`, `Current losses: **${stats.streaks?.currentLoss ?? 0}**`, `Best losses: **${stats.streaks?.bestLoss ?? 0}**` ].join("\n"), inline: true }, { name: "🎮 Per Game", value: gameLines.join("\n\n") } ) .setFooter({ text: buildProfileFooter( sessionOwnerId, user.id, "stats", 0, 1 ) }); } async function buildBadgesEmbed( guildId, user, sessionOwnerId = user.id, page = 0 ) { const { badges } = getProfileCollections(); const documents = await badges.find({ guildId }).sort({ category: 1, name: 1 }).toArray(); const owned = documents.filter( badge => badge.ownerId === user.id ); const pages = []; pages.push({ title: "Owned", content: owned.length ? owned.map( badge => formatBadgeLine( badge, user.id, true ) ).join("\n\n") : "*No unique badges yet.*" }); const categoryOrder = [ "economy", "stealing", "slots", "blackjack", "roulette", "heist", "collector" ]; const categories = [...new Set( documents.map( badge => badge.category ?? "other" ) )].sort( (a, b) => { const ai = categoryOrder.indexOf(a); const bi = categoryOrder.indexOf(b); if ( ai === -1 && bi === -1 ) { return a.localeCompare(b); } if (ai === -1) { return 1; } if (bi === -1) { return -1; } return ai - bi; } ); for (const category of categories) { const categoryBadges = documents.filter( badge => (badge.category ?? "other") === category ); if (!categoryBadges.length) { continue; } const chunks = paginateLines( categoryBadges.map( badge => formatBadgeLine( badge, user.id, false ) ), 3200, "\n\n" ); for ( let index = 0; index < chunks.length; index++ ) { pages.push({ title: `${formatCategoryName(category)}${chunks.length > 1 ? ` ${index + 1}/${chunks.length}` : ""}`, content: chunks[index] }); } } const safePage = clampPage( page, pages.length ); const selected = pages[safePage] ?? { title: "Badges", content: "No badges configured." }; return new EmbedBuilder() .setTitle( `🏅 ${user.globalName ?? user.username}'s Badges` ) .setDescription( [ `### ${selected.title}`, selected.content ].join("\n") ) .setFooter({ text: buildProfileFooter( sessionOwnerId, user.id, "badges", safePage, pages.length ) }); } async function buildAchievementsEmbed( guildId, user, sessionOwnerId = user.id, page = 0 ) { const { achievements, achievementConfig } = getProfileCollections(); const [ unlockedDoc, definitions ] = await Promise.all([ achievements.findOne({ userId: user.id, guildId }), achievementConfig .find({ active: { $ne: false } }) .sort({ category: 1, target: 1, name: 1 }) .toArray() ]); const unlocked = unlockedDoc?.unlocked ?? {}; const lines = definitions.map( achievement => { const complete = Boolean( unlocked[ achievement.achievementId ] ); return [ complete ? "✅" : "⬜", " ", achievement.emoji ?? "🏆", " **", achievement.name, "** — ", achievement.requirement ?? achievement.description ?? "Requirement unavailable." ].join(""); } ); const pages = paginateLines( lines, 3400, "\n" ); if (!pages.length) { pages.push( "No achievements configured." ); } const safePage = clampPage( page, pages.length ); const unlockedCount = definitions.filter( achievement => Boolean( unlocked[ achievement.achievementId ] ) ).length; return new EmbedBuilder() .setTitle( `🏆 ${user.globalName ?? user.username}'s Achievements` ) .setDescription( pages[safePage] ) .addFields({ name: "Progress", value: `**${unlockedCount} / ${definitions.length}** achievements unlocked` }) .setFooter({ text: buildProfileFooter( sessionOwnerId, user.id, "achievements", safePage, pages.length ) }); } function buildProfileButtons( section = "overview", page = 0, pageCount = 1 ) { const rows = [ new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId( "profileOverview" ) .setLabel( "Overview" ) .setEmoji( "👤" ) .setStyle( section === "overview" ? ButtonStyle.Primary : ButtonStyle.Secondary ), new ButtonBuilder() .setCustomId( "profileStats" ) .setLabel( "Stats" ) .setEmoji( "📊" ) .setStyle( section === "stats" ? ButtonStyle.Primary : ButtonStyle.Secondary ), new ButtonBuilder() .setCustomId( "profileBadges" ) .setLabel( "Badges" ) .setEmoji( "🏅" ) .setStyle( section === "badges" ? ButtonStyle.Primary : ButtonStyle.Secondary ), new ButtonBuilder() .setCustomId( "profileAchievements" ) .setLabel( "Achievements" ) .setEmoji( "🏆" ) .setStyle( section === "achievements" ? ButtonStyle.Primary : ButtonStyle.Secondary ), new ButtonBuilder() .setCustomId( "profileClose" ) .setLabel( "Close" ) .setEmoji( "✖️" ) .setStyle( ButtonStyle.Danger ) ) ]; if ( [ "badges", "achievements" ].includes(section) && pageCount > 1 ) { const prefix = section === "badges" ? "profileBadges" : "profileAchievements"; rows.push( new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId( `${prefix}Left` ) .setLabel( "Previous" ) .setEmoji( "◀️" ) .setDisabled( page <= 0 ) .setStyle( ButtonStyle.Secondary ), new ButtonBuilder() .setCustomId( `${prefix}Page` ) .setLabel( `${page + 1} / ${pageCount}` ) .setDisabled( true ) .setStyle( ButtonStyle.Secondary ), new ButtonBuilder() .setCustomId( `${prefix}Right` ) .setLabel( "Next" ) .setEmoji( "▶️" ) .setDisabled( page >= pageCount - 1 ) .setStyle( ButtonStyle.Secondary ) ) ); } return rows; } function buildProfileFooter( sessionOwnerId, profileUserId, section, page, pageCount ) { return [ "profile", sessionOwnerId, profileUserId, section, page, pageCount ].join(":"); } function getProfileContext( interaction ) { const footer = interaction.message ?.embeds?.[0] ?.footer ?.text; return getProfileContextFromFooter( footer ); } function getProfileContextFromFooter( footer ) { const [ type, sessionOwnerId, profileUserId, section = "overview", rawPage = "0", rawPageCount = "1" ] = footer?.split(":") ?? []; if ( type !== "profile" || !sessionOwnerId || !profileUserId ) { return null; } return { sessionOwnerId, profileUserId, section, page: Number(rawPage) || 0, pageCount: Math.max( 1, Number(rawPageCount) || 1 ) }; } function getProfileTargetId( interaction ) { return getProfileContext( interaction )?.profileUserId ?? null; } async function requireProfileOwner( interaction ) { const context = getProfileContext( interaction ); if (!context) { await replyProfileError( interaction, "This profile session is no longer valid." ); return null; } if ( context.sessionOwnerId !== interaction.user.id ) { await replyProfileError( interaction, "This isn't your profile session." ); return null; } return context; } async function replyProfileError( interaction, content ) { const payload = { content, flags: MessageFlags.Ephemeral }; if ( interaction.replied || interaction.deferred ) { return interaction.followUp( payload ); } return interaction.reply( payload ); } function paginateLines( lines, maxLength = 3400, separator = "\n" ) { const pages = []; let current = []; for (const line of lines) { const candidate = [...current, line].join( separator ); if ( candidate.length > maxLength && current.length ) { pages.push( current.join( separator ) ); current = [line]; } else { current.push(line); } } if (current.length) { pages.push( current.join( separator ) ); } return pages; } function clampPage( page, pageCount ) { return Math.max( 0, Math.min( Number(page) || 0, Math.max( 0, pageCount - 1 ) ) ); } function formatCategoryName( category ) { const labels = { economy: "🍪 Economy", stealing: "🥷 Stealing", slots: "🎰 Slots", blackjack: "🃏 Blackjack", roulette: "🎡 Roulette", heist: "🏦 Heist", collector: "🏆 Collector", other: "🏅 Other" }; return labels[category] ?? `🏅 ${category.charAt(0).toUpperCase()}${category.slice(1)}`; } function formatBadgeLine( badge, profileUserId, ownedPage ) { const emoji = badge.emoji ?? "🏅"; const description = badge.description ?? "No description available."; if (ownedPage) { return `${emoji} **${badge.name}**\n${description}`; } let status; if ( badge.ownerId === profileUserId ) { status = "✅ **Owned by this player**"; } else if (badge.ownerId) { status = `🔒 Claimed by <@${badge.ownerId}>`; } else { status = "🟢 **Available**"; } return [ `${emoji} **${badge.name}**`, description, status ].join("\n"); } module.exports = { ensureProfileSystem, trackProfileEvent, getProfileStats, buildProfileOverview, buildStatsEmbed, buildBadgesEmbed, buildAchievementsEmbed, buildProfileButtons, getProfileContext, getProfileContextFromFooter, getProfileTargetId, requireProfileOwner };