refactor(componentsV2): migrate all admin and game message commands to new display components API

This commit is contained in:
2025-10-05 18:09:15 -05:00
parent b37261e73b
commit f317b6d3bf
11 changed files with 522 additions and 810 deletions

View File

@@ -3,6 +3,7 @@ import type Amayo from '../../../core/client';
import { hasManageGuildOrStaff } from '../../../core/lib/permissions'; import { hasManageGuildOrStaff } from '../../../core/lib/permissions';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import type { TextBasedChannel } from 'discord.js'; import type { TextBasedChannel } from 'discord.js';
import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'area-eliminar', name: 'area-eliminar',
@@ -12,28 +13,18 @@ export const command: CommandMessage = {
description: 'Eliminar un área del servidor', description: 'Eliminar un área del servidor',
usage: 'area-eliminar <key>', usage: 'area-eliminar <key>',
run: async (message, args, client: Amayo) => { run: async (message, args, client: Amayo) => {
const channel = message.channel as TextBasedChannel & { send: Function };
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma); const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
if (!allowed) { if (!allowed) {
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [
type: 17, buildDisplay(0xFF0000, [
accent_color: 0xFF0000, textBlock('❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.')
components: [{ ])
type: 9, ],
components: [{ reply: { messageReference: message.id }
type: 10,
content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
}]
}]
}],
message_reference: {
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -42,26 +33,17 @@ export const command: CommandMessage = {
const key = args[0]?.trim(); const key = args[0]?.trim();
if (!key) { if (!key) {
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [
type: 17, buildDisplay(0xFFA500, [
accent_color: 0xFFA500, textBlock('⚠️ **Uso Incorrecto**'),
components: [{ dividerBlock(),
type: 9, textBlock('└ Uso: `!area-eliminar <key>`\n└ Ejemplo: `!area-eliminar mine.cavern`')
components: [{ ])
type: 10, ],
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-eliminar <key>`\n└ Ejemplo: `!area-eliminar mine.cavern`' reply: { messageReference: message.id }
}]
}]
}],
message_reference: {
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -71,26 +53,17 @@ export const command: CommandMessage = {
}); });
if (!area) { if (!area) {
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [
type: 17, buildDisplay(0xFF0000, [
accent_color: 0xFF0000, textBlock(`❌ **Área No Encontrada**`),
components: [{ dividerBlock(),
type: 9, textBlock(`└ No se encontró el área local con key \`${key}\` en este servidor.`)
components: [{ ])
type: 10, ],
content: `❌ **Área No Encontrada**\n└ No se encontró el área local con key \`${key}\` en este servidor.` reply: { messageReference: message.id }
}]
}]
}],
message_reference: {
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -109,38 +82,23 @@ export const command: CommandMessage = {
where: { id: area.id } where: { id: area.id }
}); });
const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [
type: 17, buildDisplay(0x00FF00, [
accent_color: 0x00FF00, textBlock(`✅ **Área Eliminada Exitosamente**`),
components: [ dividerBlock(),
{ textBlock(`└ Key: \`${key}\``),
type: 9, ...(levelsCount > 0
components: [{ ? [
type: 10, dividerBlock(),
content: `✅ **Área Eliminada Exitosamente**\n└ Key: \`${key}\`` textBlock(`⚠️ Se eliminaron ${levelsCount} nivel(es) asociado(s).`),
}] ]
}, : []),
...(levelsCount > 0 ? [{ ])
type: 14, ],
divider: true reply: { messageReference: message.id }
}, {
type: 9,
components: [{
type: 10,
content: `⚠️ Se eliminaron ${levelsCount} nivel(es) asociado(s).`
}]
}] : [])
]
}],
message_reference: {
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
} }
}; };

View File

@@ -3,6 +3,7 @@ import type Amayo from '../../../core/client';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import { ComponentType, ButtonStyle } from 'discord-api-types/v10'; import { ComponentType, ButtonStyle } from 'discord-api-types/v10';
import type { MessageComponentInteraction, TextBasedChannel } from 'discord.js'; import type { MessageComponentInteraction, TextBasedChannel } from 'discord.js';
import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'areas-lista', name: 'areas-lista',
@@ -34,29 +35,27 @@ export const command: CommandMessage = {
const totalPages = Math.ceil(total / perPage); const totalPages = Math.ceil(total / perPage);
const display = { const displayBlocks = [
type: 17, textBlock(`# 🗺️ Lista de Áreas`),
accent_color: 0x00FF00, dividerBlock(),
components: [ textBlock(`Página ${page}/${totalPages} • Total: ${total}`),
{ dividerBlock({ divider: false, spacing: 2 }),
type: 9, ...areas.flatMap((area, index) => {
components: [{ const lines = [
type: 10, `**${area.name || area.key}**`,
content: `**🗺️ Lista de Áreas**\nPágina ${page}/${totalPages} • Total: ${total}` `└ Key: \`${area.key}\``,
}] `${area.guildId === guildId ? '📍 Local' : '🌐 Global'}`,
}, ].join('\n');
{ type: 14, divider: true },
...areas.map(area => ({ const blocks = [textBlock(lines)];
type: 9, if (index < areas.length - 1) {
components: [{ blocks.push(dividerBlock({ divider: false, spacing: 1 }));
type: 10, }
content: `**${area.name || area.key}**\n` + return blocks;
`└ Key: \`${area.key}\`\n` + })
`${area.guildId === guildId ? '📍 Local' : '🌐 Global'}` ];
}]
})) const display = buildDisplay(0x00FF00, displayBlocks);
]
};
const buttons: any[] = []; const buttons: any[] = [];
@@ -80,7 +79,9 @@ export const command: CommandMessage = {
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
const msg = await (channel.send as any)({ const msg = await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
reply: { messageReference: message.id },
components: [ components: [
display, display,
...(buttons.length > 0 ? [{ ...(buttons.length > 0 ? [{

View File

@@ -2,6 +2,7 @@ import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client'; import type Amayo from '../../../core/client';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import type { TextBasedChannel } from 'discord.js'; import type { TextBasedChannel } from 'discord.js';
import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'item-ver', name: 'item-ver',
@@ -34,70 +35,42 @@ export const command: CommandMessage = {
const props = item.props as any || {}; const props = item.props as any || {};
const tags = item.tags || []; const tags = item.tags || [];
const display = { const blocks = [
type: 17, textBlock(`# 🛠️ ${item.name || item.key}`),
accent_color: 0x00D9FF, dividerBlock(),
components: [ textBlock([
{ `**Key:** \`${item.key}\``,
type: 9, `**Nombre:** ${item.name || '*Sin nombre*'}`,
components: [{ `**Descripción:** ${item.description || '*Sin descripción*'}`,
type: 10, `**Categoría:** ${item.category || '*Sin categoría*'}`,
content: `**🛠️ ${item.name || item.key}**` `**Stackable:** ${item.stackable ? 'Sí' : 'No'}`,
}] `**Máx. Inventario:** ${item.maxPerInventory ?? 'Ilimitado'}`,
}, `**Ámbito:** ${item.guildId ? '📍 Local del servidor' : '🌐 Global'}`,
{ type: 14, divider: true }, ].join('\n')),
{ ];
type: 9,
components: [{
type: 10,
content: `**Key:** \`${item.key}\`\n` +
`**Nombre:** ${item.name || '*Sin nombre*'}\n` +
`**Descripción:** ${item.description || '*Sin descripción*'}\n` +
`**Categoría:** ${item.category || '*Sin categoría*'}\n` +
`**Stackable:** ${item.stackable ? 'Sí' : 'No'}\n` +
`**Máx. Inventario:** ${item.maxPerInventory || 'Ilimitado'}\n` +
`**Ámbito:** ${item.guildId ? '📍 Local del servidor' : '🌐 Global'}`
}]
}
]
};
if (tags.length > 0) { if (tags.length > 0) {
display.components.push({ type: 14, divider: true }); blocks.push(dividerBlock());
display.components.push({ blocks.push(textBlock(`**Tags:** ${tags.join(', ')}`));
type: 9,
components: [{
type: 10,
content: `**Tags:** ${tags.join(', ')}`
}]
});
} }
if (item.icon) { if (item.icon) {
display.components.push({ type: 14, divider: true }); blocks.push(dividerBlock());
display.components.push({ blocks.push(textBlock(`**Icon URL:** ${item.icon}`));
type: 9,
components: [{
type: 10,
content: `**Icon URL:** ${item.icon}`
}]
});
} }
if (Object.keys(props).length > 0) { if (Object.keys(props).length > 0) {
display.components.push({ type: 14, divider: true }); blocks.push(dividerBlock());
display.components.push({ blocks.push(textBlock(`**Props (JSON):**\n\`\`\`json\n${JSON.stringify(props, null, 2)}\n\`\`\``));
type: 9,
components: [{
type: 10,
content: `**Props (JSON):**\n\`\`\`json\n${JSON.stringify(props, null, 2)}\n\`\`\``
}]
});
} }
const display = buildDisplay(0x00D9FF, blocks);
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({ await (channel.send as any)({
display, content: null,
flags: 32768,
components: [display],
reply: { messageReference: message.id } reply: { messageReference: message.id }
}); });
} }

View File

@@ -3,6 +3,7 @@ import type Amayo from '../../../core/client';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import { ComponentType, ButtonStyle } from 'discord-api-types/v10'; import { ComponentType, ButtonStyle } from 'discord-api-types/v10';
import type { MessageComponentInteraction, TextBasedChannel } from 'discord.js'; import type { MessageComponentInteraction, TextBasedChannel } from 'discord.js';
import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'items-lista', name: 'items-lista',
@@ -34,32 +35,28 @@ export const command: CommandMessage = {
const totalPages = Math.ceil(total / perPage); const totalPages = Math.ceil(total / perPage);
const display = { const displayBlocks = [
type: 17, textBlock(`# 🛠️ Lista de Items`),
accent_color: 0x00D9FF, dividerBlock(),
components: [ textBlock(`Página ${page}/${totalPages} • Total: ${total}`),
{ dividerBlock({ divider: false, spacing: 2 }),
type: 9, ...items.flatMap((item, index) => {
components: [{ const lines = [
type: 10, `**${item.name || item.key}**`,
content: `**🛠️ Lista de Items**\nPágina ${page}/${totalPages} • Total: ${total}` `└ Key: \`${item.key}\``,
}] `└ Categoría: ${item.category || '*Sin categoría*'}`,
}, `${item.stackable ? '📚 Apilable' : '🔒 No apilable'}${item.maxPerInventory ? ` (Máx: ${item.maxPerInventory})` : ''}${item.guildId === guildId ? ' • 📍 Local' : ' • 🌐 Global'}`,
{ type: 14, divider: true }, ].join('\n');
...items.map(item => ({
type: 9, const blocks = [textBlock(lines)];
components: [{ if (index < items.length - 1) {
type: 10, blocks.push(dividerBlock({ divider: false, spacing: 1 }));
content: `**${item.name || item.key}**\n` + }
`└ Key: \`${item.key}\`\n` + return blocks;
`└ Categoría: ${item.category || '*Sin categoría*'}\n` + })
`${item.stackable ? '📚 Apilable' : '🔒 No apilable'}` + ];
(item.maxPerInventory ? ` (Máx: ${item.maxPerInventory})` : '') +
(item.guildId === guildId ? ' • 📍 Local' : ' • 🌐 Global') const display = buildDisplay(0x00D9FF, displayBlocks);
}]
}))
]
};
const buttons: any[] = []; const buttons: any[] = [];
@@ -90,7 +87,9 @@ export const command: CommandMessage = {
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
const msg = await (channel.send as any)({ const msg = await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
reply: { messageReference: message.id },
components: [ components: [
display, display,
...(buttons.length > 0 ? [{ ...(buttons.length > 0 ? [{

View File

@@ -2,7 +2,8 @@ import type {CommandMessage} from '../../../core/types/commands';
import type Amayo from '../../../core/client'; import type Amayo from '../../../core/client';
import {hasManageGuildOrStaff} from '../../../core/lib/permissions'; import {hasManageGuildOrStaff} from '../../../core/lib/permissions';
import {prisma} from '../../../core/database/prisma'; import {prisma} from '../../../core/database/prisma';
import {ButtonStyle, ComponentType, MessageFlags, TextInputStyle} from 'discord-api-types/v10'; import {ButtonStyle, ComponentType, TextInputStyle} from 'discord-api-types/v10';
import {buildDisplay, dividerBlock, textBlock} from '../../../core/lib/componentsV2';
import type {ButtonInteraction, MessageComponentInteraction, TextBasedChannel} from 'discord.js'; import type {ButtonInteraction, MessageComponentInteraction, TextBasedChannel} from 'discord.js';
interface AchievementState { interface AchievementState {
@@ -53,25 +54,12 @@ export const command: CommandMessage = {
rewards: { coins: 100 } rewards: { coins: 100 }
}; };
// Crear mensaje con DisplayComponents
const displayMessage = createDisplay(state);
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
const editorMsg = await channel.send({ const editorMsg = await (channel.send as any)({
...displayMessage, content: null,
flags: 32768, // MessageFlags.IsComponentsV2 flags: 32768,
components: [ reply: { messageReference: message.id },
{ components: buildEditorComponents(state)
type: ComponentType.ActionRow,
components: [
{ type: ComponentType.Button, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ach_base' },
{ type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Requisitos', custom_id: 'ach_req' },
{ type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Recompensas', custom_id: 'ach_reward' },
{ type: ComponentType.Button, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ach_save' },
{ type: ComponentType.Button, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ach_cancel' }
]
}
]
}); });
const collector = editorMsg.createMessageComponentCollector({ const collector = editorMsg.createMessageComponentCollector({
@@ -87,19 +75,13 @@ export const command: CommandMessage = {
case 'ach_cancel': case 'ach_cancel':
await i.deferUpdate(); await i.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
display: { content: null,
type: 17,
accent_color: 0xFF0000,
components: [{
type: 9,
components: [{
type: 10,
content: '**❌ Creación de logro cancelada.**'
}]
}]
},
flags: 32768, flags: 32768,
components: [] components: [
buildDisplay(0xFF0000, [
textBlock('**❌ Creación de logro cancelada.**')
])
]
}); });
collector.stop('cancel'); collector.stop('cancel');
return; return;
@@ -139,19 +121,13 @@ export const command: CommandMessage = {
await i.reply({ content: '✅ Logro creado exitosamente!', flags: 64 }); await i.reply({ content: '✅ Logro creado exitosamente!', flags: 64 });
await editorMsg.edit({ await editorMsg.edit({
display: { content: null,
type: 17,
accent_color: 0x00FF00,
components: [{
type: 9,
components: [{
type: 10,
content: `**✅ Logro \`${state.key}\` creado exitosamente.**`
}]
}]
},
flags: 32768, flags: 32768,
components: [] components: [
buildDisplay(0x00FF00, [
textBlock(`**✅ Logro \`${state.key}\` creado exitosamente.**`)
])
]
}); });
collector.stop('saved'); collector.stop('saved');
return; return;
@@ -168,19 +144,13 @@ export const command: CommandMessage = {
if (r === 'time') { if (r === 'time') {
try { try {
await editorMsg.edit({ await editorMsg.edit({
display: { content: null,
type: 17,
accent_color: 0xFFA500,
components: [{
type: 9,
components: [{
type: 10,
content: '**⏰ Editor expirado.**'
}]
}]
},
flags: 32768, flags: 32768,
components: [] components: [
buildDisplay(0xFFA500, [
textBlock('**⏰ Editor expirado.**')
])
]
}); });
} catch {} } catch {}
} }
@@ -188,54 +158,41 @@ export const command: CommandMessage = {
} }
}; };
function createDisplay(state: AchievementState) { function buildEditorDisplay(state: AchievementState) {
return { const baseInfo = [
display: { `**Nombre:** ${state.name || '*Sin definir*'}`,
type: 17, // Container `**Descripción:** ${state.description || '*Sin definir*'}`,
accent_color: 0xFFD700, `**Categoría:** ${state.category || 'economy'}`,
`**Icono:** ${state.icon || '🏆'}`,
`**Puntos:** ${state.points ?? 10}`,
`**Oculto:** ${state.hidden ? 'Sí' : 'No'}`,
].join('\n');
return buildDisplay(0xFFD700, [
textBlock(`# 🏆 Creando Logro: \`${state.key}\``),
dividerBlock(),
textBlock(baseInfo),
dividerBlock(),
textBlock(`**Requisitos:**\n\`\`\`json\n${JSON.stringify(state.requirements, null, 2)}\n\`\`\``),
dividerBlock(),
textBlock(`**Recompensas:**\n\`\`\`json\n${JSON.stringify(state.rewards, null, 2)}\n\`\`\``),
]);
}
function buildEditorComponents(state: AchievementState) {
return [
buildEditorDisplay(state),
{
type: ComponentType.ActionRow,
components: [ components: [
{ { type: ComponentType.Button, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ach_base' },
type: 9, // Section { type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Requisitos', custom_id: 'ach_req' },
components: [ { type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Recompensas', custom_id: 'ach_reward' },
{ { type: ComponentType.Button, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ach_save' },
type: 10, // Text Display { type: ComponentType.Button, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ach_cancel' }
content: `**🏆 Creando Logro: \`${state.key}\`**`
}
]
},
{ type: 14, divider: true }, // Separator
{
type: 9,
components: [
{
type: 10,
content: `**Nombre:** ${state.name || '*Sin definir*'}\n**Descripción:** ${state.description || '*Sin definir*'}\n**Categoría:** ${state.category || 'economy'}\n**Icono:** ${state.icon || '🏆'}\n**Puntos:** ${state.points || 10}\n**Oculto:** ${state.hidden ? 'Sí' : 'No'}`
}
]
},
{ type: 14, divider: true },
{
type: 9,
components: [
{
type: 10,
content: `**Requisitos:**\n\`\`\`json\n${JSON.stringify(state.requirements, null, 2)}\n\`\`\``
}
]
},
{ type: 14, divider: true },
{
type: 9,
components: [
{
type: 10,
content: `**Recompensas:**\n\`\`\`json\n${JSON.stringify(state.rewards, null, 2)}\n\`\`\``
}
]
}
] ]
} }
}; ];
} }
async function showBaseModal(i: ButtonInteraction, state: AchievementState, editorMsg: any) { async function showBaseModal(i: ButtonInteraction, state: AchievementState, editorMsg: any) {
@@ -316,7 +273,11 @@ async function showBaseModal(i: ButtonInteraction, state: AchievementState, edit
state.points = parseInt(submit.components.getTextInputValue('points')) || 10; state.points = parseInt(submit.components.getTextInputValue('points')) || 10;
await submit.deferUpdate(); await submit.deferUpdate();
await editorMsg.edit(createDisplay(state)); await editorMsg.edit({
content: null,
flags: 32768,
components: buildEditorComponents(state)
});
} }
async function showRequirementsModal(i: ButtonInteraction, state: AchievementState, editorMsg: any) { async function showRequirementsModal(i: ButtonInteraction, state: AchievementState, editorMsg: any) {
@@ -351,7 +312,11 @@ async function showRequirementsModal(i: ButtonInteraction, state: AchievementSta
try { try {
state.requirements = JSON.parse(submit.components.getTextInputValue('requirements')); state.requirements = JSON.parse(submit.components.getTextInputValue('requirements'));
await submit.deferUpdate(); await submit.deferUpdate();
await editorMsg.edit(createDisplay(state)); await editorMsg.edit({
content: null,
flags: 32768,
components: buildEditorComponents(state)
});
} catch (e) { } catch (e) {
await submit.reply({ content: '❌ JSON inválido en requisitos.', flags: 64 }); await submit.reply({ content: '❌ JSON inválido en requisitos.', flags: 64 });
} }
@@ -389,7 +354,11 @@ async function showRewardsModal(i: ButtonInteraction, state: AchievementState, e
try { try {
state.rewards = JSON.parse(submit.components.getTextInputValue('rewards')); state.rewards = JSON.parse(submit.components.getTextInputValue('rewards'));
await submit.deferUpdate(); await submit.deferUpdate();
await editorMsg.edit(createDisplay(state)); await editorMsg.edit({
content: null,
flags: 32768,
components: buildEditorComponents(state)
});
} catch (e) { } catch (e) {
await submit.reply({ content: '❌ JSON inválido en recompensas.', flags: 64 }); await submit.reply({ content: '❌ JSON inválido en recompensas.', flags: 64 });
} }

View File

@@ -2,7 +2,7 @@ import type { CommandMessage } from '../../../core/types/commands';
import type Amayo from '../../../core/client'; import type Amayo from '../../../core/client';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import type { TextBasedChannel } from 'discord.js'; import type { TextBasedChannel } from 'discord.js';
import { ComponentType, ButtonStyle } from 'discord-api-types/v10'; import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'logro-ver', name: 'logro-ver',
@@ -50,78 +50,40 @@ export const command: CommandMessage = {
const req = achievement.requirements as any; const req = achievement.requirements as any;
const rew = achievement.rewards as any; const rew = achievement.rewards as any;
const display = { const blocks = [
type: 17, textBlock(`${achievement.icon || '🏆'} **${achievement.name}**`),
accent_color: 0xFFD700, dividerBlock(),
components: [ textBlock([
{ `**Descripción:** ${achievement.description}`,
type: 9, `**Key:** \`${achievement.key}\``,
components: [ `**Categoría:** ${achievement.category}`,
{ `**Puntos:** ${achievement.points} pts`,
type: 10, `**Visibilidad:** ${achievement.hidden ? '🔒 Oculto' : '👁️ Visible'}`,
content: `${achievement.icon || '🏆'} **${achievement.name}**` `**Ámbito:** ${achievement.guildId ? '📍 Local del servidor' : '🌐 Global'}`,
} `**Desbloqueados:** ${unlockedCount} jugadores`,
] ].join('\n')),
}, dividerBlock(),
{ type: 14, divider: true }, textBlock(`**📋 Requisitos:**\n\`\`\`json\n${JSON.stringify(req, null, 2)}\n\`\`\``),
{ dividerBlock(),
type: 9, textBlock(`**🎁 Recompensas:**\n\`\`\`json\n${JSON.stringify(rew, null, 2)}\n\`\`\``),
components: [ ];
{
type: 10,
content: `**Descripción:** ${achievement.description}\n` +
`**Key:** \`${achievement.key}\`\n` +
`**Categoría:** ${achievement.category}\n` +
`**Puntos:** ${achievement.points} pts\n` +
`**Visibilidad:** ${achievement.hidden ? '🔒 Oculto' : '👁️ Visible'}\n` +
`**Ámbito:** ${achievement.guildId ? '📍 Local del servidor' : '🌐 Global'}\n` +
`**Desbloqueados:** ${unlockedCount} jugadores`
}
]
},
{ type: 14, divider: true },
{
type: 9,
components: [
{
type: 10,
content: `**📋 Requisitos:**\n\`\`\`json\n${JSON.stringify(req, null, 2)}\n\`\`\``
}
]
},
{ type: 14, divider: true },
{
type: 9,
components: [
{
type: 10,
content: `**🎁 Recompensas:**\n\`\`\`json\n${JSON.stringify(rew, null, 2)}\n\`\`\``
}
]
}
]
};
if (achievement.unlocked.length > 0) { if (achievement.unlocked.length > 0) {
display.components.push({ type: 14, divider: true }); const unlockedLines = achievement.unlocked.slice(0, 5)
display.components.push({ .map(pa => `• <@${pa.userId}> - ${pa.unlockedAt ? new Date(pa.unlockedAt).toLocaleDateString() : 'N/A'}`)
type: 9, .join('\n');
components: [ blocks.push(dividerBlock());
{ blocks.push(textBlock(`**🏆 Últimos Desbloqueados:**\n${unlockedLines}`));
type: 10,
content: `**🏆 Últimos Desbloqueados:**\n` +
achievement.unlocked.slice(0, 5).map(pa =>
`• <@${pa.userId}> - ${pa.unlockedAt ? new Date(pa.unlockedAt).toLocaleDateString() : 'N/A'}`
).join('\n')
}
]
});
} }
const display = buildDisplay(0xFFD700, blocks);
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [display] components: [display],
reply: { messageReference: message.id }
}); });
} }
}; };

View File

@@ -4,6 +4,7 @@ import { hasManageGuildOrStaff } from '../../../core/lib/permissions';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import { ComponentType, ButtonStyle } from 'discord-api-types/v10'; import { ComponentType, ButtonStyle } from 'discord-api-types/v10';
import type { MessageComponentInteraction, TextBasedChannel } from 'discord.js'; import type { MessageComponentInteraction, TextBasedChannel } from 'discord.js';
import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'logros-lista', name: 'logros-lista',
@@ -35,36 +36,29 @@ export const command: CommandMessage = {
const totalPages = Math.ceil(total / perPage); const totalPages = Math.ceil(total / perPage);
const display = { const displayBlocks = [
type: 17, textBlock(`# 🏆 Lista de Logros`),
accent_color: 0xFFD700, dividerBlock(),
components: [ textBlock(`Página ${page}/${totalPages} • Total: ${total}`),
{ dividerBlock({ divider: false, spacing: 2 }),
type: 9, ...achievements.flatMap((ach, index) => {
components: [ const lines = [
{ `${ach.icon || '🏆'} **${ach.name}** (${ach.points} pts)`,
type: 10, `└ Key: \`${ach.key}\``,
content: `**🏆 Lista de Logros**\nPágina ${page}/${totalPages} • Total: ${total}` `└ Categoría: ${ach.category}`,
} `${ach.description}`,
] `${ach.hidden ? '🔒 Oculto' : '👁️ Visible'}${ach.guildId === guildId ? ' • 📍 Local' : ' • 🌐 Global'}`,
}, ].join('\n');
{ type: 14, divider: true },
...achievements.map(ach => ({ const blocks = [textBlock(lines)];
type: 9, if (index < achievements.length - 1) {
components: [ blocks.push(dividerBlock({ divider: false, spacing: 1 }));
{ }
type: 10, return blocks;
content: `${ach.icon || '🏆'} **${ach.name}** (${ach.points} pts)\n` + })
`└ Key: \`${ach.key}\`\n` + ];
`└ Categoría: ${ach.category}\n` +
`${ach.description}\n` + const display = buildDisplay(0xFFD700, displayBlocks);
`${ach.hidden ? '🔒 Oculto' : '👁️ Visible'}` +
(ach.guildId === guildId ? ' • 📍 Local' : ' • 🌐 Global')
}
]
}))
]
};
const buttons: any[] = []; const buttons: any[] = [];
@@ -95,7 +89,9 @@ export const command: CommandMessage = {
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
const msg = await (channel.send as any)({ const msg = await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
reply: { messageReference: message.id },
components: [ components: [
display, display,
...(buttons.length > 0 ? [{ ...(buttons.length > 0 ? [{

View File

@@ -3,6 +3,7 @@ import type Amayo from '../../../core/client';
import { hasManageGuildOrStaff } from '../../../core/lib/permissions'; import { hasManageGuildOrStaff } from '../../../core/lib/permissions';
import { prisma } from '../../../core/database/prisma'; import { prisma } from '../../../core/database/prisma';
import { ComponentType, TextInputStyle, ButtonStyle } from 'discord-api-types/v10'; import { ComponentType, TextInputStyle, ButtonStyle } from 'discord-api-types/v10';
import { buildDisplay, dividerBlock, textBlock } from '../../../core/lib/componentsV2';
import type { ButtonInteraction, MessageComponentInteraction, TextBasedChannel } from 'discord.js'; import type { ButtonInteraction, MessageComponentInteraction, TextBasedChannel } from 'discord.js';
interface QuestState { interface QuestState {
@@ -53,24 +54,12 @@ export const command: CommandMessage = {
rewards: { coins: 500 } rewards: { coins: 500 }
}; };
const displayMessage = createDisplay(state);
const channel = message.channel as TextBasedChannel & { send: Function }; const channel = message.channel as TextBasedChannel & { send: Function };
const editorMsg = await channel.send({ const editorMsg = await (channel.send as any)({
...displayMessage, content: null,
flags: 32768, flags: 32768,
components: [ reply: { messageReference: message.id },
{ components: buildEditorComponents(state)
type: ComponentType.ActionRow,
components: [
{ type: ComponentType.Button, style: ButtonStyle.Primary, label: 'Base', custom_id: 'quest_base' },
{ type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Requisitos', custom_id: 'quest_req' },
{ type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Recompensas', custom_id: 'quest_reward' },
{ type: ComponentType.Button, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'quest_save' },
{ type: ComponentType.Button, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'quest_cancel' }
]
}
]
}); });
const collector = editorMsg.createMessageComponentCollector({ const collector = editorMsg.createMessageComponentCollector({
@@ -86,19 +75,13 @@ export const command: CommandMessage = {
case 'quest_cancel': case 'quest_cancel':
await i.deferUpdate(); await i.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
display: { content: null,
type: 17,
accent_color: 0xFF0000,
components: [{
type: 9,
components: [{
type: 10,
content: '**❌ Creación de misión cancelada.**'
}]
}]
},
flags: 32768, flags: 32768,
components: [] components: [
buildDisplay(0xFF0000, [
textBlock('**❌ Creación de misión cancelada.**')
])
]
}); });
collector.stop('cancel'); collector.stop('cancel');
return; return;
@@ -139,19 +122,13 @@ export const command: CommandMessage = {
await i.reply({ content: '✅ Misión creada exitosamente.', flags: 64 }); await i.reply({ content: '✅ Misión creada exitosamente.', flags: 64 });
await editorMsg.edit({ await editorMsg.edit({
display: { content: null,
type: 17,
accent_color: 0x00FF00,
components: [{
type: 9,
components: [{
type: 10,
content: `**✅ Misión \`${state.key}\` creada exitosamente.**`
}]
}]
},
flags: 32768, flags: 32768,
components: [] components: [
buildDisplay(0x00FF00, [
textBlock(`**✅ Misión \`${state.key}\` creada exitosamente.**`)
])
]
}); });
collector.stop('saved'); collector.stop('saved');
return; return;
@@ -168,19 +145,13 @@ export const command: CommandMessage = {
if (r === 'time') { if (r === 'time') {
try { try {
await editorMsg.edit({ await editorMsg.edit({
display: { content: null,
type: 17,
accent_color: 0xFFA500,
components: [{
type: 9,
components: [{
type: 10,
content: '**⏰ Editor expirado.**'
}]
}]
},
flags: 32768, flags: 32768,
components: [] components: [
buildDisplay(0xFFA500, [
textBlock('**⏰ Editor expirado.**')
])
]
}); });
} catch {} } catch {}
} }
@@ -188,7 +159,7 @@ export const command: CommandMessage = {
} }
}; };
function createDisplay(state: QuestState) { function buildEditorDisplay(state: QuestState) {
const typeEmojis: Record<string, string> = { const typeEmojis: Record<string, string> = {
daily: '📅', daily: '📅',
weekly: '📆', weekly: '📆',
@@ -196,53 +167,40 @@ function createDisplay(state: QuestState) {
event: '🎉' event: '🎉'
}; };
return { const baseInfo = [
display: { `**Nombre:** ${state.name || '*Sin definir*'}`,
type: 17, // Container `**Descripción:** ${state.description || '*Sin definir*'}`,
accent_color: 0x5865F2, `**Categoría:** ${state.category || 'mining'}`,
`**Tipo:** ${typeEmojis[state.type || 'daily']} ${state.type || 'daily'}`,
`**Icono:** ${state.icon || '📋'}`,
`**Repetible:** ${state.repeatable ? 'Sí' : 'No'}`
].join('\n');
return buildDisplay(0x5865F2, [
textBlock(`# 📜 Creando Misión: \`${state.key}\``),
dividerBlock(),
textBlock(baseInfo),
dividerBlock(),
textBlock(`**Requisitos:**\n\`\`\`json\n${JSON.stringify(state.requirements, null, 2)}\n\`\`\``),
dividerBlock(),
textBlock(`**Recompensas:**\n\`\`\`json\n${JSON.stringify(state.rewards, null, 2)}\n\`\`\``)
]);
}
function buildEditorComponents(state: QuestState) {
return [
buildEditorDisplay(state),
{
type: ComponentType.ActionRow,
components: [ components: [
{ { type: ComponentType.Button, style: ButtonStyle.Primary, label: 'Base', custom_id: 'quest_base' },
type: 9, // Section { type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Requisitos', custom_id: 'quest_req' },
components: [ { type: ComponentType.Button, style: ButtonStyle.Secondary, label: 'Recompensas', custom_id: 'quest_reward' },
{ { type: ComponentType.Button, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'quest_save' },
type: 10, // Text Display { type: ComponentType.Button, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'quest_cancel' }
content: `**📜 Creando Misión: \`${state.key}\`**`
}
]
},
{ type: 14, divider: true }, // Separator
{
type: 9,
components: [
{
type: 10,
content: `**Nombre:** ${state.name || '*Sin definir*'}\n**Descripción:** ${state.description || '*Sin definir*'}\n**Categoría:** ${state.category || 'mining'}\n**Tipo:** ${typeEmojis[state.type || 'daily']} ${state.type || 'daily'}\n**Icono:** ${state.icon || '📋'}\n**Repetible:** ${state.repeatable ? 'Sí' : 'No'}`
}
]
},
{ type: 14, divider: true },
{
type: 9,
components: [
{
type: 10,
content: `**Requisitos:**\n\`\`\`json\n${JSON.stringify(state.requirements, null, 2)}\n\`\`\``
}
]
},
{ type: 14, divider: true },
{
type: 9,
components: [
{
type: 10,
content: `**Recompensas:**\n\`\`\`json\n${JSON.stringify(state.rewards, null, 2)}\n\`\`\``
}
]
}
] ]
} }
}; ];
} }
async function showBaseModal(i: ButtonInteraction, state: QuestState, editorMsg: any) { async function showBaseModal(i: ButtonInteraction, state: QuestState, editorMsg: any) {
@@ -323,7 +281,11 @@ async function showBaseModal(i: ButtonInteraction, state: QuestState, editorMsg:
state.icon = submit.components.getTextInputValue('icon') || '📋'; state.icon = submit.components.getTextInputValue('icon') || '📋';
await submit.deferUpdate(); await submit.deferUpdate();
await editorMsg.edit(createDisplay(state)); await editorMsg.edit({
content: null,
flags: 32768,
components: buildEditorComponents(state)
});
} }
async function showRequirementsModal(i: ButtonInteraction, state: QuestState, editorMsg: any) { async function showRequirementsModal(i: ButtonInteraction, state: QuestState, editorMsg: any) {
@@ -358,7 +320,11 @@ async function showRequirementsModal(i: ButtonInteraction, state: QuestState, ed
try { try {
state.requirements = JSON.parse(submit.components.getTextInputValue('requirements')); state.requirements = JSON.parse(submit.components.getTextInputValue('requirements'));
await submit.deferUpdate(); await submit.deferUpdate();
await editorMsg.edit(createDisplay(state)); await editorMsg.edit({
content: null,
flags: 32768,
components: buildEditorComponents(state)
});
} catch (e) { } catch (e) {
await submit.reply({ content: '❌ JSON inválido en requisitos.', flags: 64 }); await submit.reply({ content: '❌ JSON inválido en requisitos.', flags: 64 });
} }
@@ -396,7 +362,11 @@ async function showRewardsModal(i: ButtonInteraction, state: QuestState, editorM
try { try {
state.rewards = JSON.parse(submit.components.getTextInputValue('rewards')); state.rewards = JSON.parse(submit.components.getTextInputValue('rewards'));
await submit.deferUpdate(); await submit.deferUpdate();
await editorMsg.edit(createDisplay(state)); await editorMsg.edit({
content: null,
flags: 32768,
components: buildEditorComponents(state)
});
} catch (e) { } catch (e) {
await submit.reply({ content: '❌ JSON inválido en recompensas.', flags: 64 }); await submit.reply({ content: '❌ JSON inválido en recompensas.', flags: 64 });
} }

View File

@@ -13,48 +13,61 @@ interface AreaState {
metadata?: any; metadata?: any;
} }
function createAreaDisplay(state: AreaState, editing: boolean = false) { function buildAreaDisplay(state: AreaState, editing: boolean = false) {
const title = editing ? 'Editando Área' : 'Creando Área'; const title = editing ? 'Editando Área' : 'Creando Área';
const statusText = [
'**📋 Estado Actual:**',
`**Nombre:** ${state.name || '❌ No configurado'}`,
`**Tipo:** ${state.type || '❌ No configurado'}`,
`**Config:** ${Object.keys(state.config || {}).length} campos`,
`**Metadata:** ${Object.keys(state.metadata || {}).length} campos`
].join('\n');
const instructionsText = [
'**🎮 Instrucciones:**',
'• **Base**: Configura nombre y tipo',
'• **Config (JSON)**: Configuración técnica',
'• **Meta (JSON)**: Metadatos adicionales',
'• **Guardar**: Confirma los cambios',
'• **Cancelar**: Descarta los cambios'
].join('\n');
return { return {
type: 17, type: 17,
accent_color: 0x00FF00, accent_color: 0x00FF00,
components: [ components: [
{ {
type: 9, type: 10,
components: [{ content: `# 🗺️ ${title}: \`${state.key}\``
type: 10,
content: `🗺️ **${title}: \`${state.key}\`**`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: statusText
type: 10,
content: `**📋 Estado Actual:**\n` +
`**Nombre:** ${state.name || '❌ No configurado'}\n` +
`**Tipo:** ${state.type || '❌ No configurado'}\n` +
`**Config:** ${Object.keys(state.config || {}).length} campos\n` +
`**Metadata:** ${Object.keys(state.metadata || {}).length} campos`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: instructionsText
type: 10,
content: `**🎮 Instrucciones:**\n` +
`• **Base**: Configura nombre y tipo\n` +
`• **Config (JSON)**: Configuración técnica\n` +
`• **Meta (JSON)**: Metadatos adicionales\n` +
`• **Guardar**: Confirma los cambios\n` +
`• **Cancelar**: Descarta los cambios`
}]
} }
] ]
}; };
} }
const buildEditorComponents = (state: AreaState, editing: boolean = false) => [
buildAreaDisplay(state, editing),
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
];
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'area-crear', name: 'area-crear',
type: 'message', type: 'message',
@@ -67,24 +80,17 @@ export const command: CommandMessage = {
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma); const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
if (!allowed) { if (!allowed) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
type: 10,
content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -92,24 +98,17 @@ export const command: CommandMessage = {
const key = args[0]?.trim(); const key = args[0]?.trim();
if (!key) { if (!key) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFFA500, accent_color: 0xFFA500,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-crear <key-única>`'
type: 10,
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-crear <key-única>`'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -118,47 +117,28 @@ export const command: CommandMessage = {
const exists = await prisma.gameArea.findFirst({ where: { key, guildId } }); const exists = await prisma.gameArea.findFirst({ where: { key, guildId } });
if (exists) { if (exists) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '❌ **Área Ya Existe**\n└ Ya existe un área con esa key en este servidor.'
type: 10,
content: '❌ **Área Ya Existe**\n└ Ya existe un área con esa key en este servidor.'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
const state: AreaState = { key, config: {}, metadata: {} }; const state: AreaState = { key, config: {}, metadata: {} };
const display = createAreaDisplay(state, false);
const editorMsg = await (channel.send as any)({ const editorMsg = await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [ components: buildEditorComponents(state, false),
display, reply: { messageReference: message.id }
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
]
}); });
const collector = editorMsg.createMessageComponentCollector({ time: 30*60_000, filter: (i)=> i.user.id === message.author.id }); const collector = editorMsg.createMessageComponentCollector({ time: 30*60_000, filter: (i)=> i.user.id === message.author.id });
@@ -169,16 +149,14 @@ export const command: CommandMessage = {
case 'ga_cancel': case 'ga_cancel':
await i.deferUpdate(); await i.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '**❌ Editor de Área cancelado.**'
type: 10,
content: '**❌ Editor de Área cancelado.**'
}]
}] }]
}] }]
}); });
@@ -198,16 +176,14 @@ export const command: CommandMessage = {
await prisma.gameArea.create({ data: { guildId, key: state.key, name: state.name!, type: state.type!, config: state.config ?? {}, metadata: state.metadata ?? {} } }); await prisma.gameArea.create({ data: { guildId, key: state.key, name: state.name!, type: state.type!, config: state.config ?? {}, metadata: state.metadata ?? {} } });
await i.reply({ content: '✅ Área guardada.', flags: MessageFlags.Ephemeral }); await i.reply({ content: '✅ Área guardada.', flags: MessageFlags.Ephemeral });
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0x00FF00, accent_color: 0x00FF00,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: `**✅ Área \`${state.key}\` creada exitosamente.**`
type: 10,
content: `**✅ Área \`${state.key}\` creada exitosamente.**`
}]
}] }]
}] }]
}); });
@@ -223,16 +199,14 @@ export const command: CommandMessage = {
if (r==='time') { if (r==='time') {
try { try {
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFFA500, accent_color: 0xFFA500,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '**⏰ Editor expirado.**'
type: 10,
content: '**⏰ Editor expirado.**'
}]
}] }]
}] }]
}); });
@@ -255,22 +229,10 @@ async function showBaseModal(i: ButtonInteraction, state: AreaState, editorMsg:
await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral }); await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral });
// Actualizar display // Actualizar display
const newDisplay = createAreaDisplay(state, editing);
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [ components: buildEditorComponents(state, editing)
newDisplay,
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
]
}); });
} catch {} } catch {}
} }
@@ -298,22 +260,10 @@ async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'con
} }
// Actualizar display // Actualizar display
const newDisplay = createAreaDisplay(state, editing);
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [ components: buildEditorComponents(state, editing)
newDisplay,
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
]
}); });
} catch {} } catch {}
} }

View File

@@ -13,48 +13,61 @@ interface AreaState {
metadata?: any; metadata?: any;
} }
function createAreaDisplay(state: AreaState, editing: boolean = false) { function buildAreaDisplay(state: AreaState, editing: boolean = false) {
const title = editing ? 'Editando Área' : 'Creando Área'; const title = editing ? 'Editando Área' : 'Creando Área';
const statusText = [
'**📋 Estado Actual:**',
`**Nombre:** ${state.name || '❌ No configurado'}`,
`**Tipo:** ${state.type || '❌ No configurado'}`,
`**Config:** ${Object.keys(state.config || {}).length} campos`,
`**Metadata:** ${Object.keys(state.metadata || {}).length} campos`
].join('\n');
const instructionsText = [
'**🎮 Instrucciones:**',
'• **Base**: Configura nombre y tipo',
'• **Config (JSON)**: Configuración técnica',
'• **Meta (JSON)**: Metadatos adicionales',
'• **Guardar**: Confirma los cambios',
'• **Cancelar**: Descarta los cambios'
].join('\n');
return { return {
type: 17, type: 17,
accent_color: 0x00FF00, accent_color: 0x00FF00,
components: [ components: [
{ {
type: 9, type: 10,
components: [{ content: `# 🗺️ ${title}: \`${state.key}\``
type: 10,
content: `🗺️ **${title}: \`${state.key}\`**`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: statusText
type: 10,
content: `**📋 Estado Actual:**\n` +
`**Nombre:** ${state.name || '❌ No configurado'}\n` +
`**Tipo:** ${state.type || '❌ No configurado'}\n` +
`**Config:** ${Object.keys(state.config || {}).length} campos\n` +
`**Metadata:** ${Object.keys(state.metadata || {}).length} campos`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: instructionsText
type: 10,
content: `**🎮 Instrucciones:**\n` +
`• **Base**: Configura nombre y tipo\n` +
`• **Config (JSON)**: Configuración técnica\n` +
`• **Meta (JSON)**: Metadatos adicionales\n` +
`• **Guardar**: Confirma los cambios\n` +
`• **Cancelar**: Descarta los cambios`
}]
} }
] ]
}; };
} }
const buildEditorComponents = (state: AreaState, editing: boolean = false) => [
buildAreaDisplay(state, editing),
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
];
export const command: CommandMessage = { export const command: CommandMessage = {
name: 'area-editar', name: 'area-editar',
type: 'message', type: 'message',
@@ -67,24 +80,17 @@ export const command: CommandMessage = {
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma); const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
if (!allowed) { if (!allowed) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
type: 10,
content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -92,24 +98,17 @@ export const command: CommandMessage = {
const key = args[0]?.trim(); const key = args[0]?.trim();
if (!key) { if (!key) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFFA500, accent_color: 0xFFA500,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-editar <key-única>`'
type: 10,
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-editar <key-única>`'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -118,47 +117,28 @@ export const command: CommandMessage = {
const area = await prisma.gameArea.findFirst({ where: { key, guildId } }); const area = await prisma.gameArea.findFirst({ where: { key, guildId } });
if (!area) { if (!area) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '❌ **Área No Encontrada**\n└ No existe un área con esa key en este servidor.'
type: 10,
content: '❌ **Área No Encontrada**\n└ No existe un área con esa key en este servidor.'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
const state: AreaState = { key, name: area.name, type: area.type, config: area.config ?? {}, metadata: area.metadata ?? {} }; const state: AreaState = { key, name: area.name, type: area.type, config: area.config ?? {}, metadata: area.metadata ?? {} };
const display = createAreaDisplay(state, true);
const editorMsg = await (channel.send as any)({ const editorMsg = await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [ components: buildEditorComponents(state, true),
display, reply: { messageReference: message.id }
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
]
}); });
const collector = editorMsg.createMessageComponentCollector({ time: 30*60_000, filter: (i)=> i.user.id === message.author.id }); const collector = editorMsg.createMessageComponentCollector({ time: 30*60_000, filter: (i)=> i.user.id === message.author.id });
@@ -169,16 +149,14 @@ export const command: CommandMessage = {
case 'ga_cancel': case 'ga_cancel':
await i.deferUpdate(); await i.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '**❌ Editor de Área cancelado.**'
type: 10,
content: '**❌ Editor de Área cancelado.**'
}]
}] }]
}] }]
}); });
@@ -198,16 +176,14 @@ export const command: CommandMessage = {
await prisma.gameArea.update({ where: { id: area.id }, data: { name: state.name!, type: state.type!, config: state.config ?? {}, metadata: state.metadata ?? {} } }); await prisma.gameArea.update({ where: { id: area.id }, data: { name: state.name!, type: state.type!, config: state.config ?? {}, metadata: state.metadata ?? {} } });
await i.reply({ content: '✅ Área actualizada.', flags: MessageFlags.Ephemeral }); await i.reply({ content: '✅ Área actualizada.', flags: MessageFlags.Ephemeral });
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0x00FF00, accent_color: 0x00FF00,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: `**✅ Área \`${state.key}\` actualizada exitosamente.**`
type: 10,
content: `**✅ Área \`${state.key}\` actualizada exitosamente.**`
}]
}] }]
}] }]
}); });
@@ -223,16 +199,14 @@ export const command: CommandMessage = {
if (r==='time') { if (r==='time') {
try { try {
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFFA500, accent_color: 0xFFA500,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '**⏰ Editor expirado.**'
type: 10,
content: '**⏰ Editor expirado.**'
}]
}] }]
}] }]
}); });
@@ -255,22 +229,10 @@ async function showBaseModal(i: ButtonInteraction, state: AreaState, editorMsg:
await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral }); await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral });
// Actualizar display // Actualizar display
const newDisplay = createAreaDisplay(state, editing);
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [ components: buildEditorComponents(state, editing)
newDisplay,
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
]
}); });
} catch {} } catch {}
} }
@@ -298,22 +260,10 @@ async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'con
} }
// Actualizar display // Actualizar display
const newDisplay = createAreaDisplay(state, editing);
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [ components: buildEditorComponents(state, editing)
newDisplay,
{
type: 1,
components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'ga_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Config (JSON)', custom_id: 'ga_config' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Meta (JSON)', custom_id: 'ga_meta' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'ga_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'ga_cancel' },
]
}
]
}); });
} catch {} } catch {}
} }

View File

@@ -30,24 +30,17 @@ export const command: CommandMessage = {
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, client.prisma); const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, client.prisma);
if (!allowed) { if (!allowed) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
type: 10,
content: '❌ **Error de Permisos**\n└ No tienes permisos de ManageGuild ni rol de staff.'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -55,24 +48,17 @@ export const command: CommandMessage = {
const key = args[0]?.trim(); const key = args[0]?.trim();
if (!key) { if (!key) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFFA500, accent_color: 0xFFA500,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '⚠️ **Uso Incorrecto**\n└ Uso: `!item-editar <key-única>`'
type: 10,
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!item-editar <key-única>`'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -82,24 +68,17 @@ export const command: CommandMessage = {
const existing = await client.prisma.economyItem.findFirst({ where: { key, guildId } }); const existing = await client.prisma.economyItem.findFirst({ where: { key, guildId } });
if (!existing) { if (!existing) {
await (channel.send as any)({ await (channel.send as any)({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '❌ **Item No Encontrado**\n└ No existe un item con esa key en este servidor.'
type: 10,
content: '❌ **Item No Encontrado**\n└ No existe un item con esa key en este servidor.'
}]
}] }]
}], }],
message_reference: { reply: { messageReference: message.id }
message_id: message.id,
channel_id: message.channel.id,
guild_id: message.guild!.id,
fail_if_not_exists: false
}
}); });
return; return;
} }
@@ -116,64 +95,65 @@ export const command: CommandMessage = {
props: existing.props || {}, props: existing.props || {},
}; };
// Función para crear display const buildEditorDisplay = () => {
const createDisplay = () => ({ const baseInfo = [
display: { `**Nombre:** ${state.name || '*Sin definir*'}`,
`**Descripción:** ${state.description || '*Sin definir*'}`,
`**Categoría:** ${state.category || '*Sin definir*'}`,
`**Icon URL:** ${state.icon || '*Sin definir*'}`,
`**Stackable:** ${state.stackable ? 'Sí' : 'No'}`,
`**Máx. Inventario:** ${state.maxPerInventory ?? 'Ilimitado'}`,
].join('\n');
const tagsInfo = `**Tags:** ${state.tags.length > 0 ? state.tags.join(', ') : '*Ninguno*'}`;
const propsJson = JSON.stringify(state.props ?? {}, null, 2);
return {
type: 17, type: 17,
accent_color: 0x00D9FF, accent_color: 0x00D9FF,
components: [ components: [
{ {
type: 9, type: 10,
components: [{ content: `# 🛠️ Editando Item: \`${key}\``
type: 10,
content: `**🛠️ Editando Item: \`${key}\`**`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: baseInfo
type: 10,
content: `**Nombre:** ${state.name || '*Sin definir*'}\n` +
`**Descripción:** ${state.description || '*Sin definir*'}\n` +
`**Categoría:** ${state.category || '*Sin definir*'}\n` +
`**Icon URL:** ${state.icon || '*Sin definir*'}\n` +
`**Stackable:** ${state.stackable ? 'Sí' : 'No'}\n` +
`**Máx. Inventario:** ${state.maxPerInventory || 'Ilimitado'}`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: tagsInfo
type: 10,
content: `**Tags:** ${state.tags.length > 0 ? state.tags.join(', ') : '*Ninguno*'}`
}]
}, },
{ type: 14, divider: true }, { type: 14, divider: true },
{ {
type: 9, type: 10,
components: [{ content: `**Props (JSON):**\n\`\`\`json\n${propsJson}\n\`\`\``
type: 10,
content: `**Props (JSON):**\n\`\`\`json\n${JSON.stringify(state.props, null, 2)}\n\`\`\``
}]
} }
] ]
} };
}); };
const editorMsg = await (channel.send as any)({ const buildEditorComponents = () => [
flags: 32768, buildEditorDisplay(),
components: [ {
createDisplay().display, type: 1,
{ type: 1, components: [ components: [
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'it_base' }, { type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'it_base' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Tags', custom_id: 'it_tags' }, { type: 2, style: ButtonStyle.Secondary, label: 'Tags', custom_id: 'it_tags' },
{ type: 2, style: ButtonStyle.Secondary, label: 'Props (JSON)', custom_id: 'it_props' }, { type: 2, style: ButtonStyle.Secondary, label: 'Props (JSON)', custom_id: 'it_props' },
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'it_save' }, { type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'it_save' },
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'it_cancel' }, { type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'it_cancel' },
]}, ]
], }
];
const editorMsg = await (channel.send as any)({
content: null,
flags: 32768,
components: buildEditorComponents(),
reply: { messageReference: message.id }
}); });
const collector = editorMsg.createMessageComponentCollector({ time: 30 * 60_000, filter: (i) => i.user.id === message.author.id }); const collector = editorMsg.createMessageComponentCollector({ time: 30 * 60_000, filter: (i) => i.user.id === message.author.id });
@@ -184,16 +164,14 @@ export const command: CommandMessage = {
if (i.customId === 'it_cancel') { if (i.customId === 'it_cancel') {
await i.deferUpdate(); await i.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFF0000, accent_color: 0xFF0000,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '**❌ Editor cancelado.**'
type: 10,
content: '**❌ Editor cancelado.**'
}]
}] }]
}] }]
}); });
@@ -201,15 +179,15 @@ export const command: CommandMessage = {
return; return;
} }
if (i.customId === 'it_base') { if (i.customId === 'it_base') {
await showBaseModal(i as ButtonInteraction, state, editorMsg, createDisplay); await showBaseModal(i as ButtonInteraction, state, editorMsg, buildEditorComponents);
return; return;
} }
if (i.customId === 'it_tags') { if (i.customId === 'it_tags') {
await showTagsModal(i as ButtonInteraction, state, editorMsg, createDisplay); await showTagsModal(i as ButtonInteraction, state, editorMsg, buildEditorComponents);
return; return;
} }
if (i.customId === 'it_props') { if (i.customId === 'it_props') {
await showPropsModal(i as ButtonInteraction, state, editorMsg, createDisplay); await showPropsModal(i as ButtonInteraction, state, editorMsg, buildEditorComponents);
return; return;
} }
if (i.customId === 'it_save') { if (i.customId === 'it_save') {
@@ -234,16 +212,14 @@ export const command: CommandMessage = {
}); });
await i.reply({ content: '✅ Item actualizado!', flags: MessageFlags.Ephemeral }); await i.reply({ content: '✅ Item actualizado!', flags: MessageFlags.Ephemeral });
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0x00FF00, accent_color: 0x00FF00,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: `✅ **Item Actualizado**\n└ Item \`${state.key}\` actualizado exitosamente.`
type: 10,
content: `✅ **Item Actualizado**\n└ Item \`${state.key}\` actualizado exitosamente.`
}]
}] }]
}] }]
}); });
@@ -259,16 +235,14 @@ export const command: CommandMessage = {
collector.on('end', async (_c, r) => { collector.on('end', async (_c, r) => {
if (r === 'time') { if (r === 'time') {
try { await editorMsg.edit({ try { await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [{ components: [{
type: 17, type: 17,
accent_color: 0xFFA500, accent_color: 0xFFA500,
components: [{ components: [{
type: 9, type: 10,
components: [{ content: '**⏰ Editor expirado.**'
type: 10,
content: '**⏰ Editor expirado.**'
}]
}] }]
}] }]
}); } catch {} }); } catch {}
@@ -277,7 +251,7 @@ export const command: CommandMessage = {
}, },
}; };
async function showBaseModal(i: ButtonInteraction, state: ItemEditorState, editorMsg: any, createDisplay: Function) { async function showBaseModal(i: ButtonInteraction, state: ItemEditorState, editorMsg: any, buildComponents: () => any[]) {
const modal = { const modal = {
title: 'Configuración base del Item', title: 'Configuración base del Item',
customId: 'it_base_modal', customId: 'it_base_modal',
@@ -313,13 +287,14 @@ async function showBaseModal(i: ButtonInteraction, state: ItemEditorState, edito
await sub.deferUpdate(); await sub.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [createDisplay().display] components: buildComponents()
}); });
} catch {} } catch {}
} }
async function showTagsModal(i: ButtonInteraction, state: ItemEditorState, editorMsg: any, createDisplay: Function) { async function showTagsModal(i: ButtonInteraction, state: ItemEditorState, editorMsg: any, buildComponents: () => any[]) {
const modal = { const modal = {
title: 'Tags del Item (separados por coma)', title: 'Tags del Item (separados por coma)',
customId: 'it_tags_modal', customId: 'it_tags_modal',
@@ -334,13 +309,14 @@ async function showTagsModal(i: ButtonInteraction, state: ItemEditorState, edito
state.tags = tags ? tags.split(',').map((t) => t.trim()).filter(Boolean) : []; state.tags = tags ? tags.split(',').map((t) => t.trim()).filter(Boolean) : [];
await sub.deferUpdate(); await sub.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [createDisplay().display] components: buildComponents()
}); });
} catch {} } catch {}
} }
async function showPropsModal(i: ButtonInteraction, state: ItemEditorState, editorMsg: any, createDisplay: Function) { async function showPropsModal(i: ButtonInteraction, state: ItemEditorState, editorMsg: any, buildComponents: () => any[]) {
const template = state.props && Object.keys(state.props).length ? JSON.stringify(state.props) : JSON.stringify({ const template = state.props && Object.keys(state.props).length ? JSON.stringify(state.props) : JSON.stringify({
tool: undefined, tool: undefined,
breakable: undefined, breakable: undefined,
@@ -371,8 +347,9 @@ async function showPropsModal(i: ButtonInteraction, state: ItemEditorState, edit
state.props = parsed; state.props = parsed;
await sub.deferUpdate(); await sub.deferUpdate();
await editorMsg.edit({ await editorMsg.edit({
content: null,
flags: 32768, flags: 32768,
components: [createDisplay().display] components: buildComponents()
}); });
} catch (e) { } catch (e) {
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral }); await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
@@ -380,6 +357,13 @@ async function showPropsModal(i: ButtonInteraction, state: ItemEditorState, edit
} else { } else {
state.props = {}; state.props = {};
await sub.reply({ content: ' Props limpiados.', flags: MessageFlags.Ephemeral }); await sub.reply({ content: ' Props limpiados.', flags: MessageFlags.Ephemeral });
try {
await editorMsg.edit({
content: null,
flags: 32768,
components: buildComponents()
});
} catch {}
} }
} catch {} } catch {}
} }