AeThex-Bot-Master/aethex-bot/commands/profile.js
sirpiglr d7b41b5b4f Introduce prestige system for permanent XP bonuses and rewards
Integrates a new prestige system across multiple commands, including daily rewards, profile, rank, reaction XP, voice XP, and message XP. This involves fetching and utilizing `prestige_level` and `total_xp_earned` from `user_profiles`, applying prestige-based XP multipliers, updating embed messages with prestige information, and adding new helper functions like `getPrestigeInfo` and `getPrestigeColor`. A new `/prestige` command is also introduced with subcommands to view prestige status, perform the prestige action, and view rewards.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 0d5b8d92-a33c-43bb-abda-1d75baaf3b97
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/ZjyNKqu
Replit-Helium-Checkpoint-Created: true
2025-12-08 21:21:59 +00:00

213 lines
6.7 KiB
JavaScript

const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("profile")
.setDescription("View your AeThex profile in Discord")
.addUserOption(option =>
option.setName('user')
.setDescription('User to view profile of')
.setRequired(false)
),
async execute(interaction, supabase) {
if (!supabase) {
return interaction.reply({ content: "This feature requires Supabase to be configured.", ephemeral: true });
}
await interaction.deferReply();
const targetUser = interaction.options.getUser('user') || interaction.user;
try {
const { data: link } = await supabase
.from("discord_links")
.select("user_id, primary_arm")
.eq("discord_id", targetUser.id)
.single();
if (!link) {
const embed = new EmbedBuilder()
.setColor(0xff6b6b)
.setTitle("❌ Not Linked")
.setThumbnail(targetUser.displayAvatarURL({ size: 256 }))
.setDescription(
targetUser.id === interaction.user.id
? "You must link your Discord account to AeThex first.\nUse `/verify` to get started."
: `${targetUser.tag} hasn't linked their Discord account to AeThex yet.`
);
return await interaction.editReply({ embeds: [embed] });
}
const { data: profile } = await supabase
.from("user_profiles")
.select("*")
.eq("id", link.user_id)
.single();
if (!profile) {
const embed = new EmbedBuilder()
.setColor(0xff6b6b)
.setTitle("❌ Profile Not Found")
.setDescription("The AeThex profile could not be found.");
return await interaction.editReply({ embeds: [embed] });
}
const armEmojis = {
labs: "🧪",
gameforge: "🎮",
corp: "💼",
foundation: "🤝",
devlink: "💻",
};
const armColors = {
labs: 0x22c55e,
gameforge: 0xf97316,
corp: 0x3b82f6,
foundation: 0xec4899,
devlink: 0x8b5cf6,
};
const xp = profile.xp || 0;
const prestige = profile.prestige_level || 0;
const level = Math.floor(Math.sqrt(xp / 100));
const currentLevelXp = level * level * 100;
const nextLevelXp = (level + 1) * (level + 1) * 100;
const progressXp = xp - currentLevelXp;
const neededXp = nextLevelXp - currentLevelXp;
const progressPercent = Math.min(100, Math.floor((progressXp / neededXp) * 100));
const progressBar = createProgressBar(progressPercent);
const prestigeInfo = getPrestigeInfo(prestige);
const badges = profile.badges || [];
const badgeDisplay = badges.length > 0
? badges.map(b => getBadgeEmoji(b)).join(' ')
: 'No badges yet';
// Validate avatar URL - must be http/https, not base64
let avatarUrl = targetUser.displayAvatarURL({ size: 256 });
if (profile.avatar_url && profile.avatar_url.startsWith('http')) {
avatarUrl = profile.avatar_url;
}
const embed = new EmbedBuilder()
.setColor(armColors[link.primary_arm] || 0x7c3aed)
.setAuthor({
name: `${profile.full_name || profile.username || 'AeThex User'}`,
iconURL: targetUser.displayAvatarURL({ size: 64 })
})
.setThumbnail(avatarUrl)
.setDescription(profile.bio || '*No bio set*')
.addFields(
{
name: "👤 Username",
value: `\`${profile.username || 'N/A'}\``,
inline: true,
},
{
name: `${armEmojis[link.primary_arm] || "⚔️"} Realm`,
value: capitalizeFirst(link.primary_arm) || "Not set",
inline: true,
},
{
name: "📊 Role",
value: formatRole(profile.user_type),
inline: true,
},
{
name: `${prestigeInfo.icon} Prestige`,
value: prestige > 0 ? `**${prestigeInfo.name}** (P${prestige}) +${prestige * 5}% XP` : 'Not prestiged',
inline: true,
},
{
name: `📈 Level ${level}`,
value: `${progressBar}\n\`${xp.toLocaleString()}\` / \`${nextLevelXp.toLocaleString()}\` XP`,
inline: false,
},
{
name: "🏆 Badges",
value: badgeDisplay,
inline: false,
}
)
.addFields({
name: "🔗 Links",
value: `[View Full Profile](https://aethex.dev/creators/${profile.username}) • [AeThex Platform](https://aethex.dev)`,
})
.setFooter({
text: `AeThex | ${targetUser.tag}`,
iconURL: 'https://aethex.dev/favicon.ico'
})
.setTimestamp();
if (profile.banner_url) {
embed.setImage(profile.banner_url);
}
await interaction.editReply({ embeds: [embed] });
} catch (error) {
console.error("Profile command error:", error);
const embed = new EmbedBuilder()
.setColor(0xff0000)
.setTitle("❌ Error")
.setDescription("Failed to fetch profile. Please try again.");
await interaction.editReply({ embeds: [embed] });
}
},
};
function createProgressBar(percent) {
const filled = Math.floor(percent / 10);
const empty = 10 - filled;
return `${'▓'.repeat(filled)}${'░'.repeat(empty)} ${percent}%`;
}
function capitalizeFirst(str) {
if (!str) return str;
return str.charAt(0).toUpperCase() + str.slice(1);
}
function formatRole(role) {
if (!role) return 'Member';
return role.split('_').map(capitalizeFirst).join(' ');
}
function getBadgeEmoji(badge) {
const badgeMap = {
'verified': '✅',
'founder': '👑',
'early_adopter': '🌟',
'contributor': '💎',
'creator': '🎨',
'developer': '💻',
'moderator': '🛡️',
'partner': '🤝',
'premium': '💫',
'top_poster': '📝',
'helpful': '❤️',
'bug_hunter': '🐛',
'event_winner': '🏆',
};
return badgeMap[badge] || `[${badge}]`;
}
function getPrestigeInfo(level) {
const prestiges = [
{ name: 'Unprestiged', icon: '⚪', color: 0x6b7280 },
{ name: 'Bronze', icon: '🥉', color: 0xcd7f32 },
{ name: 'Silver', icon: '🥈', color: 0xc0c0c0 },
{ name: 'Gold', icon: '🥇', color: 0xffd700 },
{ name: 'Platinum', icon: '💎', color: 0xe5e4e2 },
{ name: 'Diamond', icon: '💠', color: 0xb9f2ff },
{ name: 'Master', icon: '🔥', color: 0xff4500 },
{ name: 'Grandmaster', icon: '⚔️', color: 0x9400d3 },
{ name: 'Champion', icon: '👑', color: 0xffd700 },
{ name: 'Legend', icon: '🌟', color: 0xff69b4 },
{ name: 'Mythic', icon: '🌈', color: 0x7c3aed }
];
return prestiges[Math.min(level, 10)] || prestiges[0];
}