Introduces a new server mode configuration system (Federation/Standalone) with associated command changes, dynamic status rotation for the bot, and adds new commands and features. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: b08e6ba5-7498-4b9f-b1c9-7dc11b362ddd Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/R9PkDi8 Replit-Helium-Checkpoint-Created: true
230 lines
5.7 KiB
JavaScript
230 lines
5.7 KiB
JavaScript
const { EmbedBuilder } = require('discord.js');
|
|
const { getEmbedColor } = require('./modeHelper');
|
|
|
|
async function getStandaloneXp(supabase, discordId, guildId) {
|
|
if (!supabase) return null;
|
|
|
|
try {
|
|
const { data } = await supabase
|
|
.from('guild_user_xp')
|
|
.select('*')
|
|
.eq('discord_id', discordId)
|
|
.eq('guild_id', guildId)
|
|
.maybeSingle();
|
|
|
|
return data || null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function updateStandaloneXp(supabase, discordId, guildId, xpGain, username) {
|
|
if (!supabase) return null;
|
|
|
|
try {
|
|
const { data: existing } = await supabase
|
|
.from('guild_user_xp')
|
|
.select('*')
|
|
.eq('discord_id', discordId)
|
|
.eq('guild_id', guildId)
|
|
.maybeSingle();
|
|
|
|
if (existing) {
|
|
const newXp = (existing.xp || 0) + xpGain;
|
|
const totalEarned = (existing.total_xp_earned || 0) + xpGain;
|
|
|
|
const { data } = await supabase
|
|
.from('guild_user_xp')
|
|
.update({
|
|
xp: newXp,
|
|
total_xp_earned: totalEarned,
|
|
updated_at: new Date().toISOString(),
|
|
})
|
|
.eq('id', existing.id)
|
|
.select()
|
|
.single();
|
|
|
|
return data;
|
|
} else {
|
|
const { data } = await supabase
|
|
.from('guild_user_xp')
|
|
.insert({
|
|
discord_id: discordId,
|
|
guild_id: guildId,
|
|
username: username,
|
|
xp: xpGain,
|
|
total_xp_earned: xpGain,
|
|
prestige_level: 0,
|
|
daily_streak: 0,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
return data;
|
|
}
|
|
} catch (e) {
|
|
console.error('Standalone XP update error:', e.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function getStandaloneLeaderboard(supabase, guildId, limit = 10) {
|
|
if (!supabase) return [];
|
|
|
|
try {
|
|
const { data } = await supabase
|
|
.from('guild_user_xp')
|
|
.select('discord_id, username, xp, prestige_level')
|
|
.eq('guild_id', guildId)
|
|
.order('xp', { ascending: false })
|
|
.limit(limit);
|
|
|
|
return data || [];
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function claimStandaloneDaily(supabase, discordId, guildId, username) {
|
|
if (!supabase) return { success: false, message: 'Database not available' };
|
|
|
|
try {
|
|
const { data: existing } = await supabase
|
|
.from('guild_user_xp')
|
|
.select('*')
|
|
.eq('discord_id', discordId)
|
|
.eq('guild_id', guildId)
|
|
.maybeSingle();
|
|
|
|
const now = new Date();
|
|
const today = now.toISOString().split('T')[0];
|
|
|
|
if (existing) {
|
|
const lastClaim = existing.last_daily_claim ? existing.last_daily_claim.split('T')[0] : null;
|
|
|
|
if (lastClaim === today) {
|
|
return { success: false, message: 'You already claimed your daily reward today!' };
|
|
}
|
|
|
|
const yesterday = new Date(now);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
const yesterdayStr = yesterday.toISOString().split('T')[0];
|
|
|
|
let newStreak = 1;
|
|
if (lastClaim === yesterdayStr) {
|
|
newStreak = (existing.daily_streak || 0) + 1;
|
|
}
|
|
|
|
const baseXp = 50;
|
|
const streakBonus = Math.min(newStreak * 5, 100);
|
|
const totalXp = baseXp + streakBonus;
|
|
|
|
const newXp = (existing.xp || 0) + totalXp;
|
|
const totalEarned = (existing.total_xp_earned || 0) + totalXp;
|
|
|
|
await supabase
|
|
.from('guild_user_xp')
|
|
.update({
|
|
xp: newXp,
|
|
total_xp_earned: totalEarned,
|
|
daily_streak: newStreak,
|
|
last_daily_claim: now.toISOString(),
|
|
updated_at: now.toISOString(),
|
|
})
|
|
.eq('id', existing.id);
|
|
|
|
return {
|
|
success: true,
|
|
xpGained: totalXp,
|
|
streak: newStreak,
|
|
totalXp: newXp,
|
|
};
|
|
} else {
|
|
const baseXp = 50;
|
|
|
|
await supabase
|
|
.from('guild_user_xp')
|
|
.insert({
|
|
discord_id: discordId,
|
|
guild_id: guildId,
|
|
username: username,
|
|
xp: baseXp,
|
|
total_xp_earned: baseXp,
|
|
prestige_level: 0,
|
|
daily_streak: 1,
|
|
last_daily_claim: now.toISOString(),
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
xpGained: baseXp,
|
|
streak: 1,
|
|
totalXp: baseXp,
|
|
};
|
|
}
|
|
} catch (e) {
|
|
console.error('Standalone daily claim error:', e.message);
|
|
return { success: false, message: 'An error occurred' };
|
|
}
|
|
}
|
|
|
|
async function prestigeStandalone(supabase, discordId, guildId) {
|
|
if (!supabase) return { success: false, message: 'Database not available' };
|
|
|
|
try {
|
|
const { data: existing } = await supabase
|
|
.from('guild_user_xp')
|
|
.select('*')
|
|
.eq('discord_id', discordId)
|
|
.eq('guild_id', guildId)
|
|
.maybeSingle();
|
|
|
|
if (!existing) {
|
|
return { success: false, message: 'No XP data found' };
|
|
}
|
|
|
|
const level = calculateLevel(existing.xp || 0, 'normal');
|
|
if (level < 50) {
|
|
return { success: false, message: `You need Level 50 to prestige! (Current: Level ${level})` };
|
|
}
|
|
|
|
const newPrestige = (existing.prestige_level || 0) + 1;
|
|
|
|
await supabase
|
|
.from('guild_user_xp')
|
|
.update({
|
|
xp: 0,
|
|
prestige_level: newPrestige,
|
|
updated_at: new Date().toISOString(),
|
|
})
|
|
.eq('id', existing.id);
|
|
|
|
return {
|
|
success: true,
|
|
newPrestige: newPrestige,
|
|
bonus: newPrestige * 5,
|
|
};
|
|
} catch (e) {
|
|
console.error('Standalone prestige error:', e.message);
|
|
return { success: false, message: 'An error occurred' };
|
|
}
|
|
}
|
|
|
|
function calculateLevel(xp, curve = 'normal') {
|
|
const bases = {
|
|
easy: 50,
|
|
normal: 100,
|
|
hard: 200,
|
|
};
|
|
const base = bases[curve] || 100;
|
|
return Math.floor(Math.sqrt(xp / base));
|
|
}
|
|
|
|
module.exports = {
|
|
getStandaloneXp,
|
|
updateStandaloneXp,
|
|
getStandaloneLeaderboard,
|
|
claimStandaloneDaily,
|
|
prestigeStandalone,
|
|
calculateLevel,
|
|
};
|