feat(economy): add commands for creating, editing, and managing game areas and levels with interactive editors
This commit is contained in:
100
src/commands/messages/game/areaCreate.ts
Normal file
100
src/commands/messages/game/areaCreate.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
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 { Message, MessageComponentInteraction, MessageFlags, ButtonInteraction } from 'discord.js';
|
||||||
|
import { ComponentType, TextInputStyle, ButtonStyle } from 'discord-api-types/v10';
|
||||||
|
|
||||||
|
interface AreaState {
|
||||||
|
key: string;
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
config?: any;
|
||||||
|
metadata?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const command: CommandMessage = {
|
||||||
|
name: 'area-crear',
|
||||||
|
type: 'message',
|
||||||
|
aliases: ['crear-area','areacreate'],
|
||||||
|
cooldown: 10,
|
||||||
|
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 allowed = await hasManageGuildOrStaff(message.member, message.guild!.id, prisma);
|
||||||
|
if (!allowed) { await message.reply('❌ No tienes permisos de ManageGuild ni rol de staff.'); return; }
|
||||||
|
|
||||||
|
const key = args[0]?.trim();
|
||||||
|
if (!key) { await message.reply('Uso: `!area-crear <key-única>`'); 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; }
|
||||||
|
|
||||||
|
const state: AreaState = { key, config: {}, metadata: {} };
|
||||||
|
|
||||||
|
const editorMsg = await message.channel.send({
|
||||||
|
content: `🗺️ Editor de Área: \`${key}\``,
|
||||||
|
components: [ { 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 });
|
||||||
|
collector.on('collect', async (i: MessageComponentInteraction) => {
|
||||||
|
try {
|
||||||
|
if (!i.isButton()) return;
|
||||||
|
switch (i.customId) {
|
||||||
|
case 'ga_cancel':
|
||||||
|
await i.deferUpdate();
|
||||||
|
await editorMsg.edit({ content: '❌ Editor de Área cancelado.', components: [] });
|
||||||
|
collector.stop('cancel');
|
||||||
|
return;
|
||||||
|
case 'ga_base':
|
||||||
|
await showBaseModal(i as ButtonInteraction, state);
|
||||||
|
return;
|
||||||
|
case 'ga_config':
|
||||||
|
await showJsonModal(i as ButtonInteraction, state, 'config', 'Config del Área');
|
||||||
|
return;
|
||||||
|
case 'ga_meta':
|
||||||
|
await showJsonModal(i as ButtonInteraction, state, 'metadata', 'Meta del Área');
|
||||||
|
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: [] });
|
||||||
|
collector.stop('saved');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!i.deferred && !i.replied) await i.reply({ content: '❌ Error procesando la acción.', flags: MessageFlags.Ephemeral });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on('end', async (_c,r)=> { if (r==='time') { try { await editorMsg.edit({ content: '⏰ Editor expirado.', components: [] }); } catch {} } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function showBaseModal(i: ButtonInteraction, state: AreaState) {
|
||||||
|
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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'config'|'metadata', title: string) {
|
||||||
|
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 {}
|
||||||
|
}
|
||||||
|
|
||||||
100
src/commands/messages/game/areaEdit.ts
Normal file
100
src/commands/messages/game/areaEdit.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
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 { Message, MessageComponentInteraction, MessageFlags, ButtonInteraction } from 'discord.js';
|
||||||
|
import { ComponentType, TextInputStyle, ButtonStyle } from 'discord-api-types/v10';
|
||||||
|
|
||||||
|
interface AreaState {
|
||||||
|
key: string;
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
config?: any;
|
||||||
|
metadata?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const command: CommandMessage = {
|
||||||
|
name: 'area-editar',
|
||||||
|
type: 'message',
|
||||||
|
aliases: ['editar-area','areaedit'],
|
||||||
|
cooldown: 10,
|
||||||
|
description: 'Edita una GameArea de este servidor con un editor interactivo.',
|
||||||
|
usage: 'area-editar <key-única>',
|
||||||
|
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.'); return; }
|
||||||
|
|
||||||
|
const key = args[0]?.trim();
|
||||||
|
if (!key) { await message.reply('Uso: `!area-editar <key-única>`'); 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; }
|
||||||
|
|
||||||
|
const state: AreaState = { key, name: area.name, type: area.type, config: area.config ?? {}, metadata: area.metadata ?? {} };
|
||||||
|
|
||||||
|
const editorMsg = await message.channel.send({
|
||||||
|
content: `🗺️ Editor de Área (editar): \`${key}\``,
|
||||||
|
components: [ { 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 });
|
||||||
|
collector.on('collect', async (i: MessageComponentInteraction) => {
|
||||||
|
try {
|
||||||
|
if (!i.isButton()) return;
|
||||||
|
switch (i.customId) {
|
||||||
|
case 'ga_cancel':
|
||||||
|
await i.deferUpdate();
|
||||||
|
await editorMsg.edit({ content: '❌ Editor de Área cancelado.', components: [] });
|
||||||
|
collector.stop('cancel');
|
||||||
|
return;
|
||||||
|
case 'ga_base':
|
||||||
|
await showBaseModal(i as ButtonInteraction, state);
|
||||||
|
return;
|
||||||
|
case 'ga_config':
|
||||||
|
await showJsonModal(i as ButtonInteraction, state, 'config', 'Config del Área');
|
||||||
|
return;
|
||||||
|
case 'ga_meta':
|
||||||
|
await showJsonModal(i as ButtonInteraction, state, 'metadata', 'Meta del Área');
|
||||||
|
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: [] });
|
||||||
|
collector.stop('saved');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!i.deferred && !i.replied) await i.reply({ content: '❌ Error procesando la acción.', flags: MessageFlags.Ephemeral });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on('end', async (_c,r)=> { if (r==='time') { try { await editorMsg.edit({ content: '⏰ Editor expirado.', components: [] }); } catch {} } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function showBaseModal(i: ButtonInteraction, state: AreaState) {
|
||||||
|
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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showJsonModal(i: ButtonInteraction, state: AreaState, field: 'config'|'metadata', title: string) {
|
||||||
|
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 {}
|
||||||
|
}
|
||||||
|
|
||||||
129
src/commands/messages/game/areaNivel.ts
Normal file
129
src/commands/messages/game/areaNivel.ts
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
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 { Message, MessageComponentInteraction, MessageFlags, ButtonInteraction } from 'discord.js';
|
||||||
|
import { ComponentType, TextInputStyle, ButtonStyle } from 'discord-api-types/v10';
|
||||||
|
|
||||||
|
interface LevelState {
|
||||||
|
areaKey: string;
|
||||||
|
level: number;
|
||||||
|
requirements?: any;
|
||||||
|
rewards?: any;
|
||||||
|
mobs?: any;
|
||||||
|
availableFrom?: string;
|
||||||
|
availableTo?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const command: CommandMessage = {
|
||||||
|
name: 'area-nivel',
|
||||||
|
type: 'message',
|
||||||
|
aliases: ['nivel-area','arealevel'],
|
||||||
|
cooldown: 10,
|
||||||
|
description: 'Crea o edita un nivel de una GameArea (requisitos, recompensas, mobs, ventana).',
|
||||||
|
usage: 'area-nivel <areaKey> <level>',
|
||||||
|
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.'); return; }
|
||||||
|
|
||||||
|
const areaKey = args[0]?.trim();
|
||||||
|
const levelNum = parseInt(args[1] || '', 10);
|
||||||
|
if (!areaKey || !Number.isFinite(levelNum) || levelNum <= 0) { await message.reply('Uso: `!area-nivel <areaKey> <level>`'); return; }
|
||||||
|
|
||||||
|
const guildId = message.guild!.id;
|
||||||
|
const area = await prisma.gameArea.findFirst({ where: { key: areaKey, OR: [{ guildId }, { guildId: null }] }, orderBy: [{ guildId: 'desc' }] });
|
||||||
|
if (!area) { await message.reply('❌ Área no encontrada.'); return; }
|
||||||
|
|
||||||
|
const existing = await prisma.gameAreaLevel.findFirst({ where: { areaId: area.id, level: levelNum } });
|
||||||
|
|
||||||
|
const state: LevelState = {
|
||||||
|
areaKey,
|
||||||
|
level: levelNum,
|
||||||
|
requirements: existing?.requirements ?? {},
|
||||||
|
rewards: existing?.rewards ?? {},
|
||||||
|
mobs: existing?.mobs ?? {},
|
||||||
|
availableFrom: existing?.availableFrom ? new Date(existing.availableFrom).toISOString() : '',
|
||||||
|
availableTo: existing?.availableTo ? new Date(existing.availableTo).toISOString() : '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const editorMsg = await message.channel.send({
|
||||||
|
content: `📊 Editor Nivel Área: \`${areaKey}\` nivel ${levelNum} ${existing ? '(editar)' : '(nuevo)'}`,
|
||||||
|
components: [ { type: 1, components: [
|
||||||
|
{ type: 2, style: ButtonStyle.Primary, label: 'Requisitos', custom_id: 'gl_req' },
|
||||||
|
{ type: 2, style: ButtonStyle.Secondary, label: 'Recompensas', custom_id: 'gl_rewards' },
|
||||||
|
{ type: 2, style: ButtonStyle.Secondary, label: 'Mobs', custom_id: 'gl_mobs' },
|
||||||
|
{ type: 2, style: ButtonStyle.Secondary, label: 'Ventana', custom_id: 'gl_window' },
|
||||||
|
{ type: 2, style: ButtonStyle.Success, label: 'Guardar', custom_id: 'gl_save' },
|
||||||
|
{ type: 2, style: ButtonStyle.Danger, label: 'Cancelar', custom_id: 'gl_cancel' },
|
||||||
|
] } ],
|
||||||
|
});
|
||||||
|
|
||||||
|
const collector = editorMsg.createMessageComponentCollector({ time: 30*60_000, filter: (i)=> i.user.id === message.author.id });
|
||||||
|
collector.on('collect', async (i: MessageComponentInteraction) => {
|
||||||
|
try {
|
||||||
|
if (!i.isButton()) return;
|
||||||
|
switch (i.customId) {
|
||||||
|
case 'gl_cancel':
|
||||||
|
await i.deferUpdate(); await editorMsg.edit({ content: '❌ Editor de Nivel cancelado.', components: [] }); collector.stop('cancel'); return;
|
||||||
|
case 'gl_req': await showJsonModal(i as ButtonInteraction, state, 'requirements', 'Requisitos'); return;
|
||||||
|
case 'gl_rewards': await showJsonModal(i as ButtonInteraction, state, 'rewards', 'Recompensas'); return;
|
||||||
|
case 'gl_mobs': await showJsonModal(i as ButtonInteraction, state, 'mobs', 'Mobs'); return;
|
||||||
|
case 'gl_window': await showWindowModal(i as ButtonInteraction, state); return;
|
||||||
|
case 'gl_save':
|
||||||
|
const data = {
|
||||||
|
areaId: area.id,
|
||||||
|
level: state.level,
|
||||||
|
requirements: state.requirements ?? {},
|
||||||
|
rewards: state.rewards ?? {},
|
||||||
|
mobs: state.mobs ?? {},
|
||||||
|
availableFrom: state.availableFrom ? new Date(state.availableFrom) : null,
|
||||||
|
availableTo: state.availableTo ? new Date(state.availableTo) : null,
|
||||||
|
} as const;
|
||||||
|
if (existing) {
|
||||||
|
await prisma.gameAreaLevel.update({ where: { id: existing.id }, data });
|
||||||
|
} else {
|
||||||
|
await prisma.gameAreaLevel.create({ data });
|
||||||
|
}
|
||||||
|
await i.reply({ content: '✅ Nivel guardado.', flags: MessageFlags.Ephemeral });
|
||||||
|
await editorMsg.edit({ content: `✅ Nivel guardado para \`${areaKey}\` (${state.level}).`, components: [] });
|
||||||
|
collector.stop('saved');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!i.deferred && !i.replied) await i.reply({ content: '❌ Error procesando la acción.', flags: MessageFlags.Ephemeral });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
collector.on('end', async (_c,r)=> { if (r==='time') { try { await editorMsg.edit({ content:'⏰ Editor expirado.', components: [] }); } catch {} } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function showJsonModal(i: ButtonInteraction, state: LevelState, field: 'requirements'|'rewards'|'mobs', title: string) {
|
||||||
|
const current = JSON.stringify(state[field] ?? {});
|
||||||
|
const modal = { title, customId: `gl_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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showWindowModal(i: ButtonInteraction, state: LevelState) {
|
||||||
|
const modal = { title: 'Ventana del Nivel', customId: 'gl_window_modal', components: [
|
||||||
|
{ type: ComponentType.Label, label: 'Desde (ISO, opcional)', component: { type: ComponentType.TextInput, customId: 'from', style: TextInputStyle.Short, required: false, value: state.availableFrom ?? '' } },
|
||||||
|
{ type: ComponentType.Label, label: 'Hasta (ISO, opcional)', component: { type: ComponentType.TextInput, customId: 'to', style: TextInputStyle.Short, required: false, value: state.availableTo ?? '' } },
|
||||||
|
] } as const;
|
||||||
|
await i.showModal(modal);
|
||||||
|
try {
|
||||||
|
const sub = await i.awaitModalSubmit({ time: 300_000 });
|
||||||
|
const from = sub.components.getTextInputValue('from').trim();
|
||||||
|
const to = sub.components.getTextInputValue('to').trim();
|
||||||
|
state.availableFrom = from || '';
|
||||||
|
state.availableTo = to || '';
|
||||||
|
await sub.reply({ content: '✅ Ventana actualizada.', flags: MessageFlags.Ephemeral });
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user