feat(economy): enhance area and mob commands with improved display and error handling
This commit is contained in:
@@ -1,84 +0,0 @@
|
||||
import { Message, AttachmentBuilder } from 'discord.js';
|
||||
import { aiService } from '../../../core/services/AIService';
|
||||
import logger from '../../../core/lib/logger';
|
||||
|
||||
export default {
|
||||
name: 'image',
|
||||
aliases: ['imagen', 'img', 'aiimage'],
|
||||
description: 'Genera una imagen usando IA',
|
||||
cooldown: 10,
|
||||
async run(message: Message, args: string[]) {
|
||||
// Verificar que hay un prompt
|
||||
if (!args || args.length === 0) {
|
||||
await message.reply('❌ **Error**: Debes proporcionar una descripción para generar la imagen.\n\n**Ejemplo**: `!image un gato espacial flotando entre estrellas`');
|
||||
return;
|
||||
}
|
||||
|
||||
const prompt = args.join(' ').trim();
|
||||
|
||||
// Validar longitud del prompt
|
||||
if (prompt.length < 3) {
|
||||
await message.reply('❌ **Error**: La descripción debe tener al menos 3 caracteres.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (prompt.length > 1000) {
|
||||
await message.reply('❌ **Error**: La descripción es demasiado larga (máximo 1000 caracteres).');
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar mensaje de "generando..."
|
||||
const thinkingMessage = await message.reply('🎨 **Generando imagen**... Esto puede tomar unos momentos.');
|
||||
|
||||
try {
|
||||
logger.info(`Generando imagen para usuario ${message.author.id}: ${prompt.slice(0, 100)}`);
|
||||
|
||||
// Generar la imagen usando el AIService actualizado
|
||||
const result = await aiService.generateImage(prompt, {
|
||||
size: 'square', // Por defecto usar formato cuadrado
|
||||
mimeType: 'image/jpeg',
|
||||
numberOfImages: 1,
|
||||
personGeneration: true
|
||||
});
|
||||
|
||||
// Crear attachment para Discord
|
||||
const attachment = new AttachmentBuilder(result.data, {
|
||||
name: result.fileName,
|
||||
description: `Imagen generada: ${prompt.slice(0, 100)}${prompt.length > 100 ? '...' : ''}`
|
||||
});
|
||||
|
||||
// Responder con la imagen
|
||||
await thinkingMessage.edit({
|
||||
content: `✅ **Imagen generada** para: *${prompt.slice(0, 150)}${prompt.length > 150 ? '...' : ''}*`,
|
||||
files: [attachment]
|
||||
});
|
||||
|
||||
logger.info(`Imagen generada exitosamente para usuario ${message.author.id}, tamaño: ${result.data.length} bytes`);
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Error generando imagen para usuario ${message.author.id}: ${error}`);
|
||||
|
||||
let errorMessage = '❌ **Error generando imagen**: ';
|
||||
|
||||
if (error instanceof Error) {
|
||||
const errorText = error.message.toLowerCase();
|
||||
|
||||
if (errorText.includes('no está disponible') || errorText.includes('not found')) {
|
||||
errorMessage += 'El servicio de generación de imágenes no está disponible en este momento.';
|
||||
} else if (errorText.includes('límite') || errorText.includes('quota')) {
|
||||
errorMessage += 'Se ha alcanzado el límite de generación de imágenes. Intenta más tarde.';
|
||||
} else if (errorText.includes('bloqueado') || errorText.includes('safety')) {
|
||||
errorMessage += 'Tu descripción fue bloqueada por las políticas de seguridad. Intenta con algo diferente.';
|
||||
} else if (errorText.includes('inicializado') || errorText.includes('api')) {
|
||||
errorMessage += 'El servicio no está configurado correctamente.';
|
||||
} else {
|
||||
errorMessage += error.message;
|
||||
}
|
||||
} else {
|
||||
errorMessage += 'Error desconocido. Intenta de nuevo más tarde.';
|
||||
}
|
||||
|
||||
await thinkingMessage.edit(errorMessage);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { CommandMessage } from '../../../core/types/commands';
|
||||
import type Amayo from '../../../core/client';
|
||||
import { hasManageGuildOrStaff } from '../../../core/lib/permissions';
|
||||
import { prisma } from '../../../core/database/prisma';
|
||||
import type { TextBasedChannel } from 'discord.js';
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'area-eliminar',
|
||||
@@ -13,7 +14,27 @@ export const command: CommandMessage = {
|
||||
run: async (message, args, client: Amayo) => {
|
||||
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
|
||||
if (!allowed) {
|
||||
await message.reply('❌ No tienes permisos de ManageGuild ni rol de staff.');
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -21,7 +42,27 @@ export const command: CommandMessage = {
|
||||
const key = args[0]?.trim();
|
||||
|
||||
if (!key) {
|
||||
await message.reply('Uso: \`!area-eliminar <key>\`\nEjemplo: \`!area-eliminar mine.cavern\`');
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-eliminar <key>`\n└ Ejemplo: `!area-eliminar mine.cavern`'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,7 +71,27 @@ export const command: CommandMessage = {
|
||||
});
|
||||
|
||||
if (!area) {
|
||||
await message.reply(`❌ No se encontró el área local con key ${key} en este servidor.`);
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `❌ **Área No Encontrada**\n└ No se encontró el área local con key \`${key}\` en este servidor.`
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,9 +109,38 @@ export const command: CommandMessage = {
|
||||
where: { id: area.id }
|
||||
});
|
||||
|
||||
await message.reply(
|
||||
`✅ Área ${key} eliminada exitosamente.\n` +
|
||||
`${levelsCount > 0 ? `⚠️ Se eliminaron ${levelsCount} nivel(es) asociado(s).` : ''}`
|
||||
);
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `✅ **Área Eliminada Exitosamente**\n└ Key: \`${key}\``
|
||||
}]
|
||||
},
|
||||
...(levelsCount > 0 ? [{
|
||||
type: 14,
|
||||
divider: true
|
||||
}, {
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,48 @@ interface AreaState {
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
function createAreaDisplay(state: AreaState, editing: boolean = false) {
|
||||
const title = editing ? 'Editando Área' : 'Creando Área';
|
||||
return {
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `🗺️ **${title}: \`${state.key}\`**`
|
||||
}]
|
||||
},
|
||||
{ type: 14, divider: true },
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
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: 9,
|
||||
components: [{
|
||||
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`
|
||||
}]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'area-crear',
|
||||
type: 'message',
|
||||
@@ -21,28 +63,102 @@ export const command: CommandMessage = {
|
||||
description: 'Crea una GameArea (mina/laguna/arena/farm) para este servidor con editor.',
|
||||
usage: 'area-crear <key-única>',
|
||||
run: async (message, args, _client: Amayo) => {
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
|
||||
if (!allowed) { await message.reply('❌ No tienes permisos de ManageGuild ni rol de staff.'); return; }
|
||||
if (!allowed) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
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;
|
||||
}
|
||||
|
||||
const key = args[0]?.trim();
|
||||
if (!key) { await message.reply('Uso: `!area-crear <key-única>`'); return; }
|
||||
if (!key) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-crear <key-única>`'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const guildId = message.guild!.id;
|
||||
const exists = await prisma.gameArea.findFirst({ where: { key, guildId } });
|
||||
if (exists) { await message.reply('❌ Ya existe un área con esa key en este servidor.'); return; }
|
||||
if (exists) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '❌ **Área Ya Existe**\n└ Ya existe un área con esa key en este servidor.'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const state: AreaState = { key, config: {}, metadata: {} };
|
||||
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const editorMsg = await channel.send({
|
||||
content: `🗺️ Editor de Área: \`${key}\``,
|
||||
components: [ { type: 1, components: [
|
||||
const display = createAreaDisplay(state, false);
|
||||
|
||||
const editorMsg = await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [
|
||||
display,
|
||||
{
|
||||
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 });
|
||||
@@ -52,23 +168,49 @@ export const command: CommandMessage = {
|
||||
switch (i.customId) {
|
||||
case 'ga_cancel':
|
||||
await i.deferUpdate();
|
||||
await editorMsg.edit({ content: '❌ Editor de Área cancelado.', components: [] });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '**❌ Editor de Área cancelado.**'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('cancel');
|
||||
return;
|
||||
case 'ga_base':
|
||||
await showBaseModal(i as ButtonInteraction, state);
|
||||
await showBaseModal(i as ButtonInteraction, state, editorMsg, false);
|
||||
return;
|
||||
case 'ga_config':
|
||||
await showJsonModal(i as ButtonInteraction, state, 'config', 'Config del Área');
|
||||
await showJsonModal(i as ButtonInteraction, state, 'config', 'Config del Área', editorMsg, false);
|
||||
return;
|
||||
case 'ga_meta':
|
||||
await showJsonModal(i as ButtonInteraction, state, 'metadata', 'Meta del Área');
|
||||
await showJsonModal(i as ButtonInteraction, state, 'metadata', 'Meta del Área', editorMsg, false);
|
||||
return;
|
||||
case 'ga_save':
|
||||
if (!state.name || !state.type) { await i.reply({ content: '❌ Completa Base (nombre/tipo).', flags: MessageFlags.Ephemeral }); return; }
|
||||
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 editorMsg.edit({ content: `✅ Área \`${state.key}\` creada.`, components: [] });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**✅ Área \`${state.key}\` creada exitosamente.**`
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('saved');
|
||||
return;
|
||||
}
|
||||
@@ -77,25 +219,102 @@ export const command: CommandMessage = {
|
||||
}
|
||||
});
|
||||
|
||||
collector.on('end', async (_c,r)=> { if (r==='time') { try { await editorMsg.edit({ content: '⏰ Editor expirado.', components: [] }); } catch {} } });
|
||||
collector.on('end', async (_c,r)=> {
|
||||
if (r==='time') {
|
||||
try {
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '**⏰ Editor expirado.**'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async function showBaseModal(i: ButtonInteraction, state: AreaState) {
|
||||
async function showBaseModal(i: ButtonInteraction, state: AreaState, editorMsg: Message, editing: boolean) {
|
||||
const modal = { title: 'Base del Área', customId: 'ga_base_modal', components: [
|
||||
{ type: ComponentType.Label, label: 'Nombre', component: { type: ComponentType.TextInput, customId: 'name', style: TextInputStyle.Short, required: true, value: state.name ?? '' } },
|
||||
{ type: ComponentType.Label, label: 'Tipo (MINE/LAGOON/FIGHT/FARM)', component: { type: ComponentType.TextInput, customId: 'type', style: TextInputStyle.Short, required: true, value: state.type ?? '' } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try { const sub = await i.awaitModalSubmit({ time: 300_000 }); state.name = sub.components.getTextInputValue('name').trim(); state.type = sub.components.getTextInputValue('type').trim().toUpperCase(); await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral }); } catch {}
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
state.name = sub.components.getTextInputValue('name').trim();
|
||||
state.type = sub.components.getTextInputValue('type').trim().toUpperCase();
|
||||
await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral });
|
||||
|
||||
// Actualizar display
|
||||
const newDisplay = createAreaDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
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 {}
|
||||
}
|
||||
|
||||
async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'config'|'metadata', title: string) {
|
||||
async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'config'|'metadata', title: string, editorMsg: Message, editing: boolean) {
|
||||
const current = JSON.stringify(state[field] ?? {});
|
||||
const modal = { title, customId: `ga_json_${field}`, components: [
|
||||
{ type: ComponentType.Label, label: 'JSON', component: { type: ComponentType.TextInput, customId: 'json', style: TextInputStyle.Paragraph, required: false, value: current.slice(0,4000) } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try { const sub = await i.awaitModalSubmit({ time: 300_000 }); const raw = sub.components.getTextInputValue('json'); if (raw) { try { state[field] = JSON.parse(raw); await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral }); } catch { await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral }); } } else { state[field] = {}; await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral }); } } catch {}
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
const raw = sub.components.getTextInputValue('json');
|
||||
if (raw) {
|
||||
try {
|
||||
state[field] = JSON.parse(raw);
|
||||
await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral });
|
||||
} catch {
|
||||
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state[field] = {};
|
||||
await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
// Actualizar display
|
||||
const newDisplay = createAreaDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
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 {}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,48 @@ interface AreaState {
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
function createAreaDisplay(state: AreaState, editing: boolean = false) {
|
||||
const title = editing ? 'Editando Área' : 'Creando Área';
|
||||
return {
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `🗺️ **${title}: \`${state.key}\`**`
|
||||
}]
|
||||
},
|
||||
{ type: 14, divider: true },
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
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: 9,
|
||||
components: [{
|
||||
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`
|
||||
}]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'area-editar',
|
||||
type: 'message',
|
||||
@@ -21,28 +63,102 @@ export const command: CommandMessage = {
|
||||
description: 'Edita una GameArea de este servidor con un editor interactivo.',
|
||||
usage: 'area-editar <key-única>',
|
||||
run: async (message, args, _client: Amayo) => {
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
|
||||
if (!allowed) { await message.reply('❌ No tienes permisos de ManageGuild ni rol de staff.'); return; }
|
||||
if (!allowed) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
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;
|
||||
}
|
||||
|
||||
const key = args[0]?.trim();
|
||||
if (!key) { await message.reply('Uso: `!area-editar <key-única>`'); return; }
|
||||
if (!key) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!area-editar <key-única>`'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const guildId = message.guild!.id;
|
||||
const area = await prisma.gameArea.findFirst({ where: { key, guildId } });
|
||||
if (!area) { await message.reply('❌ No existe un área con esa key en este servidor.'); return; }
|
||||
if (!area) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '❌ **Área No Encontrada**\n└ No existe un área con esa key en este servidor.'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const state: AreaState = { key, name: area.name, type: area.type, config: area.config ?? {}, metadata: area.metadata ?? {} };
|
||||
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const editorMsg = await channel.send({
|
||||
content: `🗺️ Editor de Área (editar): \`${key}\``,
|
||||
components: [ { type: 1, components: [
|
||||
const display = createAreaDisplay(state, true);
|
||||
|
||||
const editorMsg = await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [
|
||||
display,
|
||||
{
|
||||
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 });
|
||||
@@ -52,23 +168,49 @@ export const command: CommandMessage = {
|
||||
switch (i.customId) {
|
||||
case 'ga_cancel':
|
||||
await i.deferUpdate();
|
||||
await editorMsg.edit({ content: '❌ Editor de Área cancelado.', components: [] });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '**❌ Editor de Área cancelado.**'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('cancel');
|
||||
return;
|
||||
case 'ga_base':
|
||||
await showBaseModal(i as ButtonInteraction, state);
|
||||
await showBaseModal(i as ButtonInteraction, state, editorMsg, true);
|
||||
return;
|
||||
case 'ga_config':
|
||||
await showJsonModal(i as ButtonInteraction, state, 'config', 'Config del Área');
|
||||
await showJsonModal(i as ButtonInteraction, state, 'config', 'Config del Área', editorMsg, true);
|
||||
return;
|
||||
case 'ga_meta':
|
||||
await showJsonModal(i as ButtonInteraction, state, 'metadata', 'Meta del Área');
|
||||
await showJsonModal(i as ButtonInteraction, state, 'metadata', 'Meta del Área', editorMsg, true);
|
||||
return;
|
||||
case 'ga_save':
|
||||
if (!state.name || !state.type) { await i.reply({ content: '❌ Completa Base (nombre/tipo).', flags: MessageFlags.Ephemeral }); return; }
|
||||
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 editorMsg.edit({ content: `✅ Área \`${state.key}\` actualizada.`, components: [] });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**✅ Área \`${state.key}\` actualizada exitosamente.**`
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('saved');
|
||||
return;
|
||||
}
|
||||
@@ -77,25 +219,102 @@ export const command: CommandMessage = {
|
||||
}
|
||||
});
|
||||
|
||||
collector.on('end', async (_c,r)=> { if (r==='time') { try { await editorMsg.edit({ content: '⏰ Editor expirado.', components: [] }); } catch {} } });
|
||||
collector.on('end', async (_c,r)=> {
|
||||
if (r==='time') {
|
||||
try {
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '**⏰ Editor expirado.**'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async function showBaseModal(i: ButtonInteraction, state: AreaState) {
|
||||
async function showBaseModal(i: ButtonInteraction, state: AreaState, editorMsg: Message, editing: boolean) {
|
||||
const modal = { title: 'Base del Área', customId: 'ga_base_modal', components: [
|
||||
{ type: ComponentType.Label, label: 'Nombre', component: { type: ComponentType.TextInput, customId: 'name', style: TextInputStyle.Short, required: true, value: state.name ?? '' } },
|
||||
{ type: ComponentType.Label, label: 'Tipo (MINE/LAGOON/FIGHT/FARM)', component: { type: ComponentType.TextInput, customId: 'type', style: TextInputStyle.Short, required: true, value: state.type ?? '' } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try { const sub = await i.awaitModalSubmit({ time: 300_000 }); state.name = sub.components.getTextInputValue('name').trim(); state.type = sub.components.getTextInputValue('type').trim().toUpperCase(); await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral }); } catch {}
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
state.name = sub.components.getTextInputValue('name').trim();
|
||||
state.type = sub.components.getTextInputValue('type').trim().toUpperCase();
|
||||
await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral });
|
||||
|
||||
// Actualizar display
|
||||
const newDisplay = createAreaDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
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 {}
|
||||
}
|
||||
|
||||
async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'config'|'metadata', title: string) {
|
||||
async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'config'|'metadata', title: string, editorMsg: Message, editing: boolean) {
|
||||
const current = JSON.stringify(state[field] ?? {});
|
||||
const modal = { title, customId: `ga_json_${field}`, components: [
|
||||
{ type: ComponentType.Label, label: 'JSON', component: { type: ComponentType.TextInput, customId: 'json', style: TextInputStyle.Paragraph, required: false, value: current.slice(0,4000) } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try { const sub = await i.awaitModalSubmit({ time: 300_000 }); const raw = sub.components.getTextInputValue('json'); if (raw) { try { state[field] = JSON.parse(raw); await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral }); } catch { await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral }); } } else { state[field] = {}; await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral }); } } catch {}
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
const raw = sub.components.getTextInputValue('json');
|
||||
if (raw) {
|
||||
try {
|
||||
state[field] = JSON.parse(raw);
|
||||
await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral });
|
||||
} catch {
|
||||
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state[field] = {};
|
||||
await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
// Actualizar display
|
||||
const newDisplay = createAreaDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
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 {}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,54 @@ export const command: CommandMessage = {
|
||||
category: 'Economía',
|
||||
usage: 'item-crear <key-única>',
|
||||
run: async (message: Message, args: string[], client: Amayo) => {
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, client.prisma);
|
||||
if (!allowed) {
|
||||
await message.reply('❌ No tienes permisos de ManageGuild ni rol de staff.');
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
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;
|
||||
}
|
||||
|
||||
const key = args[0]?.trim();
|
||||
if (!key) {
|
||||
await message.reply('Uso: `!item-crear <key-única>`');
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!item-crear <key-única>`'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,7 +81,26 @@ export const command: CommandMessage = {
|
||||
|
||||
const exists = await client.prisma.economyItem.findFirst({ where: { key, guildId } });
|
||||
if (exists) {
|
||||
await message.reply('❌ Ya existe un item con esa key en este servidor.');
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '❌ **Item Ya Existe**\n└ Ya existe un item con esa key en este servidor.'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,10 +158,10 @@ export const command: CommandMessage = {
|
||||
}
|
||||
});
|
||||
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const editorMsg = await channel.send({
|
||||
...createDisplay(),
|
||||
const editorMsg = await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [
|
||||
createDisplay().display,
|
||||
{ type: 1, components: [
|
||||
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'it_base' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Tags', custom_id: 'it_tags' },
|
||||
@@ -172,7 +230,20 @@ export const command: CommandMessage = {
|
||||
},
|
||||
});
|
||||
await i.reply({ content: '✅ Item guardado!', flags: MessageFlags.Ephemeral });
|
||||
await editorMsg.edit({ content: `✅ Item \`${state.key}\` creado.`, components: [], display: undefined });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `✅ **Item Creado**\n└ Item \`${state.key}\` creado exitosamente.`
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('saved');
|
||||
return;
|
||||
}
|
||||
@@ -238,7 +309,10 @@ async function showBaseModal(i: ButtonInteraction, state: ItemEditorState, edito
|
||||
}
|
||||
|
||||
await sub.deferUpdate();
|
||||
await editorMsg.edit(createDisplay());
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [createDisplay().display]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -256,7 +330,10 @@ async function showTagsModal(i: ButtonInteraction, state: ItemEditorState, edito
|
||||
const tags = sub.components.getTextInputValue('tags');
|
||||
state.tags = tags ? tags.split(',').map((t) => t.trim()).filter(Boolean) : [];
|
||||
await sub.deferUpdate();
|
||||
await editorMsg.edit(createDisplay());
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [createDisplay().display]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -289,7 +366,11 @@ async function showPropsModal(i: ButtonInteraction, state: ItemEditorState, edit
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
state.props = parsed;
|
||||
await sub.deferUpdate(); await editorMsg.edit(createDisplay());
|
||||
await sub.deferUpdate();
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [createDisplay().display]
|
||||
});
|
||||
} catch (e) {
|
||||
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
@@ -20,38 +20,100 @@ interface ItemEditorState {
|
||||
export const command: CommandMessage = {
|
||||
name: 'item-editar',
|
||||
type: 'message',
|
||||
aliases: ['crear-item','itemcreate'],
|
||||
aliases: ['editar-item','itemedit'],
|
||||
cooldown: 10,
|
||||
description: 'Crea un EconomyItem para este servidor con un pequeño editor interactivo.',
|
||||
description: 'Edita un EconomyItem existente del servidor con un pequeño editor interactivo.',
|
||||
category: 'Economía',
|
||||
usage: 'item-editar <key-única>',
|
||||
run: async (message: Message, args: string[], client: Amayo) => {
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, client.prisma);
|
||||
if (!allowed) {
|
||||
await message.reply('❌ No tienes permisos de ManageGuild ni rol de staff.');
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
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;
|
||||
}
|
||||
|
||||
const key = args[0]?.trim();
|
||||
if (!key) {
|
||||
await message.reply('Uso: `!item-editar <key-única>`');
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFFA500,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '⚠️ **Uso Incorrecto**\n└ Uso: `!item-editar <key-única>`'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const guildId = message.guild!.id;
|
||||
|
||||
const exists = await client.prisma.economyItem.findFirst({ where: { key, guildId } });
|
||||
if (exists) {
|
||||
await message.reply('❌ Ya existe un item con esa key en este servidor.');
|
||||
const existing = await client.prisma.economyItem.findFirst({ where: { key, guildId } });
|
||||
if (!existing) {
|
||||
await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '❌ **Item No Encontrado**\n└ No existe un item con esa key en este servidor.'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
guild_id: message.guild!.id,
|
||||
fail_if_not_exists: false
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const state: ItemEditorState = {
|
||||
key,
|
||||
tags: [],
|
||||
stackable: true,
|
||||
maxPerInventory: null,
|
||||
props: {},
|
||||
name: existing.name,
|
||||
description: existing.description || undefined,
|
||||
category: existing.category || undefined,
|
||||
icon: existing.icon || undefined,
|
||||
stackable: existing.stackable ?? true,
|
||||
maxPerInventory: existing.maxPerInventory || null,
|
||||
tags: existing.tags || [],
|
||||
props: existing.props || {},
|
||||
};
|
||||
|
||||
// Función para crear display
|
||||
@@ -100,10 +162,10 @@ export const command: CommandMessage = {
|
||||
}
|
||||
});
|
||||
|
||||
const channel = message.channel as TextBasedChannel & { send: Function };
|
||||
const editorMsg = await channel.send({
|
||||
...createDisplay(),
|
||||
const editorMsg = await (channel.send as any)({
|
||||
flags: 32768,
|
||||
components: [
|
||||
createDisplay().display,
|
||||
{ type: 1, components: [
|
||||
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'it_base' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Tags', custom_id: 'it_tags' },
|
||||
@@ -156,11 +218,10 @@ export const command: CommandMessage = {
|
||||
await i.reply({ content: '❌ Falta el nombre del item (configura en Base).', flags: MessageFlags.Ephemeral });
|
||||
return;
|
||||
}
|
||||
// Guardar
|
||||
await client.prisma.economyItem.create({
|
||||
// Actualizar
|
||||
await client.prisma.economyItem.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
guildId,
|
||||
key: state.key,
|
||||
name: state.name!,
|
||||
description: state.description,
|
||||
category: state.category,
|
||||
@@ -171,8 +232,21 @@ export const command: CommandMessage = {
|
||||
props: state.props ?? {},
|
||||
},
|
||||
});
|
||||
await i.reply({ content: '✅ Item guardado!', flags: MessageFlags.Ephemeral });
|
||||
await editorMsg.edit({ content: `✅ Item \`${state.key}\` creado.`, components: [], display: undefined });
|
||||
await i.reply({ content: '✅ Item actualizado!', flags: MessageFlags.Ephemeral });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `✅ **Item Actualizado**\n└ Item \`${state.key}\` actualizado exitosamente.`
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('saved');
|
||||
return;
|
||||
}
|
||||
@@ -238,7 +312,10 @@ async function showBaseModal(i: ButtonInteraction, state: ItemEditorState, edito
|
||||
}
|
||||
|
||||
await sub.deferUpdate();
|
||||
await editorMsg.edit(createDisplay());
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [createDisplay().display]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -256,7 +333,10 @@ async function showTagsModal(i: ButtonInteraction, state: ItemEditorState, edito
|
||||
const tags = sub.components.getTextInputValue('tags');
|
||||
state.tags = tags ? tags.split(',').map((t) => t.trim()).filter(Boolean) : [];
|
||||
await sub.deferUpdate();
|
||||
await editorMsg.edit(createDisplay());
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [createDisplay().display]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -289,7 +369,11 @@ async function showPropsModal(i: ButtonInteraction, state: ItemEditorState, edit
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
state.props = parsed;
|
||||
await sub.deferUpdate(); await editorMsg.edit(createDisplay());
|
||||
await sub.deferUpdate();
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [createDisplay().display]
|
||||
});
|
||||
} catch (e) {
|
||||
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
@@ -13,6 +13,51 @@ interface MobEditorState {
|
||||
drops?: any; // JSON libre, tabla de recompensas
|
||||
}
|
||||
|
||||
function createMobDisplay(state: MobEditorState, editing: boolean = false) {
|
||||
const title = editing ? 'Editando Mob' : 'Creando Mob';
|
||||
const stats = state.stats || {};
|
||||
return {
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `👹 **${title}: \`${state.key}\`**`
|
||||
}]
|
||||
},
|
||||
{ type: 14, divider: true },
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**📋 Estado Actual:**\n` +
|
||||
`**Nombre:** ${state.name || '❌ No configurado'}\n` +
|
||||
`**Categoría:** ${state.category || 'Sin categoría'}\n` +
|
||||
`**Attack:** ${stats.attack || 0}\n` +
|
||||
`**HP:** ${stats.hp || 0}\n` +
|
||||
`**Defense:** ${stats.defense || 0}\n` +
|
||||
`**Drops:** ${Object.keys(state.drops || {}).length} items`
|
||||
}]
|
||||
},
|
||||
{ type: 14, divider: true },
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**🎮 Instrucciones:**\n` +
|
||||
`• **Base**: Nombre y categoría\n` +
|
||||
`• **Stats (JSON)**: Estadísticas del mob\n` +
|
||||
`• **Drops (JSON)**: Items que dropea\n` +
|
||||
`• **Guardar**: Confirma los cambios\n` +
|
||||
`• **Cancelar**: Descarta los cambios`
|
||||
}]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'mob-crear',
|
||||
type: 'message',
|
||||
@@ -49,15 +94,46 @@ export const command: CommandMessage = {
|
||||
collector.on('collect', async (i: MessageComponentInteraction) => {
|
||||
try {
|
||||
if (!i.isButton()) return;
|
||||
if (i.customId === 'mb_cancel') { await i.deferUpdate(); await editorMsg.edit({ content: '❌ Editor cancelado.', components: [] }); collector.stop('cancel'); return; }
|
||||
if (i.customId === 'mb_base') { await showBaseModal(i as ButtonInteraction, state); return; }
|
||||
if (i.customId === 'mb_stats') { await showJsonModal(i as ButtonInteraction, state, 'stats', 'Stats del Mob (JSON)'); return; }
|
||||
if (i.customId === 'mb_drops') { await showJsonModal(i as ButtonInteraction, state, 'drops', 'Drops del Mob (JSON)'); return; }
|
||||
if (i.customId === 'mb_cancel') {
|
||||
await i.deferUpdate();
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '**❌ Editor cancelado.**'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('cancel');
|
||||
return;
|
||||
}
|
||||
if (i.customId === 'mb_base') { await showBaseModal(i as ButtonInteraction, state, editorMsg, false); return; }
|
||||
if (i.customId === 'mb_stats') { await showJsonModal(i as ButtonInteraction, state, 'stats', 'Stats del Mob (JSON)', editorMsg, false); return; }
|
||||
if (i.customId === 'mb_drops') { await showJsonModal(i as ButtonInteraction, state, 'drops', 'Drops del Mob (JSON)', editorMsg, false); return; }
|
||||
if (i.customId === 'mb_save') {
|
||||
if (!state.name) { await i.reply({ content: '❌ Falta el nombre del mob.', flags: MessageFlags.Ephemeral }); return; }
|
||||
await client.prisma.mob.create({ data: { guildId, key: state.key, name: state.name!, category: state.category ?? null, stats: state.stats ?? {}, drops: state.drops ?? {} } });
|
||||
await i.reply({ content: '✅ Mob guardado!', flags: MessageFlags.Ephemeral });
|
||||
await editorMsg.edit({ content: `✅ Mob \`${state.key}\` creado.`, components: [] });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**✅ Mob \`${state.key}\` creado exitosamente.**`
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('saved');
|
||||
return;
|
||||
}
|
||||
@@ -70,26 +146,79 @@ export const command: CommandMessage = {
|
||||
},
|
||||
};
|
||||
|
||||
async function showBaseModal(i: ButtonInteraction, state: MobEditorState) {
|
||||
const modal = { title: 'Configuración base del Mob', customId: 'mb_base_modal', components: [
|
||||
async function showBaseModal(i: ButtonInteraction, state: MobEditorState, editorMsg: Message, editing: boolean) {
|
||||
const modal = { title: 'Base del Mob', customId: 'mb_base_modal', components: [
|
||||
{ type: ComponentType.Label, label: 'Nombre', component: { type: ComponentType.TextInput, customId: 'name', style: TextInputStyle.Short, required: true, value: state.name ?? '' } },
|
||||
{ type: ComponentType.Label, label: 'Categoría', component: { type: ComponentType.TextInput, customId: 'cat', style: TextInputStyle.Short, required: false, value: state.category ?? '' } },
|
||||
{ type: ComponentType.Label, label: 'Categoría (opcional)', component: { type: ComponentType.TextInput, customId: 'category', style: TextInputStyle.Short, required: false, value: state.category ?? '' } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try { const sub = await i.awaitModalSubmit({ time: 300_000 }); state.name = sub.components.getTextInputValue('name').trim(); state.category = sub.components.getTextInputValue('cat').trim() || undefined; await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral }); } catch {}
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
state.name = sub.components.getTextInputValue('name').trim();
|
||||
const cat = sub.components.getTextInputValue('category')?.trim();
|
||||
state.category = cat || undefined;
|
||||
await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral });
|
||||
|
||||
// Refresh display
|
||||
const newDisplay = createMobDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
newDisplay,
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'mb_base' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Stats (JSON)', custom_id: 'mb_stats' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Drops (JSON)', custom_id: 'mb_drops' },
|
||||
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'mb_save' },
|
||||
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'mb_cancel' },
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function showJsonModal(i: ButtonInteraction, state: MobEditorState, field: 'stats'|'drops', label: string) {
|
||||
const current = JSON.stringify(state[field] ?? (field==='stats'? { attack: 5 }: {}));
|
||||
const modal = { title: label, customId: `mb_json_${field}`, components: [
|
||||
{ type: ComponentType.Label, label: 'JSON', component: { type: ComponentType.TextInput, customId: 'json', style: TextInputStyle.Paragraph, required: false, value: current.slice(0, 4000) } },
|
||||
async function showJsonModal(i: ButtonInteraction, state: MobEditorState, field: 'stats'|'drops', title: string, editorMsg: Message, editing: boolean) {
|
||||
const current = JSON.stringify(state[field] ?? {});
|
||||
const modal = { title, customId: `mb_json_${field}`, components: [
|
||||
{ type: ComponentType.Label, label: 'JSON', component: { type: ComponentType.TextInput, customId: 'json', style: TextInputStyle.Paragraph, required: false, value: current.slice(0,4000) } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
const raw = sub.components.getTextInputValue('json');
|
||||
if (raw) {
|
||||
try { state[field] = JSON.parse(raw); await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral }); } catch { await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral }); }
|
||||
} else { state[field] = field==='stats' ? { attack: 5 } : {}; await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral }); }
|
||||
try {
|
||||
state[field] = JSON.parse(raw);
|
||||
await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral });
|
||||
} catch {
|
||||
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state[field] = {};
|
||||
await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
// Refresh display
|
||||
const newDisplay = createMobDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
newDisplay,
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'mb_base' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Stats (JSON)', custom_id: 'mb_stats' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Drops (JSON)', custom_id: 'mb_drops' },
|
||||
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'mb_save' },
|
||||
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'mb_cancel' },
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,50 @@ interface MobEditorState {
|
||||
stats?: any;
|
||||
drops?: any;
|
||||
}
|
||||
function createMobDisplay(state: MobEditorState, editing: boolean = false) {
|
||||
const title = editing ? 'Editando Mob' : 'Creando Mob';
|
||||
const stats = state.stats || {};
|
||||
return {
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `👹 **${title}: \`${state.key}\`**`
|
||||
}]
|
||||
},
|
||||
{ type: 14, divider: true },
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**📋 Estado Actual:**\n` +
|
||||
`**Nombre:** ${state.name || '❌ No configurado'}\n` +
|
||||
`**Categoría:** ${state.category || 'Sin categoría'}\n` +
|
||||
`**Attack:** ${stats.attack || 0}\n` +
|
||||
`**HP:** ${stats.hp || 0}\n` +
|
||||
`**Defense:** ${stats.defense || 0}\n` +
|
||||
`**Drops:** ${Object.keys(state.drops || {}).length} items`
|
||||
}]
|
||||
},
|
||||
{ type: 14, divider: true },
|
||||
{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**🎮 Instrucciones:**\n` +
|
||||
`• **Base**: Nombre y categoría\n` +
|
||||
`• **Stats (JSON)**: Estadísticas del mob\n` +
|
||||
`• **Drops (JSON)**: Items que dropea\n` +
|
||||
`• **Guardar**: Confirma los cambios\n` +
|
||||
`• **Cancelar**: Descarta los cambios`
|
||||
}]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'mob-editar',
|
||||
@@ -55,15 +99,46 @@ export const command: CommandMessage = {
|
||||
collector.on('collect', async (i: MessageComponentInteraction) => {
|
||||
try {
|
||||
if (!i.isButton()) return;
|
||||
if (i.customId === 'mb_cancel') { await i.deferUpdate(); await editorMsg.edit({ content: '❌ Editor cancelado.', components: [] }); collector.stop('cancel'); return; }
|
||||
if (i.customId === 'mb_base') { await showBaseModal(i as ButtonInteraction, state); return; }
|
||||
if (i.customId === 'mb_stats') { await showJsonModal(i as ButtonInteraction, state, 'stats', 'Stats del Mob (JSON)'); return; }
|
||||
if (i.customId === 'mb_drops') { await showJsonModal(i as ButtonInteraction, state, 'drops', 'Drops del Mob (JSON)'); return; }
|
||||
if (i.customId === 'mb_cancel') {
|
||||
await i.deferUpdate();
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0xFF0000,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: '**❌ Editor cancelado.**'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('cancel');
|
||||
return;
|
||||
}
|
||||
if (i.customId === 'mb_base') { await showBaseModal(i as ButtonInteraction, state, editorMsg, true); return; }
|
||||
if (i.customId === 'mb_stats') { await showJsonModal(i as ButtonInteraction, state, 'stats', 'Stats del Mob (JSON)', editorMsg, true); return; }
|
||||
if (i.customId === 'mb_drops') { await showJsonModal(i as ButtonInteraction, state, 'drops', 'Drops del Mob (JSON)', editorMsg, true); return; }
|
||||
if (i.customId === 'mb_save') {
|
||||
if (!state.name) { await i.reply({ content: '❌ Falta el nombre del mob.', flags: MessageFlags.Ephemeral }); return; }
|
||||
await client.prisma.mob.update({ where: { id: mob.id }, data: { name: state.name!, category: state.category ?? null, stats: state.stats ?? {}, drops: state.drops ?? {} } });
|
||||
await i.reply({ content: '✅ Mob actualizado!', flags: MessageFlags.Ephemeral });
|
||||
await editorMsg.edit({ content: `✅ Mob \`${state.key}\` actualizado.`, components: [] });
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [{
|
||||
type: 17,
|
||||
accent_color: 0x00FF00,
|
||||
components: [{
|
||||
type: 9,
|
||||
components: [{
|
||||
type: 10,
|
||||
content: `**✅ Mob \`${state.key}\` actualizado exitosamente.**`
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
collector.stop('saved');
|
||||
return;
|
||||
}
|
||||
@@ -76,25 +151,79 @@ export const command: CommandMessage = {
|
||||
},
|
||||
};
|
||||
|
||||
async function showBaseModal(i: ButtonInteraction, state: MobEditorState) {
|
||||
const modal = { title: 'Configuración base del Mob', customId: 'mb_base_modal', components: [
|
||||
async function showBaseModal(i: ButtonInteraction, state: MobEditorState, editorMsg: Message, editing: boolean) {
|
||||
const modal = { title: 'Base del Mob', customId: 'mb_base_modal', components: [
|
||||
{ type: ComponentType.Label, label: 'Nombre', component: { type: ComponentType.TextInput, customId: 'name', style: TextInputStyle.Short, required: true, value: state.name ?? '' } },
|
||||
{ type: ComponentType.Label, label: 'Categoría', component: { type: ComponentType.TextInput, customId: 'cat', style: TextInputStyle.Short, required: false, value: state.category ?? '' } },
|
||||
{ type: ComponentType.Label, label: 'Categoría (opcional)', component: { type: ComponentType.TextInput, customId: 'category', style: TextInputStyle.Short, required: false, value: state.category ?? '' } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try { const sub = await i.awaitModalSubmit({ time: 300_000 }); state.name = sub.components.getTextInputValue('name').trim(); state.category = sub.components.getTextInputValue('cat').trim() || undefined; await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral }); } catch {}
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
state.name = sub.components.getTextInputValue('name').trim();
|
||||
const cat = sub.components.getTextInputValue('category')?.trim();
|
||||
state.category = cat || undefined;
|
||||
await sub.reply({ content: '✅ Base actualizada.', flags: MessageFlags.Ephemeral });
|
||||
|
||||
// Refresh display
|
||||
const newDisplay = createMobDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
newDisplay,
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'mb_base' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Stats (JSON)', custom_id: 'mb_stats' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Drops (JSON)', custom_id: 'mb_drops' },
|
||||
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'mb_save' },
|
||||
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'mb_cancel' },
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function showJsonModal(i: ButtonInteraction, state: MobEditorState, field: 'stats'|'drops', label: string) {
|
||||
const current = JSON.stringify(state[field] ?? (field==='stats'? { attack: 5 }: {}));
|
||||
const modal = { title: label, customId: `mb_json_${field}`, components: [
|
||||
{ type: ComponentType.Label, label: 'JSON', component: { type: ComponentType.TextInput, customId: 'json', style: TextInputStyle.Paragraph, required: false, value: current.slice(0, 4000) } },
|
||||
async function showJsonModal(i: ButtonInteraction, state: MobEditorState, field: 'stats'|'drops', title: string, editorMsg: Message, editing: boolean) {
|
||||
const current = JSON.stringify(state[field] ?? {});
|
||||
const modal = { title, customId: `mb_json_${field}`, components: [
|
||||
{ type: ComponentType.Label, label: 'JSON', component: { type: ComponentType.TextInput, customId: 'json', style: TextInputStyle.Paragraph, required: false, value: current.slice(0,4000) } },
|
||||
] } as const;
|
||||
await i.showModal(modal);
|
||||
try {
|
||||
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||
const raw = sub.components.getTextInputValue('json');
|
||||
if (raw) { try { state[field] = JSON.parse(raw); await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral }); } catch { await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral }); } }
|
||||
else { state[field] = field==='stats' ? { attack: 5 } : {}; await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral }); }
|
||||
if (raw) {
|
||||
try {
|
||||
state[field] = JSON.parse(raw);
|
||||
await sub.reply({ content: '✅ Guardado.', flags: MessageFlags.Ephemeral });
|
||||
} catch {
|
||||
await sub.reply({ content: '❌ JSON inválido.', flags: MessageFlags.Ephemeral });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state[field] = {};
|
||||
await sub.reply({ content: 'ℹ️ Limpio.', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
// Refresh display
|
||||
const newDisplay = createMobDisplay(state, editing);
|
||||
await editorMsg.edit({
|
||||
flags: 32768,
|
||||
components: [
|
||||
newDisplay,
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{ type: 2, style: ButtonStyle.Primary, label: 'Base', custom_id: 'mb_base' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Stats (JSON)', custom_id: 'mb_stats' },
|
||||
{ type: 2, style: ButtonStyle.Secondary, label: 'Drops (JSON)', custom_id: 'mb_drops' },
|
||||
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'mb_save' },
|
||||
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'mb_cancel' },
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user