feat(economy): migrate commands to DisplayComponents V2 and enhance user interaction

This commit is contained in:
2025-10-05 06:52:49 -05:00
parent 02b4eae7be
commit a8c38d3b23
15 changed files with 1179 additions and 1871 deletions

View File

@@ -59,7 +59,7 @@ export const command: CommandMessage = {
const channel = message.channel as TextBasedChannel & { send: Function };
const editorMsg = await channel.send({
...displayMessage,
flags: MessageFlags.IsComponentsV2,
flags: 32768, // MessageFlags.IsComponentsV2
components: [
{
type: ComponentType.ActionRow,
@@ -87,8 +87,7 @@ export const command: CommandMessage = {
case 'ach_cancel':
await i.deferUpdate();
await editorMsg.edit({
flags: 32768,
components: [{
display: {
type: 17,
accent_color: 0xFF0000,
components: [{
@@ -98,7 +97,9 @@ export const command: CommandMessage = {
content: '**❌ Creación de logro cancelada.**'
}]
}]
}]
},
flags: 32768,
components: []
});
collector.stop('cancel');
return;
@@ -138,8 +139,7 @@ export const command: CommandMessage = {
await i.reply({ content: '✅ Logro creado exitosamente!', flags: 64 });
await editorMsg.edit({
flags: 32768,
components: [{
display: {
type: 17,
accent_color: 0x00FF00,
components: [{
@@ -149,7 +149,9 @@ export const command: CommandMessage = {
content: `**✅ Logro \`${state.key}\` creado exitosamente.**`
}]
}]
}]
},
flags: 32768,
components: []
});
collector.stop('saved');
return;
@@ -166,8 +168,7 @@ export const command: CommandMessage = {
if (r === 'time') {
try {
await editorMsg.edit({
flags: 32768,
components: [{
display: {
type: 17,
accent_color: 0xFFA500,
components: [{
@@ -177,7 +178,9 @@ export const command: CommandMessage = {
content: '**⏰ Editor expirado.**'
}]
}]
}]
},
flags: 32768,
components: []
});
} catch {}
}

View File

@@ -58,6 +58,7 @@ export const command: CommandMessage = {
const channel = message.channel as TextBasedChannel & { send: Function };
const editorMsg = await channel.send({
...displayMessage,
flags: 32768,
components: [
{
type: ComponentType.ActionRow,
@@ -85,8 +86,7 @@ export const command: CommandMessage = {
case 'quest_cancel':
await i.deferUpdate();
await editorMsg.edit({
flags: 32768,
components: [{
display: {
type: 17,
accent_color: 0xFF0000,
components: [{
@@ -96,7 +96,9 @@ export const command: CommandMessage = {
content: '**❌ Creación de misión cancelada.**'
}]
}]
}]
},
flags: 32768,
components: []
});
collector.stop('cancel');
return;
@@ -137,9 +139,19 @@ export const command: CommandMessage = {
await i.reply({ content: '✅ Misión creada exitosamente.', flags: 64 });
await editorMsg.edit({
content: `✅ Misión \`${state.key}\` creada.`,
components: [],
display: undefined
display: {
type: 17,
accent_color: 0x00FF00,
components: [{
type: 9,
components: [{
type: 10,
content: `**✅ Misión \`${state.key}\` creada exitosamente.**`
}]
}]
},
flags: 32768,
components: []
});
collector.stop('saved');
return;
@@ -156,8 +168,7 @@ export const command: CommandMessage = {
if (r === 'time') {
try {
await editorMsg.edit({
flags: 32768,
components: [{
display: {
type: 17,
accent_color: 0xFFA500,
components: [{
@@ -167,7 +178,9 @@ export const command: CommandMessage = {
content: '**⏰ Editor expirado.**'
}]
}]
}]
},
flags: 32768,
components: []
});
} catch {}
}

View File

@@ -1,7 +1,7 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { prisma } from '../../../core/database/prisma';
import { EmbedBuilder } from 'discord.js';
import type { TextBasedChannel } from 'discord.js';
export const command: CommandMessage = {
name: 'cooldowns',
@@ -30,12 +30,6 @@ export const command: CommandMessage = {
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': '⛏️',
@@ -91,16 +85,38 @@ export const command: CommandMessage = {
cooldownText += `${emoji} **${actionName}**: ${timeStr}\n`;
}
embed.addFields({
name: `📋 Cooldowns (${cooldowns.length})`,
value: cooldownText,
inline: false
// Crear DisplayComponent
const display = {
type: 17,
accent_color: 0xFF6B6B,
components: [
{
type: 10,
content: `# ⏰ Cooldowns Activos\n${message.author.username}, estos son tus cooldowns:`
},
{ type: 14, divider: true },
{
type: 9,
components: [{
type: 10,
content: `**📋 Cooldowns (${cooldowns.length})**\n${cooldownText}`
}]
},
{ type: 14, spacing: 1 },
{
type: 10,
content: `*Los cooldowns se actualizan en tiempo real*`
}
]
};
// Enviar con flags
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({
display,
flags: 32768, // MessageFlags.IS_COMPONENTS_V2
reply: { messageReference: message.id }
});
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

@@ -1,6 +1,7 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { getOrCreateWallet } from '../../../game/economy/service';
import type { TextBasedChannel } from 'discord.js';
export const command: CommandMessage = {
name: 'monedas',
@@ -11,7 +12,27 @@ export const command: CommandMessage = {
usage: 'monedas',
run: async (message, _args, _client: Amayo) => {
const wallet = await getOrCreateWallet(message.author.id, message.guild!.id);
await message.reply(`💰 Monedas: ${wallet.coins}`);
const display = {
type: 17,
accent_color: 0xFFD700,
components: [
{
type: 9,
components: [{
type: 10,
content: `**💰 Monedas de ${message.author.username}**\n\nSaldo: **${wallet.coins.toLocaleString()}** monedas`
}]
}
]
};
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({
display,
flags: 32768,
reply: { messageReference: message.id }
});
}
};

View File

@@ -170,6 +170,7 @@ export const command: CommandMessage = {
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({
display,
flags: 32768, // MessageFlags.IS_COMPONENTS_V2
reply: { messageReference: message.id }
});
}

View File

@@ -1,7 +1,7 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { getStreakInfo, updateStreak } from '../../../game/streaks/service';
import { EmbedBuilder } from 'discord.js';
import type { TextBasedChannel } from 'discord.js';
export const command: CommandMessage = {
name: 'racha',
@@ -18,50 +18,49 @@ export const command: CommandMessage = {
// Actualizar racha
const { streak, newDay, rewards, daysIncreased } = await updateStreak(userId, guildId);
const embed = new EmbedBuilder()
.setColor(daysIncreased ? 0x00FF00 : 0xFFA500)
.setTitle('🔥 Racha Diaria')
.setDescription(`${message.author.username}, aquí está tu racha:`)
.setThumbnail(message.author.displayAvatarURL({ size: 128 }));
// Racha actual
embed.addFields(
{
name: '🔥 Racha Actual',
value: `**${streak.currentStreak}** días consecutivos`,
inline: true
// Construir componentes
const components: any[] = [
{
type: 10,
content: `# 🔥 Racha Diaria de ${message.author.username}`
},
{
name: '⭐ Mejor Racha',
value: `**${streak.longestStreak}** días`,
inline: true
{ type: 14, divider: true },
{
type: 9,
components: [{
type: 10,
content: `**📊 ESTADÍSTICAS**\n` +
`🔥 Racha Actual: **${streak.currentStreak}** días\n` +
`⭐ Mejor Racha: **${streak.longestStreak}** días\n` +
`📅 Días Activos: **${streak.totalDaysActive}** días`
}]
},
{
name: '📅 Días Activos',
value: `**${streak.totalDaysActive}** días totales`,
inline: true
}
);
{ type: 14, spacing: 1 }
];
// Mensaje de estado
if (newDay) {
if (daysIncreased) {
embed.addFields({
name: '✅ ¡Racha Incrementada!',
value: `Has mantenido tu racha por **${streak.currentStreak}** días seguidos.`,
inline: false
components.push({
type: 9,
components: [{
type: 10,
content: `**✅ ¡RACHA INCREMENTADA!**\nHas mantenido tu racha por **${streak.currentStreak}** días seguidos.`
}]
});
} else {
embed.addFields({
name: '⚠️ Racha Reiniciada',
value: 'Pasó más de un día sin actividad. Tu racha se ha reiniciado.',
inline: false
components.push({
type: 9,
components: [{
type: 10,
content: `**⚠️ RACHA REINICIADA**\nPasó más de un día sin actividad. Tu racha se ha reiniciado.`
}]
});
}
// Mostrar recompensas
if (rewards) {
let rewardsText = '';
let rewardsText = '**🎁 RECOMPENSA DEL DÍA**\n';
if (rewards.coins) rewardsText += `💰 **${rewards.coins.toLocaleString()}** monedas\n`;
if (rewards.items) {
rewards.items.forEach(item => {
@@ -69,19 +68,22 @@ export const command: CommandMessage = {
});
}
if (rewardsText) {
embed.addFields({
name: '🎁 Recompensa del Día',
value: rewardsText,
inline: false
});
}
components.push({ type: 14, spacing: 1 });
components.push({
type: 9,
components: [{
type: 10,
content: rewardsText
}]
});
}
} else {
embed.addFields({
name: ' Ya Reclamaste Hoy',
value: 'Ya has reclamado tu recompensa diaria. Vuelve mañana para continuar tu racha.',
inline: false
components.push({
type: 9,
components: [{
type: 10,
content: `** YA RECLAMASTE HOY**\nYa has reclamado tu recompensa diaria. Vuelve mañana para continuar tu racha.`
}]
});
}
@@ -91,17 +93,28 @@ export const command: CommandMessage = {
if (nextMilestone) {
const remaining = nextMilestone - streak.currentStreak;
embed.addFields({
name: '🎯 Próximo Hito',
value: `Faltan **${remaining}** días para alcanzar el día **${nextMilestone}**`,
inline: false
components.push({ type: 14, spacing: 1 });
components.push({
type: 9,
components: [{
type: 10,
content: `**🎯 PRÓXIMO HITO**\nFaltan **${remaining}** días para alcanzar el día **${nextMilestone}**`
}]
});
}
embed.setFooter({ text: 'Juega todos los días para mantener tu racha activa' });
embed.setTimestamp();
const display = {
type: 17,
accent_color: daysIncreased ? 0x00FF00 : 0xFFA500,
components
};
await message.reply({ embeds: [embed] });
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({
display,
flags: 32768,
reply: { messageReference: message.id }
});
} catch (error) {
console.error('Error en comando racha:', error);
await message.reply('❌ Error al obtener tu racha diaria.');

View File

@@ -1,7 +1,7 @@
import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client';
import { getPlayerStatsFormatted } from '../../../game/stats/service';
import { EmbedBuilder } from 'discord.js';
import type { TextBasedChannel } from 'discord.js';
export const command: CommandMessage = {
name: 'stats',
@@ -19,19 +19,29 @@ export const command: CommandMessage = {
// Obtener estadísticas formateadas
const stats = await getPlayerStatsFormatted(userId, guildId);
// Crear embed
const embed = new EmbedBuilder()
.setColor(0x5865F2)
.setTitle(`📊 Estadísticas de ${targetUser.username}`)
.setThumbnail(targetUser.displayAvatarURL({ size: 128 }))
.setTimestamp();
// Construir componentes de DisplayComponent
const components: any[] = [
// Header
{
type: 10,
content: `# 📊 Estadísticas de ${targetUser.username}`
},
{ type: 14, divider: true }
];
// Actividades
if (stats.activities) {
const activitiesText = Object.entries(stats.activities)
.map(([key, value]) => `${key}: **${value.toLocaleString()}**`)
.join('\n');
embed.addFields({ name: '🎮 Actividades', value: activitiesText || 'Sin datos', inline: true });
components.push({
type: 9,
components: [{
type: 10,
content: `**🎮 ACTIVIDADES**\n${activitiesText || 'Sin datos'}`
}]
});
components.push({ type: 14, spacing: 1 });
}
// Combate
@@ -39,7 +49,14 @@ export const command: CommandMessage = {
const combatText = Object.entries(stats.combat)
.map(([key, value]) => `${key}: **${value.toLocaleString()}**`)
.join('\n');
embed.addFields({ name: '⚔️ Combate', value: combatText || 'Sin datos', inline: true });
components.push({
type: 9,
components: [{
type: 10,
content: `**⚔️ COMBATE**\n${combatText || 'Sin datos'}`
}]
});
components.push({ type: 14, spacing: 1 });
}
// Economía
@@ -47,7 +64,14 @@ export const command: CommandMessage = {
const economyText = Object.entries(stats.economy)
.map(([key, value]) => `${key}: **${value.toLocaleString()}**`)
.join('\n');
embed.addFields({ name: '💰 Economía', value: economyText || 'Sin datos', inline: false });
components.push({
type: 9,
components: [{
type: 10,
content: `**💰 ECONOMÍA**\n${economyText || 'Sin datos'}`
}]
});
components.push({ type: 14, spacing: 1 });
}
// Items
@@ -55,7 +79,14 @@ export const command: CommandMessage = {
const itemsText = Object.entries(stats.items)
.map(([key, value]) => `${key}: **${value.toLocaleString()}**`)
.join('\n');
embed.addFields({ name: '📦 Items', value: itemsText || 'Sin datos', inline: true });
components.push({
type: 9,
components: [{
type: 10,
content: `**📦 ITEMS**\n${itemsText || 'Sin datos'}`
}]
});
components.push({ type: 14, spacing: 1 });
}
// Récords
@@ -63,12 +94,29 @@ export const command: CommandMessage = {
const recordsText = Object.entries(stats.records)
.map(([key, value]) => `${key}: **${value.toLocaleString()}**`)
.join('\n');
embed.addFields({ name: '🏆 Récords', value: recordsText || 'Sin datos', inline: true });
components.push({
type: 9,
components: [{
type: 10,
content: `**🏆 RÉCORDS**\n${recordsText || 'Sin datos'}`
}]
});
}
embed.setFooter({ text: 'Usa !ranking-stats para ver el ranking global' });
// Crear DisplayComponent
const display = {
type: 17,
accent_color: 0x5865F2,
components
};
await message.reply({ embeds: [embed] });
// Enviar con flags
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({
display,
flags: 32768, // MessageFlags.IS_COMPONENTS_V2
reply: { messageReference: message.id }
});
} catch (error) {
console.error('Error en comando stats:', error);
await message.reply('❌ Error al obtener las estadísticas.');