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
147 lines
5.1 KiB
JavaScript
147 lines
5.1 KiB
JavaScript
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
|
const { checkAchievements } = require('./achievements');
|
|
const { getUserStats, calculateLevel } = require('../listeners/xpTracker');
|
|
|
|
const DAILY_XP = 50;
|
|
const STREAK_BONUS = 10;
|
|
const MAX_STREAK_BONUS = 100;
|
|
|
|
module.exports = {
|
|
data: new SlashCommandBuilder()
|
|
.setName('daily')
|
|
.setDescription('Claim your daily XP bonus'),
|
|
|
|
async execute(interaction, supabase, client) {
|
|
if (!supabase) {
|
|
return interaction.reply({ content: 'Database not configured.', ephemeral: true });
|
|
}
|
|
|
|
await interaction.deferReply();
|
|
|
|
try {
|
|
const { data: link } = await supabase
|
|
.from('discord_links')
|
|
.select('user_id')
|
|
.eq('discord_id', interaction.user.id)
|
|
.single();
|
|
|
|
if (!link) {
|
|
return interaction.editReply({
|
|
embeds: [
|
|
new EmbedBuilder()
|
|
.setColor(0xff6b6b)
|
|
.setDescription('You need to link your account first! Use `/verify` to get started.')
|
|
]
|
|
});
|
|
}
|
|
|
|
const { data: profile } = await supabase
|
|
.from('user_profiles')
|
|
.select('xp, daily_streak, last_daily, prestige_level, total_xp_earned')
|
|
.eq('id', link.user_id)
|
|
.single();
|
|
|
|
const now = new Date();
|
|
const lastDaily = profile?.last_daily ? new Date(profile.last_daily) : null;
|
|
const currentXp = profile?.xp || 0;
|
|
const prestige = profile?.prestige_level || 0;
|
|
let streak = profile?.daily_streak || 0;
|
|
|
|
if (lastDaily) {
|
|
const hoursSince = (now - lastDaily) / (1000 * 60 * 60);
|
|
|
|
if (hoursSince < 20) {
|
|
const nextClaim = new Date(lastDaily.getTime() + 20 * 60 * 60 * 1000);
|
|
return interaction.editReply({
|
|
embeds: [
|
|
new EmbedBuilder()
|
|
.setColor(0xfbbf24)
|
|
.setTitle('Already Claimed!')
|
|
.setDescription(`You've already claimed your daily XP.\nNext claim: <t:${Math.floor(nextClaim.getTime() / 1000)}:R>`)
|
|
.addFields({ name: 'Current Streak', value: `🔥 ${streak} days` })
|
|
]
|
|
});
|
|
}
|
|
|
|
if (hoursSince > 48) {
|
|
streak = 0;
|
|
}
|
|
}
|
|
|
|
streak += 1;
|
|
const streakBonus = Math.min(streak * STREAK_BONUS, MAX_STREAK_BONUS);
|
|
|
|
// Prestige level 4+ gets bonus daily XP (+25)
|
|
const prestigeDailyBonus = prestige >= 4 ? 25 : 0;
|
|
|
|
// Base total before prestige multiplier
|
|
let totalXp = DAILY_XP + streakBonus + prestigeDailyBonus;
|
|
|
|
// Apply prestige XP bonus (+5% per prestige level)
|
|
if (prestige > 0) {
|
|
const prestigeMultiplier = 1 + (prestige * 0.05);
|
|
totalXp = Math.floor(totalXp * prestigeMultiplier);
|
|
}
|
|
|
|
const newXp = currentXp + totalXp;
|
|
const totalEarned = (profile?.total_xp_earned || currentXp) + totalXp;
|
|
|
|
await supabase
|
|
.from('user_profiles')
|
|
.update({
|
|
xp: newXp,
|
|
daily_streak: streak,
|
|
last_daily: now.toISOString(),
|
|
total_xp_earned: totalEarned
|
|
})
|
|
.eq('id', link.user_id);
|
|
|
|
const newLevel = Math.floor(Math.sqrt(newXp / 100));
|
|
const oldLevel = Math.floor(Math.sqrt(currentXp / 100));
|
|
|
|
const embed = new EmbedBuilder()
|
|
.setColor(prestige > 0 ? getPrestigeColor(prestige) : 0x00ff00)
|
|
.setTitle('Daily Reward Claimed!')
|
|
.setDescription(`You received **+${totalXp} XP**!${prestige > 0 ? ` *(includes P${prestige} bonus)*` : ''}`)
|
|
.addFields(
|
|
{ name: 'Base XP', value: `+${DAILY_XP}`, inline: true },
|
|
{ name: 'Streak Bonus', value: `+${streakBonus}`, inline: true },
|
|
{ name: 'Current Streak', value: `🔥 ${streak} days`, inline: true },
|
|
{ name: 'Total XP', value: newXp.toLocaleString(), inline: true },
|
|
{ name: 'Level', value: `${newLevel}`, inline: true }
|
|
);
|
|
|
|
if (prestige > 0) {
|
|
embed.addFields({ name: 'Prestige Bonus', value: `+${prestige * 5}% XP${prestigeDailyBonus > 0 ? ` + ${prestigeDailyBonus} daily bonus` : ''}`, inline: true });
|
|
}
|
|
|
|
embed.setFooter({ text: 'Come back tomorrow to keep your streak!' })
|
|
.setTimestamp();
|
|
|
|
if (newLevel > oldLevel) {
|
|
embed.addFields({ name: '🎉 Level Up!', value: `You reached level ${newLevel}!` });
|
|
}
|
|
|
|
await interaction.editReply({ embeds: [embed] });
|
|
|
|
// Check achievements with updated stats
|
|
const guildId = interaction.guildId;
|
|
const stats = await getUserStats(supabase, link.user_id, guildId);
|
|
stats.level = newLevel;
|
|
stats.prestige = prestige;
|
|
stats.totalXp = totalEarned;
|
|
stats.dailyStreak = streak;
|
|
|
|
await checkAchievements(link.user_id, interaction.member, stats, supabase, guildId, client);
|
|
|
|
} catch (error) {
|
|
console.error('Daily error:', error);
|
|
await interaction.editReply({ content: 'Failed to claim daily reward.' });
|
|
}
|
|
},
|
|
};
|
|
|
|
function getPrestigeColor(level) {
|
|
const colors = [0x6b7280, 0xcd7f32, 0xc0c0c0, 0xffd700, 0xe5e4e2, 0xb9f2ff, 0xff4500, 0x9400d3, 0xffd700, 0xff69b4, 0x7c3aed];
|
|
return colors[Math.min(level, 10)] || 0x00ff00;
|
|
}
|