feat(economy): enhance minigame commands to track stats, quest progress, and achievements

This commit is contained in:
2025-10-05 05:35:32 -05:00
parent b10cc89583
commit 0990533f6e
15 changed files with 954 additions and 30 deletions

View File

@@ -0,0 +1,109 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { prisma } from '../../../core/database/prisma';
import { EmbedBuilder } from 'discord.js';
export const command: CommandMessage = {
name: 'cooldowns',
type: 'message',
aliases: ['cds', 'tiempos', 'cd'],
cooldown: 3,
description: 'Ver todos tus cooldowns activos',
usage: 'cooldowns',
run: async (message, args, client: Amayo) => {
try {
const userId = message.author.id;
const guildId = message.guild!.id;
// Obtener todos los cooldowns activos
const cooldowns = await prisma.actionCooldown.findMany({
where: {
userId,
guildId,
until: { gt: new Date() }
},
orderBy: { until: 'asc' }
});
if (cooldowns.length === 0) {
await message.reply('✅ No tienes cooldowns activos. ¡Puedes realizar cualquier acción!');
return;
}
const embed = new EmbedBuilder()
.setColor(0xFF6B6B)
.setTitle('⏰ Cooldowns Activos')
.setDescription(`${message.author.username}, estos son tus cooldowns:`)
.setThumbnail(message.author.displayAvatarURL({ size: 128 }));
// Emojis por tipo de acción
const actionEmojis: Record<string, string> = {
'mine': '⛏️',
'fish': '🎣',
'fight': '⚔️',
'farm': '🌾',
'craft': '🛠️',
'smelt': '🔥',
'shop': '🛒',
'daily': '🎁',
'consume': '🍖'
};
// Traducción de acciones
const actionNames: Record<string, string> = {
'mine': 'Minar',
'fish': 'Pescar',
'fight': 'Pelear',
'farm': 'Granja',
'craft': 'Craftear',
'smelt': 'Fundir',
'shop': 'Tienda',
'daily': 'Diario',
'consume': 'Consumir'
};
let cooldownText = '';
const now = Date.now();
for (const cd of cooldowns) {
const remainingMs = cd.until.getTime() - now;
const remainingSec = Math.ceil(remainingMs / 1000);
// Formatear tiempo
let timeStr = '';
if (remainingSec >= 3600) {
const hours = Math.floor(remainingSec / 3600);
const mins = Math.floor((remainingSec % 3600) / 60);
timeStr = `${hours}h ${mins}m`;
} else if (remainingSec >= 60) {
const mins = Math.floor(remainingSec / 60);
const secs = remainingSec % 60;
timeStr = `${mins}m ${secs}s`;
} else {
timeStr = `${remainingSec}s`;
}
// Buscar emoji y nombre
const action = cd.key.split(':')[0];
const emoji = actionEmojis[action] || '⏱️';
const actionName = actionNames[action] || cd.key;
cooldownText += `${emoji} **${actionName}**: ${timeStr}\n`;
}
embed.addFields({
name: `📋 Cooldowns (${cooldowns.length})`,
value: cooldownText,
inline: false
});
embed.setFooter({ text: 'Los cooldowns se actualizan en tiempo real' });
embed.setTimestamp();
await message.reply({ embeds: [embed] });
} catch (error) {
console.error('Error en comando cooldowns:', error);
await message.reply('❌ Error al obtener los cooldowns.');
}
}
};

View File

@@ -0,0 +1,117 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { getPlayerAchievements, getAchievementStats, createProgressBar } from '../../../game/achievements/service';
import { EmbedBuilder } from 'discord.js';
export const command: CommandMessage = {
name: 'logros',
type: 'message',
aliases: ['achievements', 'logro', 'achievement'],
cooldown: 5,
description: 'Ver tus logros desbloqueados y progreso',
usage: 'logros [@usuario]',
run: async (message, args, client: Amayo) => {
try {
const guildId = message.guild!.id;
const targetUser = message.mentions.users.first() || message.author;
const userId = targetUser.id;
// Obtener logros del jugador
const { unlocked, inProgress } = await getPlayerAchievements(userId, guildId);
const achievementStats = await getAchievementStats(userId, guildId);
const embed = new EmbedBuilder()
.setColor(0xFFD700)
.setTitle(`🏆 Logros de ${targetUser.username}`)
.setThumbnail(targetUser.displayAvatarURL({ size: 128 }))
.setDescription(
`**${achievementStats.unlocked}/${achievementStats.total}** logros desbloqueados ` +
`(${achievementStats.percentage}%)\n` +
`⭐ **${achievementStats.points}** puntos totales`
);
// Logros desbloqueados recientes (últimos 5)
if (unlocked.length > 0) {
const recentUnlocked = unlocked.slice(0, 5);
let unlockedText = '';
for (const pa of recentUnlocked) {
const icon = pa.achievement.icon || '🏆';
const points = pa.achievement.points || 10;
unlockedText += `${icon} **${pa.achievement.name}** (+${points} pts)\n`;
unlockedText += `${pa.achievement.description}\n`;
}
embed.addFields({
name: `✅ Desbloqueados Recientes (${unlocked.length})`,
value: unlockedText || 'Ninguno aún',
inline: false
});
}
// Logros en progreso (top 5)
if (inProgress.length > 0) {
const topInProgress = inProgress.slice(0, 5);
let progressText = '';
for (const pa of topInProgress) {
const icon = pa.achievement.icon || '🔒';
const req = pa.achievement.requirements as any;
const progress = pa.progress;
const required = req.value;
const bar = createProgressBar(progress, required, 8);
progressText += `${icon} **${pa.achievement.name}**\n`;
progressText += `${bar} (${progress}/${required})\n`;
}
embed.addFields({
name: `📈 En Progreso (${inProgress.length})`,
value: progressText,
inline: false
});
}
// Categorías
const categories = ['mining', 'fishing', 'combat', 'economy', 'exploration'];
const categoryEmojis: Record<string, string> = {
mining: '⛏️',
fishing: '🎣',
combat: '⚔️',
economy: '💰',
exploration: '🗺️'
};
let categoryText = '';
for (const cat of categories) {
const count = unlocked.filter(pa => pa.achievement.category === cat).length;
if (count > 0) {
categoryText += `${categoryEmojis[cat]} ${count} `;
}
}
if (categoryText) {
embed.addFields({
name: '📊 Por Categoría',
value: categoryText,
inline: false
});
}
if (unlocked.length === 0 && inProgress.length === 0) {
embed.setDescription(
'Aún no has desbloqueado ningún logro.\n' +
'¡Empieza a jugar para obtener logros y puntos!'
);
}
embed.setFooter({ text: 'Los logros se desbloquean automáticamente al cumplir requisitos' });
embed.setTimestamp();
await message.reply({ embeds: [embed] });
} catch (error) {
console.error('Error en comando logros:', error);
await message.reply('❌ Error al obtener los logros.');
}
}
};

View File

@@ -2,6 +2,9 @@ import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { runMinigame } from '../../../game/minigames/service';
import { resolveArea, getDefaultLevel, findBestToolKey } from './_helpers';
import { updateStats } from '../../../game/stats/service';
import { updateQuestProgress } from '../../../game/quests/service';
import { checkAchievements } from '../../../game/achievements/service';
export const command: CommandMessage = {
name: 'mina',
@@ -26,13 +29,34 @@ export const command: CommandMessage = {
try {
const result = await runMinigame(userId, guildId, areaKey, level, { toolKey: toolKey ?? undefined });
// Actualizar stats
await updateStats(userId, guildId, { minesCompleted: 1 });
// Actualizar progreso de misiones
await updateQuestProgress(userId, guildId, 'mine_count', 1);
// Verificar logros
const newAchievements = await checkAchievements(userId, guildId, 'mine_count');
const rewards = result.rewards.map(r => r.type === 'coins' ? `🪙 +${r.amount}` : `📦 ${r.itemKey} x${r.qty}`).join(' · ') || '—';
const mobs = result.mobs.length ? result.mobs.join(', ') : '—';
const toolInfo = result.tool?.key ? `🔧 ${result.tool.key}${result.tool.broken ? ' (rota)' : ` (-${result.tool.durabilityDelta} dur.)`}` : '—';
await message.reply(`⛏️ Mina (nivel ${level})
let response = `⛏️ Mina (nivel ${level})
Recompensas: ${rewards}
Mobs: ${mobs}
Herramienta: ${toolInfo}`);
Herramienta: ${toolInfo}`;
// Notificar logros desbloqueados
if (newAchievements.length > 0) {
response += `\n\n🏆 ¡Logro desbloqueado!`;
for (const ach of newAchievements) {
response += `\n✨ **${ach.name}** - ${ach.description}`;
}
}
await message.reply(response);
} catch (e: any) {
await message.reply(`❌ No se pudo minar: ${e?.message ?? e}`);
}

View File

@@ -0,0 +1,73 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { claimQuestReward, getPlayerQuests } from '../../../game/quests/service';
import { EmbedBuilder } from 'discord.js';
export const command: CommandMessage = {
name: 'mision-reclamar',
type: 'message',
aliases: ['claim-quest', 'reclamar-mision'],
cooldown: 3,
description: 'Reclamar recompensa de misión completada',
usage: 'mision-reclamar <numero>',
run: async (message, args, client: Amayo) => {
try {
const userId = message.author.id;
const guildId = message.guild!.id;
if (!args[0]) {
await message.reply(`❌ Uso: \`!mision-reclamar <numero>\`\nEjemplo: \`!mision-reclamar 1\``);
return;
}
// Obtener misiones completadas
const quests = await getPlayerQuests(userId, guildId);
const allQuests = [...quests.daily, ...quests.weekly, ...quests.permanent, ...quests.event];
const claimable = allQuests.filter(q => q.canClaim);
if (claimable.length === 0) {
await message.reply('❌ No tienes misiones listas para reclamar. Completa misiones primero usando los comandos del bot.');
return;
}
const index = parseInt(args[0]) - 1;
if (isNaN(index) || index < 0 || index >= claimable.length) {
await message.reply(`❌ Número de misión inválido. Elige un número entre 1 y ${claimable.length}.`);
return;
}
const selected = claimable[index];
// Reclamar recompensa
const { quest, rewards } = await claimQuestReward(userId, guildId, selected.quest.id);
const embed = new EmbedBuilder()
.setColor(0x00FF00)
.setTitle('🎉 ¡Misión Completada!')
.setDescription(`Has reclamado las recompensas de **${quest.name}**`)
.setThumbnail(message.author.displayAvatarURL({ size: 128 }));
// Mostrar recompensas
if (rewards.length > 0) {
embed.addFields({
name: '🎁 Recompensas Recibidas',
value: rewards.join('\n'),
inline: false
});
}
// Info de la misión
embed.addFields(
{ name: '📜 Misión', value: quest.description, inline: false }
);
embed.setFooter({ text: `Usa !misiones para ver más misiones` });
embed.setTimestamp();
await message.reply({ embeds: [embed] });
} catch (error: any) {
console.error('Error en comando mision-reclamar:', error);
await message.reply(`${error.message || 'Error al reclamar la misión.'}`);
}
}
};

View File

@@ -0,0 +1,157 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { getPlayerQuests, claimQuestReward } from '../../../game/quests/service';
import { createProgressBar } from '../../../game/achievements/service';
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
export const command: CommandMessage = {
name: 'misiones',
type: 'message',
aliases: ['quests', 'mision', 'quest'],
cooldown: 5,
description: 'Ver misiones disponibles y tu progreso',
usage: 'misiones [categoria]',
run: async (message, args, client: Amayo) => {
try {
const userId = message.author.id;
const guildId = message.guild!.id;
// Obtener misiones con progreso
const quests = await getPlayerQuests(userId, guildId);
const embed = new EmbedBuilder()
.setColor(0x5865F2)
.setTitle('📜 Misiones Disponibles')
.setDescription(`${message.author.username}, aquí están tus misiones:`)
.setThumbnail(message.author.displayAvatarURL({ size: 128 }));
// Emojis por categoría
const categoryEmojis: Record<string, string> = {
mining: '⛏️',
fishing: '🎣',
combat: '⚔️',
economy: '💰',
exploration: '🗺️',
crafting: '🛠️'
};
// Función para formatear una lista de misiones
const formatQuests = (questList: any[], type: string) => {
if (questList.length === 0) return null;
let text = '';
for (const { quest, progress, canClaim, percentage } of questList) {
const icon = categoryEmojis[quest.category] || '📋';
const req = quest.requirements as any;
const currentProgress = progress?.progress || 0;
const required = req.count;
// Estado
let status = '';
if (canClaim) {
status = '✅ ¡Listo para reclamar!';
} else if (progress?.completed) {
status = '🎁 Completada';
} else {
const bar = createProgressBar(currentProgress, required, 8);
status = `${bar}`;
}
text += `${icon} **${quest.name}**\n`;
text += `${quest.description}\n`;
text += `${status}\n`;
// Recompensas
const rewards = quest.rewards as any;
let rewardStr = '';
if (rewards.coins) rewardStr += `💰 ${rewards.coins} `;
if (rewards.items && rewards.items.length > 0) {
rewardStr += `📦 ${rewards.items.length} items `;
}
if (rewardStr) {
text += `└ Recompensa: ${rewardStr}\n`;
}
text += '\n';
}
return text;
};
// Misiones diarias
if (quests.daily.length > 0) {
const dailyText = formatQuests(quests.daily, 'daily');
if (dailyText) {
embed.addFields({
name: '📅 Misiones Diarias',
value: dailyText,
inline: false
});
}
}
// Misiones semanales
if (quests.weekly.length > 0) {
const weeklyText = formatQuests(quests.weekly, 'weekly');
if (weeklyText) {
embed.addFields({
name: '📆 Misiones Semanales',
value: weeklyText,
inline: false
});
}
}
// Misiones permanentes
if (quests.permanent.length > 0) {
const permanentText = formatQuests(quests.permanent.slice(0, 3), 'permanent');
if (permanentText) {
embed.addFields({
name: '♾️ Misiones Permanentes',
value: permanentText,
inline: false
});
}
}
// Misiones de evento
if (quests.event.length > 0) {
const eventText = formatQuests(quests.event, 'event');
if (eventText) {
embed.addFields({
name: '🎉 Misiones de Evento',
value: eventText,
inline: false
});
}
}
// Verificar si hay misiones para reclamar
const canClaim = [...quests.daily, ...quests.weekly, ...quests.permanent, ...quests.event]
.filter(q => q.canClaim);
if (canClaim.length > 0) {
embed.addFields({
name: '🎁 ¡Misiones Listas!',
value: `Tienes **${canClaim.length}** misiones listas para reclamar.\nUsa \`!mision-reclamar <id>\` para reclamar recompensas.`,
inline: false
});
}
if (quests.daily.length === 0 && quests.weekly.length === 0 && quests.permanent.length === 0) {
embed.setDescription(
'No hay misiones disponibles en este momento.\n' +
'Las misiones diarias se generan automáticamente cada día.'
);
}
embed.setFooter({ text: 'Completa misiones para ganar recompensas' });
embed.setTimestamp();
await message.reply({ embeds: [embed] });
} catch (error) {
console.error('Error en comando misiones:', error);
await message.reply('❌ Error al obtener las misiones.');
}
}
};

View File

@@ -2,6 +2,9 @@ import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { runMinigame } from '../../../game/minigames/service';
import { resolveArea, getDefaultLevel, findBestToolKey } from './_helpers';
import { updateStats } from '../../../game/stats/service';
import { updateQuestProgress } from '../../../game/quests/service';
import { checkAchievements } from '../../../game/achievements/service';
export const command: CommandMessage = {
name: 'pelear',
@@ -26,13 +29,37 @@ export const command: CommandMessage = {
try {
const result = await runMinigame(userId, guildId, areaKey, level, { toolKey: toolKey ?? undefined });
// Actualizar stats y misiones
await updateStats(userId, guildId, { fightsCompleted: 1 });
await updateQuestProgress(userId, guildId, 'fight_count', 1);
// Contar mobs derrotados
const mobsCount = result.mobs.length;
if (mobsCount > 0) {
await updateStats(userId, guildId, { mobsDefeated: mobsCount });
await updateQuestProgress(userId, guildId, 'mob_defeat_count', mobsCount);
}
const newAchievements = await checkAchievements(userId, guildId, 'fight_count');
const rewards = result.rewards.map(r => r.type === 'coins' ? `🪙 +${r.amount}` : `🎁 ${r.itemKey} x${r.qty}`).join(' · ') || '—';
const mobs = result.mobs.length ? result.mobs.join(', ') : '—';
const toolInfo = result.tool?.key ? `🗡️ ${result.tool.key}${result.tool.broken ? ' (rota)' : ` (-${result.tool.durabilityDelta} dur.)`}` : '—';
await message.reply(`⚔️ Arena (nivel ${level})
let response = `⚔️ Arena (nivel ${level})
Recompensas: ${rewards}
Enemigos: ${mobs}
Arma: ${toolInfo}`);
Arma: ${toolInfo}`;
if (newAchievements.length > 0) {
response += `\n\n🏆 ¡Logro desbloqueado!`;
for (const ach of newAchievements) {
response += `\n✨ **${ach.name}** - ${ach.description}`;
}
}
await message.reply(response);
} catch (e: any) {
await message.reply(`❌ No se pudo pelear: ${e?.message ?? e}`);
}

View File

@@ -2,6 +2,9 @@ import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { runMinigame } from '../../../game/minigames/service';
import { resolveArea, getDefaultLevel, findBestToolKey } from './_helpers';
import { updateStats } from '../../../game/stats/service';
import { updateQuestProgress } from '../../../game/quests/service';
import { checkAchievements } from '../../../game/achievements/service';
export const command: CommandMessage = {
name: 'pescar',
@@ -26,13 +29,29 @@ export const command: CommandMessage = {
try {
const result = await runMinigame(userId, guildId, areaKey, level, { toolKey: toolKey ?? undefined });
// Actualizar stats y misiones
await updateStats(userId, guildId, { fishingCompleted: 1 });
await updateQuestProgress(userId, guildId, 'fish_count', 1);
const newAchievements = await checkAchievements(userId, guildId, 'fish_count');
const rewards = result.rewards.map(r => r.type === 'coins' ? `🪙 +${r.amount}` : `🐟 ${r.itemKey} x${r.qty}`).join(' · ') || '—';
const mobs = result.mobs.length ? result.mobs.join(', ') : '—';
const toolInfo = result.tool?.key ? `🎣 ${result.tool.key}${result.tool.broken ? ' (rota)' : ` (-${result.tool.durabilityDelta} dur.)`}` : '—';
await message.reply(`🎣 Pesca (nivel ${level})
let response = `🎣 Pesca (nivel ${level})
Recompensas: ${rewards}
Mobs: ${mobs}
Herramienta: ${toolInfo}`);
Herramienta: ${toolInfo}`;
if (newAchievements.length > 0) {
response += `\n\n🏆 ¡Logro desbloqueado!`;
for (const ach of newAchievements) {
response += `\n✨ **${ach.name}** - ${ach.description}`;
}
}
await message.reply(response);
} catch (e: any) {
await message.reply(`❌ No se pudo pescar: ${e?.message ?? e}`);
}

View File

@@ -66,7 +66,7 @@ export const command: CommandMessage = {
embed.addFields({ name: '🏆 Récords', value: recordsText || 'Sin datos', inline: true });
}
embed.setFooter({ text: `Usa ${client.prefix}ranking-stats para ver el ranking global` });
embed.setFooter({ text: 'Usa !ranking-stats para ver el ranking global' });
await message.reply({ embeds: [embed] });
} catch (error) {

View File

@@ -0,0 +1,221 @@
import { prisma } from '../../core/database/prisma';
import logger from '../../core/lib/logger';
/**
* Seed de logros base
*/
export async function seedAchievements(guildId: string | null = null) {
const achievements = [
// Minería
{
key: 'first_mine',
name: '⛏️ Primera Mina',
description: 'Mina por primera vez',
category: 'mining',
requirements: { type: 'mine_count', value: 1 },
rewards: { coins: 100 },
hidden: false,
points: 10
},
{
key: 'miner_novice',
name: '⛏️ Minero Novato',
description: 'Mina 10 veces',
category: 'mining',
requirements: { type: 'mine_count', value: 10 },
rewards: { coins: 500 },
hidden: false,
points: 20
},
{
key: 'miner_expert',
name: '⛏️ Minero Experto',
description: 'Mina 50 veces',
category: 'mining',
requirements: { type: 'mine_count', value: 50 },
rewards: { coins: 2500 },
hidden: false,
points: 50
},
{
key: 'miner_master',
name: '⛏️ Maestro Minero',
description: 'Mina 100 veces',
category: 'mining',
requirements: { type: 'mine_count', value: 100 },
rewards: { coins: 10000 },
hidden: false,
points: 100
},
// Pesca
{
key: 'first_fish',
name: '🎣 Primera Pesca',
description: 'Pesca por primera vez',
category: 'fishing',
requirements: { type: 'fish_count', value: 1 },
rewards: { coins: 100 },
hidden: false,
points: 10
},
{
key: 'fisher_novice',
name: '🎣 Pescador Novato',
description: 'Pesca 10 veces',
category: 'fishing',
requirements: { type: 'fish_count', value: 10 },
rewards: { coins: 500 },
hidden: false,
points: 20
},
{
key: 'fisher_expert',
name: '🎣 Pescador Experto',
description: 'Pesca 50 veces',
category: 'fishing',
requirements: { type: 'fish_count', value: 50 },
rewards: { coins: 2500 },
hidden: false,
points: 50
},
// Combate
{
key: 'first_fight',
name: '⚔️ Primera Pelea',
description: 'Pelea por primera vez',
category: 'combat',
requirements: { type: 'fight_count', value: 1 },
rewards: { coins: 150 },
hidden: false,
points: 10
},
{
key: 'warrior_novice',
name: '⚔️ Guerrero Novato',
description: 'Pelea 10 veces',
category: 'combat',
requirements: { type: 'fight_count', value: 10 },
rewards: { coins: 750 },
hidden: false,
points: 20
},
{
key: 'mob_hunter',
name: '👾 Cazador de Monstruos',
description: 'Derrota 50 mobs',
category: 'combat',
requirements: { type: 'mob_defeat_count', value: 50 },
rewards: { coins: 3000 },
hidden: false,
points: 50
},
{
key: 'mob_slayer',
name: '👾 Asesino de Monstruos',
description: 'Derrota 200 mobs',
category: 'combat',
requirements: { type: 'mob_defeat_count', value: 200 },
rewards: { coins: 15000 },
hidden: false,
points: 100
},
// Economía
{
key: 'first_coins',
name: '💰 Primeras Monedas',
description: 'Gana 1,000 monedas en total',
category: 'economy',
requirements: { type: 'coins_earned', value: 1000 },
rewards: { coins: 200 },
hidden: false,
points: 10
},
{
key: 'wealthy',
name: '💰 Acaudalado',
description: 'Gana 10,000 monedas en total',
category: 'economy',
requirements: { type: 'coins_earned', value: 10000 },
rewards: { coins: 2000 },
hidden: false,
points: 30
},
{
key: 'millionaire',
name: '💰 Millonario',
description: 'Gana 100,000 monedas en total',
category: 'economy',
requirements: { type: 'coins_earned', value: 100000 },
rewards: { coins: 25000 },
hidden: false,
points: 100
},
// Crafteo
{
key: 'first_craft',
name: '🛠️ Primer Crafteo',
description: 'Craftea tu primer item',
category: 'crafting',
requirements: { type: 'craft_count', value: 1 },
rewards: { coins: 100 },
hidden: false,
points: 10
},
{
key: 'crafter_expert',
name: '🛠️ Artesano Experto',
description: 'Craftea 50 items',
category: 'crafting',
requirements: { type: 'craft_count', value: 50 },
rewards: { coins: 5000 },
hidden: false,
points: 50
},
{
key: 'master_crafter',
name: '🛠️ Maestro Artesano',
description: 'Craftea 200 items',
category: 'crafting',
requirements: { type: 'craft_count', value: 200 },
rewards: { coins: 20000 },
hidden: false,
points: 100
}
];
let created = 0;
for (const ach of achievements) {
const existing = await prisma.achievement.findUnique({
where: { guildId_key: { guildId: guildId || '', key: ach.key } }
});
if (!existing) {
await prisma.achievement.create({
data: { ...ach, guildId: guildId || undefined }
});
created++;
}
}
console.log(`Seeded ${created} achievements for guild ${guildId || 'global'}`);
return created;
}
/**
* Ejecutar si es llamado directamente
*/
if (require.main === module) {
seedAchievements(null)
.then(count => {
console.log(`${count} achievements seeded`);
process.exit(0);
})
.catch(error => {
console.error('❌ Error seeding achievements:', error);
process.exit(1);
});
}

View File

@@ -26,7 +26,7 @@ export async function checkAchievements(
}
});
const newUnlocks = [];
const newUnlocks: any[] = [];
const stats = await getOrCreatePlayerStats(userId, guildId);
for (const achievement of achievements) {
@@ -118,7 +118,7 @@ export async function checkAchievements(
return newUnlocks;
} catch (error) {
logger.error(`Error checking achievements for ${userId}:`, error);
console.error(`Error checking achievements for ${userId}:`, error);
return [];
}
}

View File

@@ -16,15 +16,11 @@ export async function updateQuestProgress(
const quests = await prisma.quest.findMany({
where: {
OR: [{ guildId }, { guildId: null }],
active: true,
OR: [
{ endAt: null },
{ endAt: { gte: new Date() } }
]
active: true
}
});
const updates = [];
const updates: any[] = [];
for (const quest of quests) {
const req = quest.requirements as any;
@@ -94,7 +90,7 @@ export async function updateQuestProgress(
return updates;
} catch (error) {
logger.error(`Error updating quest progress for ${userId}:`, error);
console.error(`Error updating quest progress for ${userId}:`, error);
return [];
}
}
@@ -144,7 +140,7 @@ export async function claimQuestReward(
return { quest: progress.quest, rewards };
} catch (error) {
logger.error(`Error claiming quest reward for ${userId}:`, error);
console.error(`Error claiming quest reward for ${userId}:`, error);
throw error;
}
}
@@ -156,11 +152,7 @@ export async function getPlayerQuests(userId: string, guildId: string) {
const quests = await prisma.quest.findMany({
where: {
OR: [{ guildId }, { guildId: null }],
active: true,
OR: [
{ endAt: null },
{ endAt: { gte: new Date() } }
]
active: true
},
orderBy: [
{ type: 'asc' },
@@ -272,10 +264,10 @@ export async function generateDailyQuests(guildId: string) {
});
}
logger.info(`Generated ${selectedTemplates.length} daily quests for guild ${guildId}`);
console.log(`Generated ${selectedTemplates.length} daily quests for guild ${guildId}`);
return selectedTemplates.length;
} catch (error) {
logger.error(`Error generating daily quests for ${guildId}:`, error);
console.error(`Error generating daily quests for ${guildId}:`, error);
return 0;
}
}
@@ -295,6 +287,6 @@ export async function cleanExpiredQuests(guildId: string) {
}
});
logger.info(`Deactivated ${result.count} expired quests for guild ${guildId}`);
console.log(`Deactivated ${result.count} expired quests for guild ${guildId}`);
return result.count;
}

View File

@@ -52,15 +52,15 @@ export async function giveRewards(
guildId,
action: 'reward_given',
target: source,
details: rewards
details: rewards as any
}
}).catch(() => {}); // Silencioso si falla el log
logger.info(`Rewards given to ${userId} in ${guildId} from ${source}:`, rewards);
console.log(`Rewards given to ${userId} in ${guildId} from ${source}:`, rewards);
return results;
} catch (error) {
logger.error(`Error giving rewards to ${userId} in ${guildId}:`, error);
console.error(`Error giving rewards to ${userId} in ${guildId}:`, error);
throw error;
}
}

View File

@@ -69,7 +69,7 @@ export async function updateStats(
return stats;
} catch (error) {
logger.error(`Error updating stats for ${userId} in ${guildId}:`, error);
console.error(`Error updating stats for ${userId} in ${guildId}:`, error);
throw error;
}
}

View File

@@ -85,7 +85,7 @@ export async function updateStreak(userId: string, guildId: string) {
daysIncreased: daysDiff === 1
};
} catch (error) {
logger.error(`Error updating streak for ${userId}:`, error);
console.error(`Error updating streak for ${userId}:`, error);
throw error;
}
}