No mucho solo pequeños detalles
This commit is contained in:
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { ComponentType, ButtonStyle, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, Message } from "discord.js";
|
import { ComponentType, ButtonStyle, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, Message, MessageFlags } from "discord.js";
|
||||||
import { replaceVars } from "../../../core/lib/vars";
|
import { replaceVars } from "../../../core/lib/vars";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,8 +168,9 @@ const renderPreview = async (blockState: any, member: any, guild: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: "blockcreatev2",
|
name: "crear-embed",
|
||||||
type: "message",
|
type: "message",
|
||||||
|
aliases: ["embed-crear", "nuevo-embed", "blockcreatev2"],
|
||||||
cooldown: 20,
|
cooldown: 20,
|
||||||
run: async (message, args, client) => {
|
run: async (message, args, client) => {
|
||||||
if (!message.member?.permissions.has("Administrator")) {
|
if (!message.member?.permissions.has("Administrator")) {
|
||||||
@@ -233,7 +234,7 @@ export const command: CommandMessage = {
|
|||||||
|
|
||||||
collector.on("collect", async (i: any) => {
|
collector.on("collect", async (i: any) => {
|
||||||
if (i.user.id !== message.author.id) {
|
if (i.user.id !== message.author.id) {
|
||||||
await i.reply({ content: "No puedes usar este menú.", ephemeral: true });
|
await i.reply({ content: "No puedes usar este menú.", flags: MessageFlags.Ephemeral });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,18 +842,20 @@ export const command: CommandMessage = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Agregar manejo de modales
|
// Agregar manejo de modales mejorado con mejor gestión de errores
|
||||||
//@ts-ignore
|
let modalHandlerActive = true;
|
||||||
client.on('interactionCreate', async (interaction) => {
|
|
||||||
|
const modalHandler = async (interaction: any) => {
|
||||||
if (!interaction.isModalSubmit()) return;
|
if (!interaction.isModalSubmit()) return;
|
||||||
if (interaction.user.id !== message.author.id) return;
|
if (interaction.user.id !== message.author.id) return;
|
||||||
if (!interaction.customId.endsWith('_modal')) return;
|
if (!interaction.customId.endsWith('_modal')) return;
|
||||||
|
if (!modalHandlerActive) return; // Evitar procesar si ya no está activo
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (interaction.customId) {
|
switch (interaction.customId) {
|
||||||
case 'edit_title_modal': {
|
case 'edit_title_modal': {
|
||||||
blockState.title = interaction.fields.getTextInputValue('title_input');
|
blockState.title = interaction.fields.getTextInputValue('title_input');
|
||||||
await interaction.reply({ content: '✅ Título actualizado.', ephemeral: true });
|
await interaction.reply({ content: '✅ Título actualizado.', flags: MessageFlags.Ephemeral });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'edit_description_modal': {
|
case 'edit_description_modal': {
|
||||||
@@ -863,7 +866,7 @@ export const command: CommandMessage = {
|
|||||||
} else {
|
} else {
|
||||||
blockState.components.push({ type: 10, content: newDescription, thumbnail: null });
|
blockState.components.push({ type: 10, content: newDescription, thumbnail: null });
|
||||||
}
|
}
|
||||||
await interaction.reply({ content: '✅ Descripción actualizada.', ephemeral: true });
|
await interaction.reply({ content: '✅ Descripción actualizada.', flags: MessageFlags.Ephemeral });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'edit_color_modal': {
|
case 'edit_color_modal': {
|
||||||
@@ -875,26 +878,26 @@ export const command: CommandMessage = {
|
|||||||
if (/^[0-9A-F]{6}$/i.test(hexColor)) {
|
if (/^[0-9A-F]{6}$/i.test(hexColor)) {
|
||||||
blockState.color = parseInt(hexColor, 16);
|
blockState.color = parseInt(hexColor, 16);
|
||||||
} else {
|
} else {
|
||||||
await interaction.reply({ content: '❌ Color inválido. Usa formato HEX (#FF5733)', ephemeral: true });
|
await interaction.reply({ content: '❌ Color inválido. Usa formato HEX (#FF5733)', flags: MessageFlags.Ephemeral });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await interaction.reply({ content: '✅ Color actualizado.', ephemeral: true });
|
await interaction.reply({ content: '✅ Color actualizado.', flags: MessageFlags.Ephemeral });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'add_content_modal': {
|
case 'add_content_modal': {
|
||||||
const newContent = interaction.fields.getTextInputValue('content_input');
|
const newContent = interaction.fields.getTextInputValue('content_input');
|
||||||
blockState.components.push({ type: 10, content: newContent, thumbnail: null });
|
blockState.components.push({ type: 10, content: newContent, thumbnail: null });
|
||||||
await interaction.reply({ content: '✅ Contenido añadido.', ephemeral: true });
|
await interaction.reply({ content: '✅ Contenido añadido.', flags: MessageFlags.Ephemeral });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'add_image_modal': {
|
case 'add_image_modal': {
|
||||||
const imageUrl = interaction.fields.getTextInputValue('image_url_input');
|
const imageUrl = interaction.fields.getTextInputValue('image_url_input');
|
||||||
if (isValidUrl(imageUrl)) {
|
if (isValidUrl(imageUrl)) {
|
||||||
blockState.components.push({ type: 12, url: imageUrl });
|
blockState.components.push({ type: 12, url: imageUrl });
|
||||||
await interaction.reply({ content: '✅ Imagen añadida.', ephemeral: true });
|
await interaction.reply({ content: '✅ Imagen añadida.', flags: MessageFlags.Ephemeral });
|
||||||
} else {
|
} else {
|
||||||
await interaction.reply({ content: '❌ URL de imagen inválida.', ephemeral: true });
|
await interaction.reply({ content: '❌ URL de imagen inválida.', flags: MessageFlags.Ephemeral });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -904,9 +907,9 @@ export const command: CommandMessage = {
|
|||||||
const coverUrl = interaction.fields.getTextInputValue('cover_input');
|
const coverUrl = interaction.fields.getTextInputValue('cover_input');
|
||||||
if (isValidUrl(coverUrl)) {
|
if (isValidUrl(coverUrl)) {
|
||||||
blockState.coverImage = coverUrl;
|
blockState.coverImage = coverUrl;
|
||||||
await interaction.reply({ content: '✅ Imagen de portada actualizada.', ephemeral: true });
|
await interaction.reply({ content: '✅ Imagen de portada actualizada.', flags: MessageFlags.Ephemeral });
|
||||||
} else {
|
} else {
|
||||||
await interaction.reply({ content: '❌ URL de portada inválida.', ephemeral: true });
|
await interaction.reply({ content: '❌ URL de portada inválida.', flags: MessageFlags.Ephemeral });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -919,7 +922,7 @@ export const command: CommandMessage = {
|
|||||||
const spacing = Math.min(3, Math.max(1, parseInt(spacingStr) || 1));
|
const spacing = Math.min(3, Math.max(1, parseInt(spacingStr) || 1));
|
||||||
|
|
||||||
blockState.components.push({ type: 14, divider, spacing });
|
blockState.components.push({ type: 14, divider, spacing });
|
||||||
await interaction.reply({ content: '✅ Separador añadido.', ephemeral: true });
|
await interaction.reply({ content: '✅ Separador añadido.', flags: MessageFlags.Ephemeral });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'edit_thumbnail_modal': {
|
case 'edit_thumbnail_modal': {
|
||||||
@@ -927,12 +930,18 @@ export const command: CommandMessage = {
|
|||||||
const textComp = blockState.components.find((c: any) => c.type === 10);
|
const textComp = blockState.components.find((c: any) => c.type === 10);
|
||||||
|
|
||||||
if (textComp) {
|
if (textComp) {
|
||||||
if (thumbnailUrl.trim() === '' || !isValidUrl(thumbnailUrl)) {
|
if (thumbnailUrl.trim() === '') {
|
||||||
|
// Si está vacío, eliminar thumbnail
|
||||||
textComp.thumbnail = null;
|
textComp.thumbnail = null;
|
||||||
await interaction.reply({ content: '✅ Thumbnail eliminado.', ephemeral: true });
|
await interaction.reply({ content: '✅ Thumbnail eliminado.', flags: MessageFlags.Ephemeral });
|
||||||
|
} else if (!isValidUrl(thumbnailUrl)) {
|
||||||
|
// Si no es una URL válida, mostrar error
|
||||||
|
await interaction.reply({ content: '❌ URL de thumbnail inválida.', flags: MessageFlags.Ephemeral });
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
// Si es una URL válida, añadir thumbnail
|
||||||
textComp.thumbnail = thumbnailUrl;
|
textComp.thumbnail = thumbnailUrl;
|
||||||
await interaction.reply({ content: '✅ Thumbnail actualizado.', ephemeral: true });
|
await interaction.reply({ content: '✅ Thumbnail actualizado.', flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -951,13 +960,13 @@ export const command: CommandMessage = {
|
|||||||
components: Array.isArray(importedData.components) ? importedData.components : blockState.components
|
components: Array.isArray(importedData.components) ? importedData.components : blockState.components
|
||||||
};
|
};
|
||||||
|
|
||||||
await interaction.reply({ content: '✅ JSON importado correctamente.', ephemeral: true });
|
await interaction.reply({ content: '✅ JSON importado correctamente.', flags: MessageFlags.Ephemeral });
|
||||||
} else {
|
} else {
|
||||||
await interaction.reply({ content: '❌ Estructura JSON inválida.', ephemeral: true });
|
await interaction.reply({ content: '❌ Estructura JSON inválida.', flags: MessageFlags.Ephemeral });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await interaction.reply({ content: '❌ JSON inválido. Verifica el formato.', ephemeral: true });
|
await interaction.reply({ content: '❌ JSON inválido. Verifica el formato.', flags: MessageFlags.Ephemeral });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -966,34 +975,69 @@ export const command: CommandMessage = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar la vista previa después de cada cambio en el modal
|
// Actualizar la vista previa después de cada cambio en el modal con mejor manejo de errores
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
if (!modalHandlerActive) return; // Evitar actualizar si ya no está activo
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Verificar si el mensaje aún existe antes de intentar editarlo
|
||||||
|
const messageExists = await editorMessage.fetch().catch(() => null);
|
||||||
|
if (!messageExists) {
|
||||||
|
console.log('El mensaje del editor ya no existe');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await editorMessage.edit({
|
await editorMessage.edit({
|
||||||
components: [await renderPreview(blockState, message.member, message.guild), ...btns(false)]
|
components: [await renderPreview(blockState, message.member, message.guild), ...btns(false)]
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('Error actualizando preview:', error);
|
// Manejar diferentes tipos de errores
|
||||||
|
if (error.code === 10008) {
|
||||||
|
console.log('Mensaje del editor eliminado');
|
||||||
|
} else if (error.code === 10062) {
|
||||||
|
console.log('Interacción expirada');
|
||||||
|
} else {
|
||||||
|
console.error('Error actualizando preview:', error.message || error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('Error en modal:', error);
|
console.error('Error en modal:', error);
|
||||||
try {
|
try {
|
||||||
await interaction.reply({ content: '❌ Error procesando el modal.', ephemeral: true });
|
// Solo intentar responder si la interacción no ha expirado
|
||||||
} catch {}
|
if (error.code !== 10062 && !interaction.replied && !interaction.deferred) {
|
||||||
|
await interaction.reply({ content: '❌ Error procesando el modal.', flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
});
|
} catch (replyError) {
|
||||||
|
console.log('No se pudo responder a la interacción (probablemente expirada)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registrar el manejador de modales
|
||||||
|
client.on('interactionCreate', modalHandler);
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
collector.on("end", async (_, reason) => {
|
collector.on("end", async (_, reason) => {
|
||||||
|
// Desactivar el manejador de modales cuando el collector termine
|
||||||
|
modalHandlerActive = false;
|
||||||
|
client.off('interactionCreate', modalHandler);
|
||||||
|
|
||||||
if (reason === "time") {
|
if (reason === "time") {
|
||||||
|
try {
|
||||||
|
const messageExists = await editorMessage.fetch().catch(() => null);
|
||||||
|
if (messageExists) {
|
||||||
await editorMessage.edit({
|
await editorMessage.edit({
|
||||||
components: [
|
components: [
|
||||||
{ type: 17, components: [{ type: 10, content: "⏰ Editor finalizado por inactividad." }] }
|
{ type: 17, components: [{ type: 10, content: "⏰ Editor finalizado por inactividad." }] }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('No se pudo actualizar el mensaje final');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
404
src/commands/messages/alliaces/displayComponentsDemo.ts
Normal file
404
src/commands/messages/alliaces/displayComponentsDemo.ts
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
|
|
||||||
|
export const command: CommandMessage = {
|
||||||
|
name: "displaydemo",
|
||||||
|
type: "message",
|
||||||
|
aliases: ["ddemo", "componentsdemo"],
|
||||||
|
cooldown: 10,
|
||||||
|
run: async (message, args, client) => {
|
||||||
|
if (!message.member?.permissions.has("Administrator")) {
|
||||||
|
await message.reply("❌ No tienes permisos de Administrador.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🎯 DEMOSTRACIÓN COMPLETA DE DISPLAYCOMPONENTS CON ACCESORIOS
|
||||||
|
|
||||||
|
// Panel principal con accessory de thumbnail
|
||||||
|
const mainPanel = {
|
||||||
|
type: 17, // Container
|
||||||
|
accent_color: 0x5865f2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10, // TextDisplay
|
||||||
|
content: "🎨 **Demostración de DisplayComponents Avanzados**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14, // Separator
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
// Sección con accessory de botón
|
||||||
|
{
|
||||||
|
type: 9, // Section
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🔘 **Sección con Botón Accesorio**\n\nEste texto aparece junto a un botón como accesorio. Los accesorios permiten añadir elementos interactivos sin ocupar una fila completa."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2, // Button
|
||||||
|
style: 1, // Primary
|
||||||
|
label: "Acción Rápida",
|
||||||
|
custom_id: "quick_action",
|
||||||
|
emoji: { name: "⚡" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
// Sección con accessory de thumbnail
|
||||||
|
{
|
||||||
|
type: 9, // Section
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🖼️ **Sección con Thumbnail**\n\nAquí se muestra texto con una imagen en miniatura como accesorio. Perfecto para mostrar íconos de servidores, avatares o logotipos."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 11, // Thumbnail
|
||||||
|
media: {
|
||||||
|
url: message.guild?.iconURL({ forceStatic: false }) || "https://cdn.discordapp.com/embed/avatars/0.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
// Sección con accessory de link button
|
||||||
|
{
|
||||||
|
type: 9, // Section
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🔗 **Sección con Botón de Enlace**\n\nEste tipo de accesorio permite enlaces externos directos sin necesidad de interacciones complejas."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2, // Button
|
||||||
|
style: 5, // Link
|
||||||
|
label: "Ir a Discord",
|
||||||
|
url: "https://discord.com",
|
||||||
|
emoji: { name: "🚀" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fila de botones normales para interacción
|
||||||
|
const actionRow = {
|
||||||
|
type: 1, // ActionRow
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 3, // Success
|
||||||
|
label: "✨ Más Ejemplos",
|
||||||
|
custom_id: "show_more_examples"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2, // Secondary
|
||||||
|
label: "🔄 Cambiar Estilos",
|
||||||
|
custom_id: "change_styles"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 4, // Danger
|
||||||
|
label: "❌ Cerrar",
|
||||||
|
custom_id: "close_demo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const demoMessage = await message.reply({
|
||||||
|
flags: 4096, // SuppressEmbeds
|
||||||
|
components: [mainPanel, actionRow]
|
||||||
|
});
|
||||||
|
|
||||||
|
const collector = demoMessage.createMessageComponentCollector({
|
||||||
|
time: 300000, // 5 minutos
|
||||||
|
filter: (i: any) => i.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("collect", async (interaction: any) => {
|
||||||
|
switch (interaction.customId) {
|
||||||
|
case "quick_action":
|
||||||
|
await interaction.reply({
|
||||||
|
content: "⚡ **Acción Rápida Ejecutada!**\n\nEste botón estaba como accesorio en una sección.",
|
||||||
|
flags: 64 // Ephemeral
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "show_more_examples":
|
||||||
|
// Panel con múltiples ejemplos de accesorios
|
||||||
|
const examplesPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xff9500,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🎯 **Más Ejemplos de Accesorios**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
// Ejemplo con avatar del usuario
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `👤 **Perfil de ${message.author.username}**\n\nEjemplo usando tu avatar como thumbnail accesorio.`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 11,
|
||||||
|
media: {
|
||||||
|
url: message.author.displayAvatarURL({ forceStatic: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: false,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
// Ejemplo con botón de estilo diferente
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🎨 **Botones con Diferentes Estilos**\n\nLos accesorios pueden tener distintos estilos y emojis personalizados."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 4, // Danger
|
||||||
|
label: "Peligro",
|
||||||
|
custom_id: "danger_button",
|
||||||
|
emoji: { name: "⚠️" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: false,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
// Imagen como accessory
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🖼️ **Imágenes Personalizadas**\n\nTambién puedes usar imágenes personalizadas, íconos de servidores invitados, etc."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 11,
|
||||||
|
media: {
|
||||||
|
url: "https://cdn.discordapp.com/attachments/123/456/discord-logo.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [examplesPanel, {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2, // Secondary
|
||||||
|
label: "↩️ Volver",
|
||||||
|
custom_id: "back_to_main"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "change_styles":
|
||||||
|
// Panel mostrando diferentes combinaciones de estilos
|
||||||
|
const stylesPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x57f287,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🎨 **Galería de Estilos**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
// Botón Primary como accesorio
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🔵 **Botón Primary (Azul)**\nEstilo: 1 - Para acciones principales"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 1, // Primary
|
||||||
|
label: "Principal",
|
||||||
|
custom_id: "style_primary"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Botón Secondary como accesorio
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "⚫ **Botón Secondary (Gris)**\nEstilo: 2 - Para acciones secundarias"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2, // Secondary
|
||||||
|
label: "Secundario",
|
||||||
|
custom_id: "style_secondary"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Botón Success como accesorio
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🟢 **Botón Success (Verde)**\nEstilo: 3 - Para confirmar acciones"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 3, // Success
|
||||||
|
label: "Confirmar",
|
||||||
|
custom_id: "style_success"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Botón Danger como accesorio
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🔴 **Botón Danger (Rojo)**\nEstilo: 4 - Para acciones destructivas"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 4, // Danger
|
||||||
|
label: "Eliminar",
|
||||||
|
custom_id: "style_danger"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [stylesPanel, {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "↩️ Volver",
|
||||||
|
custom_id: "back_to_main"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "danger_button":
|
||||||
|
case "style_primary":
|
||||||
|
case "style_secondary":
|
||||||
|
case "style_success":
|
||||||
|
case "style_danger":
|
||||||
|
await interaction.reply({
|
||||||
|
content: `🎯 **Botón ${interaction.customId.replace('style_', '').replace('_', ' ')} activado!**\n\nEste botón era un accesorio de una sección.`,
|
||||||
|
flags: 64 // Ephemeral
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "back_to_main":
|
||||||
|
await interaction.update({
|
||||||
|
components: [mainPanel, actionRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "close_demo":
|
||||||
|
const closedPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x36393f,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "✅ **Demostración Finalizada**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "Gracias por probar DisplayComponents con accesorios!\n\n💡 **Recuerda:** Los accesorios son ideales para:\n• Botones de acción rápida\n• Thumbnails e íconos\n• Enlaces externos\n• Elementos decorativos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [closedPanel]
|
||||||
|
});
|
||||||
|
collector.stop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("end", async (collected: any, reason: string) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
try {
|
||||||
|
const timeoutPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x36393f,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "⏰ **Demostración Expirada**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "La demostración ha expirado por inactividad.\nUsa `!displaydemo` nuevamente para verla."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await demoMessage.edit({
|
||||||
|
components: [timeoutPanel]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Mensaje eliminado o error de edición
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,217 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: "embeddelete",
|
name: "eliminar-embed",
|
||||||
type: "message",
|
type: "message",
|
||||||
aliases: ["delembed", "removeembed"],
|
aliases: ["embed-eliminar", "borrar-embed", "embeddelete"],
|
||||||
cooldown: 10,
|
cooldown: 10,
|
||||||
//@ts-ignore
|
run: async (message: any, args: string[], client: any) => {
|
||||||
run: async (message, args, client) => {
|
|
||||||
if (!message.member?.permissions.has("Administrator")) {
|
if (!message.member?.permissions.has("Administrator")) {
|
||||||
return message.reply("❌ No tienes permisos de Administrador.");
|
await message.reply("❌ No tienes permisos de Administrador.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const embedName = args[0];
|
// Obtener todos los bloques del servidor
|
||||||
if (!embedName) {
|
const blocks = await client.prisma.blockV2Config.findMany({
|
||||||
return message.reply("Debes proporcionar el nombre del embed a eliminar. Uso: `!embeddelete <nombre>`");
|
where: { guildId: message.guildId! },
|
||||||
|
select: { name: true, id: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blocks.length === 0) {
|
||||||
|
const noBlocksEmbed = {
|
||||||
|
color: 0xf04747,
|
||||||
|
title: "🗂️ Panel de Eliminación de Bloques",
|
||||||
|
description: "📭 **No hay bloques disponibles**\n\nNo se encontraron bloques para eliminar en este servidor.\n\nPuedes crear nuevos bloques usando `!blockcreate`.",
|
||||||
|
footer: {
|
||||||
|
text: "Sistema de gestión de bloques • Amayo Bot"
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await message.reply({
|
||||||
|
embeds: [noBlocksEmbed]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear opciones para el select menu
|
||||||
|
const selectOptions = blocks.slice(0, 25).map((block: any, index: number) => ({
|
||||||
|
label: block.name,
|
||||||
|
value: block.name,
|
||||||
|
description: `ID: ${block.id}`,
|
||||||
|
emoji: index < 10 ? { name: `${index + 1}️⃣` } : { name: "📄" }
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Crear embed principal de eliminación
|
||||||
|
const deleteEmbed = {
|
||||||
|
color: 0xff6b35,
|
||||||
|
title: "🗑️ Panel de Eliminación de Bloques",
|
||||||
|
description: `📊 **${blocks.length} bloque(s) encontrado(s)**\n\n⚠️ **ADVERTENCIA:** La eliminación es permanente e irreversible.\n\nSelecciona el bloque que deseas eliminar del menú de abajo:`,
|
||||||
|
footer: {
|
||||||
|
text: "Selecciona un bloque para eliminar • Timeout: 5 minutos"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 3, // StringSelect
|
||||||
|
custom_id: "delete_block_select",
|
||||||
|
placeholder: "🗑️ Selecciona un bloque para eliminar...",
|
||||||
|
min_values: 1,
|
||||||
|
max_values: 1,
|
||||||
|
options: selectOptions
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2, // Button
|
||||||
|
style: 4, // Danger
|
||||||
|
label: "❌ Cancelar",
|
||||||
|
custom_id: "cancel_delete"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const panelMessage = await message.reply({
|
||||||
|
embeds: [deleteEmbed],
|
||||||
|
components: [actionRow, cancelRow]
|
||||||
|
});
|
||||||
|
|
||||||
|
const collector = panelMessage.createMessageComponentCollector({
|
||||||
|
time: 300000, // 5 minutos
|
||||||
|
filter: (i: any) => i.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("collect", async (interaction: any) => {
|
||||||
|
if (interaction.customId === "cancel_delete") {
|
||||||
|
const canceledEmbed = {
|
||||||
|
color: 0x36393f,
|
||||||
|
title: "❌ Operación Cancelada",
|
||||||
|
description: "La eliminación de bloques ha sido cancelada.\nNingún bloque fue eliminado.",
|
||||||
|
footer: { text: "Operación cancelada por el usuario" }
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [canceledEmbed],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.stop("cancelled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interaction.customId === "delete_block_select" && interaction.isStringSelectMenu()) {
|
||||||
|
const selectedBlock = interaction.values[0];
|
||||||
|
|
||||||
|
const confirmationEmbed = {
|
||||||
|
color: 0xf04747,
|
||||||
|
title: "⚠️ CONFIRMAR ELIMINACIÓN",
|
||||||
|
description: `🗑️ **Bloque a eliminar:** \`${selectedBlock}\`\n\n❗ **ESTA ACCIÓN ES IRREVERSIBLE**\n\nUna vez eliminado, no podrás recuperar:\n• Toda la configuración del bloque\n• Los componentes y contenido\n• Las imágenes y colores personalizados\n\n¿Estás seguro de que quieres continuar?`,
|
||||||
|
footer: { text: "⚠️ Acción irreversible - Piénsalo bien" }
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmationRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 4, // Danger
|
||||||
|
label: "🗑️ SÍ, ELIMINAR",
|
||||||
|
custom_id: `confirm_delete_${selectedBlock}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2, // Secondary
|
||||||
|
label: "↩️ Volver Atrás",
|
||||||
|
custom_id: "back_to_selection"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [confirmationEmbed],
|
||||||
|
components: [confirmationRow]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interaction.customId.startsWith("confirm_delete_")) {
|
||||||
|
const blockName = interaction.customId.replace("confirm_delete_", "");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.prisma.blockV2Config.delete({
|
await client.prisma.blockV2Config.delete({
|
||||||
where: {
|
where: {
|
||||||
guildId_name: {
|
guildId_name: {
|
||||||
guildId: message.guildId!,
|
guildId: message.guildId!,
|
||||||
name: embedName,
|
name: blockName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return message.reply(`✅ El embed **${embedName}** fue eliminado con éxito.`);
|
const successEmbed = {
|
||||||
} catch {
|
color: 0x57f287,
|
||||||
return message.reply("❌ No encontré un embed con ese nombre.");
|
title: "✅ Eliminación Exitosa",
|
||||||
|
description: `🗑️ **Bloque eliminado:** \`${blockName}\`\n\n✨ El bloque ha sido eliminado permanentemente de la base de datos.\n\n📋 Para ver los bloques restantes, usa: \`!embedlist\`\n📝 Para crear un nuevo bloque, usa: \`!blockcreate\``,
|
||||||
|
footer: { text: "Bloque eliminado exitosamente" }
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [successEmbed],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.stop("success");
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const errorEmbed = {
|
||||||
|
color: 0xf04747,
|
||||||
|
title: "❌ Error en la Eliminación",
|
||||||
|
description: `🔍 **Bloque no encontrado:** \`${blockName}\`\n\n💭 Posibles causas:\n• El bloque ya fue eliminado\n• Error de conexión con la base de datos\n• El nombre del bloque cambió\n\n🔄 Intenta refrescar la lista con \`!embedlist\``,
|
||||||
|
footer: { text: "Error de eliminación" }
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [errorEmbed],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.stop("error");
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interaction.customId === "back_to_selection") {
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [deleteEmbed],
|
||||||
|
components: [actionRow, cancelRow]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("end", async (collected: any, reason: string) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
const timeoutEmbed = {
|
||||||
|
color: 0x36393f,
|
||||||
|
title: "⏰ Tiempo Agotado",
|
||||||
|
description: "El panel de eliminación ha expirado por inactividad.\nUsa el comando nuevamente si necesitas eliminar bloques.",
|
||||||
|
footer: { text: "Panel expirado por inactividad" }
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await panelMessage.edit({
|
||||||
|
embeds: [timeoutEmbed],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Mensaje ya eliminado o error de edición
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,75 +1,402 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
import {
|
|
||||||
//@ts-ignore
|
|
||||||
ChannelType,
|
|
||||||
ContainerBuilder,
|
|
||||||
//@ts-ignore
|
|
||||||
MessageFlags,
|
|
||||||
SectionBuilder,
|
|
||||||
SeparatorBuilder,
|
|
||||||
//@ts-ignore
|
|
||||||
SeparatorSpacingSize,
|
|
||||||
TextChannel,
|
|
||||||
TextDisplayBuilder
|
|
||||||
} from "discord.js";
|
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: "embedlist",
|
name: "lista-embeds",
|
||||||
type: "message",
|
type: "message",
|
||||||
aliases: ["listembeds", "embeds"],
|
aliases: ["embeds", "ver-embeds", "embedlist"],
|
||||||
cooldown: 10,
|
cooldown: 10,
|
||||||
//@ts-ignore
|
run: async (message: any, args: string[], client: any) => {
|
||||||
run: async (message, args, client) => {
|
|
||||||
if (!message.member?.permissions.has("Administrator")) {
|
if (!message.member?.permissions.has("Administrator")) {
|
||||||
return message.reply("❌ No tienes permisos de Administrador.");
|
return message.reply("❌ No tienes permisos de Administrador.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const embeds = await client.prisma.blockV2Config.findMany({
|
const blocks = await client.prisma.blockV2Config.findMany({
|
||||||
where: { guildId: message.guildId! },
|
where: { guildId: message.guildId! },
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true,
|
||||||
|
config: true
|
||||||
|
},
|
||||||
|
orderBy: { name: 'asc' }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (embeds.length === 0) {
|
if (blocks.length === 0) {
|
||||||
return message.reply("📭 No hay ningún embed guardado en este servidor.");
|
const emptyEmbed = {
|
||||||
|
color: 0x5865f2,
|
||||||
|
title: "📚 Centro de Gestión de Bloques",
|
||||||
|
description: "📭 **No hay bloques disponibles**\n\nEste servidor aún no tiene bloques configurados.\n\n🚀 **¿Quieres empezar?**\n• Usa `!crear-embed <nombre>` para crear tu primer bloque\n• Usa `!editar-embed <nombre>` para editar bloques existentes",
|
||||||
|
footer: { text: "Sistema de gestión de bloques • Amayo Bot" }
|
||||||
|
};
|
||||||
|
|
||||||
|
const createRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 3,
|
||||||
|
label: "📝 Crear Primer Bloque",
|
||||||
|
custom_id: "show_create_help"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const helpMessage = await message.reply({
|
||||||
|
embeds: [emptyEmbed],
|
||||||
|
components: [createRow]
|
||||||
|
});
|
||||||
|
|
||||||
|
const helpCollector = helpMessage.createMessageComponentCollector({
|
||||||
|
time: 60000,
|
||||||
|
filter: (i: any) => i.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
helpCollector.on("collect", async (interaction: any) => {
|
||||||
|
if (interaction.customId === "show_create_help") {
|
||||||
|
const helpEmbed = {
|
||||||
|
color: 0x57f287,
|
||||||
|
title: "📖 Guía de Creación de Bloques",
|
||||||
|
description: "🔧 **Comandos disponibles:**\n\n• `!crear-embed <nombre>` - Crear nuevo bloque\n• `!editar-embed <nombre>` - Editar bloque existente\n• `!eliminar-embed <nombre>` - Eliminar bloque\n• `!lista-embeds` - Ver todos los bloques\n\n💡 **Tip:** Los bloques permiten crear interfaces modernas e interactivas.",
|
||||||
|
footer: { text: "Guía de comandos de creación" }
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [helpEmbed],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = new TextDisplayBuilder()
|
// Dividir bloques en páginas de 5
|
||||||
.setContent('﹒⌒ Embed List ╰୧﹒');
|
const itemsPerPage = 5;
|
||||||
|
const totalPages = Math.ceil(blocks.length / itemsPerPage);
|
||||||
|
let currentPage = 0;
|
||||||
|
|
||||||
// Combina la lista de embeds en la misma sección que la miniatura
|
const generateBlockListEmbed = (page: number) => {
|
||||||
// para un mejor diseño.
|
const startIndex = page * itemsPerPage;
|
||||||
//@ts-ignore
|
const endIndex = Math.min(startIndex + itemsPerPage, blocks.length);
|
||||||
const embedListContent = embeds.map((e, i) => `**${i + 1}.** ${e.name}`).join("\n");
|
const pageBlocks = blocks.slice(startIndex, endIndex);
|
||||||
|
|
||||||
// Obtenemos la URL del icono de forma segura
|
let blockListText = `📊 **Página ${page + 1} de ${totalPages}** (${blocks.length} total)\n\n`;
|
||||||
const guildIconURL = message.guild?.iconURL({ forceStatic: false });
|
|
||||||
|
|
||||||
// Creamos la sección que contendrá el texto Y la miniatura
|
pageBlocks.forEach((block: any, index: number) => {
|
||||||
const mainSection = new SectionBuilder()
|
const globalIndex = startIndex + index + 1;
|
||||||
.addTextDisplayComponents(text => text.setContent(embedListContent)); // <--- Componente principal requerido
|
const componentsCount = Array.isArray(block.config?.components) ? block.config.components.length : 0;
|
||||||
|
const hasImage = block.config?.coverImage ? "🖼️" : "";
|
||||||
|
|
||||||
// Solo añadimos la miniatura si la URL existe
|
blockListText += `**${globalIndex}.** \`${block.name}\` ${hasImage}\n`;
|
||||||
if (guildIconURL) {
|
blockListText += ` └ ${componentsCount} componente(s) • ID: ${block.id.slice(-8)}\n\n`;
|
||||||
//@ts-ignore
|
});
|
||||||
mainSection.setThumbnailAccessory(thumbnail => thumbnail
|
|
||||||
.setURL(guildIconURL)
|
return {
|
||||||
.setDescription('Icono del servidor')
|
color: 0x5865f2,
|
||||||
);
|
title: "📚 Centro de Gestión de Bloques",
|
||||||
|
description: blockListText,
|
||||||
|
footer: { text: `Página ${page + 1}/${totalPages} • ${blocks.length} bloques total` }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateActionRows = (page: number) => {
|
||||||
|
const rows = [];
|
||||||
|
|
||||||
|
// Select menu para acciones rápidas
|
||||||
|
const currentPageBlocks = blocks.slice(page * itemsPerPage, (page + 1) * itemsPerPage);
|
||||||
|
if (currentPageBlocks.length > 0) {
|
||||||
|
const selectOptions = currentPageBlocks.map((block: any) => ({
|
||||||
|
label: block.name,
|
||||||
|
value: block.name,
|
||||||
|
description: `${Array.isArray(block.config?.components) ? block.config.components.length : 0} componente(s)`,
|
||||||
|
emoji: { name: "⚙️" }
|
||||||
|
}));
|
||||||
|
|
||||||
|
rows.push({
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 3,
|
||||||
|
custom_id: "block_actions_select",
|
||||||
|
placeholder: "⚙️ Selecciona un bloque para gestionar...",
|
||||||
|
options: selectOptions
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const separator = new SeparatorBuilder()
|
// Botones de navegación y acciones generales
|
||||||
.setSpacing(SeparatorSpacingSize.Large)
|
const navigationRow: any = {
|
||||||
.setDivider(false);
|
type: 1,
|
||||||
|
components: []
|
||||||
|
};
|
||||||
|
|
||||||
const container = new ContainerBuilder()
|
// Navegación
|
||||||
.setAccentColor(0x49225B)
|
if (totalPages > 1) {
|
||||||
.addTextDisplayComponents(title)
|
navigationRow.components.push({
|
||||||
.addSeparatorComponents(separator)
|
type: 2,
|
||||||
.addSectionComponents(mainSection); // <--- Añadimos la sección ya completa
|
style: 2,
|
||||||
|
label: "◀️ Anterior",
|
||||||
|
custom_id: "prev_page",
|
||||||
|
disabled: page === 0
|
||||||
|
});
|
||||||
|
|
||||||
|
navigationRow.components.push({
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: `${page + 1}/${totalPages}`,
|
||||||
|
custom_id: "page_info",
|
||||||
|
disabled: true
|
||||||
|
});
|
||||||
|
|
||||||
if (message.channel.type === ChannelType.GuildText) {
|
navigationRow.components.push({
|
||||||
const channel = message.channel as TextChannel;
|
type: 2,
|
||||||
await channel.send({ components: [container], flags: MessageFlags.IsComponentsV2});
|
style: 2,
|
||||||
|
label: "▶️ Siguiente",
|
||||||
|
custom_id: "next_page",
|
||||||
|
disabled: page === totalPages - 1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Botón de refrescar
|
||||||
|
navigationRow.components.push({
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
label: "🔄 Refrescar",
|
||||||
|
custom_id: "refresh_list"
|
||||||
|
});
|
||||||
|
|
||||||
|
rows.push(navigationRow);
|
||||||
|
|
||||||
|
// Acciones principales
|
||||||
|
const actionsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 3,
|
||||||
|
label: "📝 Crear Nuevo",
|
||||||
|
custom_id: "show_create_commands"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "📋 Exportar Lista",
|
||||||
|
custom_id: "export_block_list"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 4,
|
||||||
|
label: "🗑️ Eliminar",
|
||||||
|
custom_id: "show_delete_commands"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
rows.push(actionsRow);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
const panelMessage = await message.reply({
|
||||||
|
embeds: [generateBlockListEmbed(currentPage)],
|
||||||
|
components: generateActionRows(currentPage)
|
||||||
|
});
|
||||||
|
|
||||||
|
const collector = panelMessage.createMessageComponentCollector({
|
||||||
|
time: 600000,
|
||||||
|
filter: (i: any) => i.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("collect", async (interaction: any) => {
|
||||||
|
switch (interaction.customId) {
|
||||||
|
case "prev_page":
|
||||||
|
if (currentPage > 0) {
|
||||||
|
currentPage--;
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [generateBlockListEmbed(currentPage)],
|
||||||
|
components: generateActionRows(currentPage)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "next_page":
|
||||||
|
if (currentPage < totalPages - 1) {
|
||||||
|
currentPage++;
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [generateBlockListEmbed(currentPage)],
|
||||||
|
components: generateActionRows(currentPage)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "refresh_list":
|
||||||
|
// Recargar datos
|
||||||
|
const refreshedBlocks = await client.prisma.blockV2Config.findMany({
|
||||||
|
where: { guildId: message.guildId! },
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
id: true,
|
||||||
|
config: true
|
||||||
|
},
|
||||||
|
orderBy: { name: 'asc' }
|
||||||
|
});
|
||||||
|
|
||||||
|
blocks.length = 0;
|
||||||
|
blocks.push(...refreshedBlocks);
|
||||||
|
|
||||||
|
const newTotalPages = Math.ceil(blocks.length / itemsPerPage);
|
||||||
|
if (currentPage >= newTotalPages) {
|
||||||
|
currentPage = Math.max(0, newTotalPages - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [generateBlockListEmbed(currentPage)],
|
||||||
|
components: generateActionRows(currentPage)
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "block_actions_select":
|
||||||
|
if (interaction.isStringSelectMenu()) {
|
||||||
|
const selectedBlock = interaction.values[0];
|
||||||
|
|
||||||
|
const blockActionEmbed = {
|
||||||
|
color: 0xff9500,
|
||||||
|
title: `⚙️ Gestión de Bloque: \`${selectedBlock}\``,
|
||||||
|
description: "Selecciona la acción que deseas realizar con este bloque:",
|
||||||
|
footer: { text: "Acciones disponibles para el bloque seleccionado" }
|
||||||
|
};
|
||||||
|
|
||||||
|
const blockActionsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
label: "✏️ Editar",
|
||||||
|
custom_id: `edit_block_${selectedBlock}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "👁️ Vista Previa",
|
||||||
|
custom_id: `preview_block_${selectedBlock}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "📋 Duplicar",
|
||||||
|
custom_id: `duplicate_block_${selectedBlock}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 4,
|
||||||
|
label: "🗑️ Eliminar",
|
||||||
|
custom_id: `delete_block_${selectedBlock}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const backRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "↩️ Volver a la Lista",
|
||||||
|
custom_id: "back_to_list"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [blockActionEmbed],
|
||||||
|
components: [blockActionsRow, backRow]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "back_to_list":
|
||||||
|
await interaction.update({
|
||||||
|
embeds: [generateBlockListEmbed(currentPage)],
|
||||||
|
components: generateActionRows(currentPage)
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "show_create_commands":
|
||||||
|
await interaction.reply({
|
||||||
|
content: `🔧 **Crear nuevos bloques:**\n\n• \`!crear-embed <nombre>\` - Crear bloque básico\n• \`!editar-embed <nombre>\` - Editor avanzado\n\n💡 **Ejemplo:** \`!crear-embed bienvenida\`\n\n📖 **Guía completa:** Los bloques usan DisplayComponents para crear interfaces modernas e interactivas.`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "show_delete_commands":
|
||||||
|
await interaction.reply({
|
||||||
|
content: `⚠️ **Eliminar bloques:**\n\n• \`!eliminar-embed\` - Panel interactivo de eliminación\n• \`!eliminar-embed <nombre>\` - Eliminación directa\n\n❗ **Advertencia:** La eliminación es irreversible.`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "export_block_list":
|
||||||
|
const exportText = blocks.map((block: any, index: number) => {
|
||||||
|
const componentsCount = Array.isArray(block.config?.components) ? block.config.components.length : 0;
|
||||||
|
return `${index + 1}. ${block.name} (${componentsCount} componentes) - ID: ${block.id}`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `📋 **Lista Exportada:**\n\`\`\`\n${exportText}\`\`\``,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Manejar acciones específicas de bloques
|
||||||
|
if (interaction.customId.startsWith("edit_block_")) {
|
||||||
|
const blockName = interaction.customId.replace("edit_block_", "");
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Usa: \`!editar-embed ${blockName}\``,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
} else if (interaction.customId.startsWith("delete_block_")) {
|
||||||
|
const blockName = interaction.customId.replace("delete_block_", "");
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Usa: \`!eliminar-embed ${blockName}\` para eliminar este bloque de forma segura.`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
} else if (interaction.customId.startsWith("preview_block_")) {
|
||||||
|
const blockName = interaction.customId.replace("preview_block_", "");
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Vista previa de \`${blockName}\` - Funcionalidad en desarrollo`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
} else if (interaction.customId.startsWith("duplicate_block_")) {
|
||||||
|
const blockName = interaction.customId.replace("duplicate_block_", "");
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Funcionalidad de duplicación de \`${blockName}\` en desarrollo`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("end", async (collected: any, reason: string) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
const timeoutEmbed = {
|
||||||
|
color: 0x36393f,
|
||||||
|
title: "⏰ Panel Expirado",
|
||||||
|
description: "El panel de gestión ha expirado por inactividad.\n\nUsa `!lista-embeds` para abrir un nuevo panel de gestión.",
|
||||||
|
footer: { text: "Panel expirado por inactividad" }
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await panelMessage.edit({
|
||||||
|
embeds: [timeoutEmbed],
|
||||||
|
components: []
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Mensaje eliminado o error de edición
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
|
// @ts-ignore
|
||||||
|
import { ComponentType, ButtonStyle, MessageFlags, ChannelType } from "discord.js";
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: "setchannel-alliance",
|
name: "setchannel-alliance",
|
||||||
@@ -11,83 +13,650 @@ export const command: CommandMessage = {
|
|||||||
return message.reply("❌ No tienes permisos de Administrador.");
|
return message.reply("❌ No tienes permisos de Administrador.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validar argumentos
|
// Obtener canales configurados existentes y bloques disponibles
|
||||||
if (args.length < 2) {
|
const existingChannels = await client.prisma.allianceChannel.findMany({
|
||||||
return message.reply("❌ Uso correcto: `!setchannel-alliance <#canal|ID> <blockConfigName>`");
|
where: { guildId: message.guildId! },
|
||||||
|
include: { blockConfig: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
const availableBlocks = await client.prisma.blockV2Config.findMany({
|
||||||
|
where: { guildId: message.guildId! },
|
||||||
|
select: { name: true, id: true, config: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Panel principal de configuración
|
||||||
|
const setupPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x00ff88, // Verde alliance
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "# 🤝 **Centro de Configuración de Alianzas**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `📊 **Estado Actual:**\n` +
|
||||||
|
`• **${existingChannels.length}** canales configurados\n` +
|
||||||
|
`• **${availableBlocks.length}** bloques disponibles\n` +
|
||||||
|
`• **${existingChannels.filter((c: any) => c.isActive).length}** canales activos\n\n` +
|
||||||
|
`⚙️ Selecciona una acción para continuar:`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainActionsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Success,
|
||||||
|
label: "➕ Configurar Canal",
|
||||||
|
custom_id: "setup_new_channel",
|
||||||
|
emoji: { name: "🔧" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Primary,
|
||||||
|
label: "📋 Ver Configurados",
|
||||||
|
custom_id: "view_configured_channels",
|
||||||
|
emoji: { name: "📊" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "🧪 Crear Bloque",
|
||||||
|
custom_id: "help_create_block",
|
||||||
|
emoji: { name: "📝" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const managementRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "🔄 Refrescar",
|
||||||
|
custom_id: "refresh_status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "📖 Ayuda",
|
||||||
|
custom_id: "show_help"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Danger,
|
||||||
|
label: "🗑️ Gestionar",
|
||||||
|
custom_id: "manage_channels",
|
||||||
|
disabled: existingChannels.length === 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const panelMessage = await message.reply({
|
||||||
|
flags: MessageFlags.SuppressEmbeds,
|
||||||
|
components: [setupPanel, mainActionsRow, managementRow]
|
||||||
|
});
|
||||||
|
|
||||||
|
const collector = panelMessage.createMessageComponentCollector({
|
||||||
|
time: 600000, // 10 minutos
|
||||||
|
filter: (i) => i.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("collect", async (interaction) => {
|
||||||
|
switch (interaction.customId) {
|
||||||
|
case "setup_new_channel":
|
||||||
|
// Obtener canales de texto disponibles
|
||||||
|
const textChannels = message.guild!.channels.cache
|
||||||
|
.filter(channel =>
|
||||||
|
channel.type === ChannelType.GuildText &&
|
||||||
|
// @ts-ignore
|
||||||
|
!existingChannels.some(ec => ec.channelId === channel.id)
|
||||||
|
)
|
||||||
|
.map(channel => ({
|
||||||
|
label: `#${channel.name}`,
|
||||||
|
value: channel.id,
|
||||||
|
description: `ID: ${channel.id}`,
|
||||||
|
emoji: { name: "💬" }
|
||||||
|
}))
|
||||||
|
.slice(0, 25);
|
||||||
|
|
||||||
|
if (textChannels.length === 0) {
|
||||||
|
const noChannelsPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xffa500,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "⚠️ **Sin Canales Disponibles**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "No hay canales de texto disponibles para configurar.\n\n**Posibles causas:**\n• Todos los canales ya están configurados\n• No hay canales de texto en el servidor\n• Faltan permisos para ver canales"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({ components: [noChannelsPanel] });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelInput = args[0];
|
const channelSelectPanel = {
|
||||||
const blockConfigName = args[1];
|
type: 17,
|
||||||
|
accent_color: 0x5865f2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "📺 **Seleccionar Canal**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `🎯 Selecciona el canal que quieres configurar para alianzas:\n\n💡 **Tip:** Solo se muestran canales de texto que aún no están configurados.`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
// Extraer ID del canal
|
const channelSelectRow = {
|
||||||
let channelId: string;
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 3,
|
||||||
|
custom_id: "channel_select",
|
||||||
|
placeholder: "📺 Selecciona un canal...",
|
||||||
|
options: textChannels
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
// Si es una mención de canal (#canal)
|
const backRow = {
|
||||||
if (channelInput.startsWith('<#') && channelInput.endsWith('>')) {
|
type: 1,
|
||||||
channelId = channelInput.slice(2, -1);
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "↩️ Volver al Inicio",
|
||||||
|
custom_id: "back_to_main"
|
||||||
}
|
}
|
||||||
// Si es solo un ID
|
]
|
||||||
else if (/^\d+$/.test(channelInput)) {
|
};
|
||||||
channelId = channelInput;
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [channelSelectPanel, channelSelectRow, backRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "channel_select":
|
||||||
|
if (interaction.isStringSelectMenu()) {
|
||||||
|
const selectedChannelId = interaction.values[0];
|
||||||
|
const selectedChannel = message.guild!.channels.cache.get(selectedChannelId);
|
||||||
|
|
||||||
|
if (availableBlocks.length === 0) {
|
||||||
|
const noBlocksPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xf04747,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "❌ **Sin Bloques Disponibles**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `📺 **Canal seleccionado:** #${selectedChannel?.name}\n\n⚠️ **Problema:** No hay bloques de configuración disponibles.\n\n🔧 **Solución:**\n• Crea un bloque usando: \`!blockcreate <nombre>\`\n• Edita bloques usando: \`!blockeditv2 <nombre>\``
|
||||||
}
|
}
|
||||||
else {
|
]
|
||||||
return message.reply("❌ Formato de canal inválido. Usa `#canal` o el ID del canal.");
|
};
|
||||||
|
|
||||||
|
const createBlockRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Success,
|
||||||
|
label: "📝 Ayuda Crear Bloque",
|
||||||
|
custom_id: "help_create_block"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "↩️ Volver",
|
||||||
|
custom_id: "setup_new_channel"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [noBlocksPanel, createBlockRow]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
const blockOptions = availableBlocks.map(block => ({
|
||||||
|
label: block.name,
|
||||||
|
value: `${selectedChannelId}_${block.name}`,
|
||||||
|
description: `ID: ${block.id}`,
|
||||||
|
emoji: { name: "🧩" }
|
||||||
|
}));
|
||||||
|
|
||||||
|
const blockSelectPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xff9500,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🧩 **Seleccionar Configuración**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `📺 **Canal:** #${selectedChannel?.name}\n\n🎯 Selecciona qué bloque de configuración usar para este canal:\n\n💡 Los bloques definen cómo se verán los mensajes de alianza.`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const blockSelectRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 3,
|
||||||
|
custom_id: "block_select",
|
||||||
|
placeholder: "🧩 Selecciona una configuración...",
|
||||||
|
options: blockOptions
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
// @ts-ignore
|
||||||
|
components: [blockSelectPanel, blockSelectRow, backRow]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "block_select":
|
||||||
|
if (interaction.isStringSelectMenu()) {
|
||||||
|
const [channelId, blockName] = interaction.values[0].split('_');
|
||||||
|
const channel = message.guild!.channels.cache.get(channelId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Verificar que el canal existe en el servidor
|
// Verificar que el bloque existe
|
||||||
const channel = await message.guild?.channels.fetch(channelId);
|
|
||||||
if (!channel) {
|
|
||||||
return message.reply("❌ El canal especificado no existe en este servidor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verificar que el canal es un canal de texto
|
|
||||||
if (!channel.isTextBased()) {
|
|
||||||
return message.reply("❌ El canal debe ser un canal de texto.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verificar que existe el blockConfig
|
|
||||||
const blockConfig = await client.prisma.blockV2Config.findFirst({
|
const blockConfig = await client.prisma.blockV2Config.findFirst({
|
||||||
where: {
|
where: {
|
||||||
guildId: message.guildId,
|
guildId: message.guildId,
|
||||||
name: blockConfigName
|
name: blockName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!blockConfig) {
|
if (!blockConfig) {
|
||||||
return message.reply(`❌ No se encontró el bloque de configuración \`${blockConfigName}\`. Asegúrate de que exista.`);
|
throw new Error("Bloque no encontrado");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configurar el canal de alianzas
|
// Configurar el canal
|
||||||
const allianceChannel = await client.prisma.allianceChannel.upsert({
|
await client.prisma.allianceChannel.upsert({
|
||||||
where: {
|
where: {
|
||||||
guildId_channelId: {
|
guildId_channelId: {
|
||||||
guildId: message.guildId,
|
guildId: message.guildId!,
|
||||||
channelId: channelId
|
channelId: channelId
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
guildId: message.guildId,
|
guildId: message.guildId!,
|
||||||
channelId: channelId,
|
channelId: channelId,
|
||||||
blockConfigName: blockConfigName,
|
blockConfigName: blockName,
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
blockConfigName: blockConfigName,
|
blockConfigName: blockName,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return message.reply(`✅ Canal de alianzas configurado correctamente!\n\n` +
|
const successPanel = {
|
||||||
`**Canal:** <#${channelId}>\n` +
|
type: 17,
|
||||||
`**Configuración:** \`${blockConfigName}\`\n` +
|
accent_color: 0x57f287,
|
||||||
`**Estado:** Activo\n\n` +
|
components: [
|
||||||
`Los enlaces de Discord válidos en este canal ahora otorgarán puntos de alianza.`);
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "✅ **Configuración Exitosa**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `🎉 **Canal configurado correctamente:**\n\n` +
|
||||||
|
`📺 **Canal:** <#${channelId}>\n` +
|
||||||
|
`🧩 **Configuración:** \`${blockName}\`\n` +
|
||||||
|
`🟢 **Estado:** Activo\n\n` +
|
||||||
|
`🚀 **¡Listo!** Los enlaces de Discord válidos en este canal ahora otorgarán puntos de alianza usando la configuración especificada.`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const successActionsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Success,
|
||||||
|
label: "🏠 Volver al Inicio",
|
||||||
|
custom_id: "back_to_main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Primary,
|
||||||
|
label: "➕ Configurar Otro",
|
||||||
|
custom_id: "setup_new_channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "📋 Ver Todos",
|
||||||
|
custom_id: "view_configured_channels"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [successPanel, successActionsRow]
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error configurando canal de alianzas:', error);
|
const errorPanel = {
|
||||||
return message.reply("❌ Ocurrió un error al configurar el canal de alianzas. Inténtalo de nuevo.");
|
type: 17,
|
||||||
|
accent_color: 0xf04747,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "❌ **Error de Configuración**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `🔍 **Detalles del error:**\n\n` +
|
||||||
|
`📺 Canal: <#${channelId}>\n` +
|
||||||
|
`🧩 Bloque: \`${blockName}\`\n\n` +
|
||||||
|
`💭 **Posibles causas:**\n` +
|
||||||
|
`• El bloque fue eliminado\n` +
|
||||||
|
`• Error de base de datos\n` +
|
||||||
|
`• Permisos insuficientes\n\n` +
|
||||||
|
`🔄 Intenta nuevamente o contacta al soporte.`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({ components: [errorPanel] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "view_configured_channels":
|
||||||
|
if (existingChannels.length === 0) {
|
||||||
|
const emptyListPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x36393f,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "📋 **Canales Configurados**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🗂️ **Lista vacía**\n\nNo hay canales configurados para alianzas en este servidor.\n\n🚀 **¿Quieres empezar?**\n• Usa el botón \"Configurar Canal\" para añadir tu primer canal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({ components: [emptyListPanel] });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let channelListText = `📊 **${existingChannels.length} canal(es) configurado(s)**\n\n`;
|
||||||
|
// @ts-ignore
|
||||||
|
existingChannels.forEach((config, index) => {
|
||||||
|
const channel = message.guild!.channels.cache.get(config.channelId);
|
||||||
|
const channelName = channel ? `#${channel.name}` : `Canal Eliminado`;
|
||||||
|
const status = config.isActive ? "🟢 Activo" : "🔴 Inactivo";
|
||||||
|
|
||||||
|
channelListText += `**${index + 1}.** ${channelName}\n`;
|
||||||
|
channelListText += ` └ \`${config.blockConfigName}\` • ${status}\n\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const channelListPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x5865f2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "📋 **Canales Configurados**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: channelListText
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const listActionsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "🏠 Volver al Inicio",
|
||||||
|
custom_id: "back_to_main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Primary,
|
||||||
|
label: "➕ Configurar Más",
|
||||||
|
custom_id: "setup_new_channel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Danger,
|
||||||
|
label: "🔧 Gestionar",
|
||||||
|
custom_id: "manage_channels"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [channelListPanel, listActionsRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "help_create_block":
|
||||||
|
await interaction.reply({
|
||||||
|
content: `📖 **Guía de Bloques**\n\n🧩 **¿Qué son los bloques?**\nLos bloques son configuraciones que definen cómo se ven los mensajes de alianza.\n\n🔧 **Comandos para gestionar bloques:**\n\n• \`!blockcreate <nombre>\` - Crear nuevo bloque\n• \`!blockeditv2 <nombre>\` - Editor completo\n• \`!embedlist\` - Ver todos los bloques\n• \`!embeddelete\` - Eliminar bloques\n\n💡 **Ejemplo:** \`!blockcreate alianza-general\``,
|
||||||
|
flags: 64 // Ephemeral
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "show_help":
|
||||||
|
await interaction.reply({
|
||||||
|
content: `📚 **Ayuda Completa**\n\n🤝 **Sistema de Alianzas:**\nConfigura canales donde los enlaces de Discord válidos otorgan puntos.\n\n🏗️ **Proceso de configuración:**\n1. Crear un bloque con \`!blockcreate\`\n2. Configurar canal con este comando\n3. ¡Los usuarios empezarán a ganar puntos!\n\n⚙️ **Gestión avanzada:**\n• Usar \`!embedlist\` para ver bloques\n• Usar \`!blockeditv2\` para personalizar\n• Este comando para gestionar canales`,
|
||||||
|
flags: 64 // Ephemeral
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "back_to_main":
|
||||||
|
case "refresh_status":
|
||||||
|
// Recargar datos y volver al panel principal
|
||||||
|
const refreshedChannels = await client.prisma.allianceChannel.findMany({
|
||||||
|
where: { guildId: message.guildId! },
|
||||||
|
include: { blockConfig: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
const refreshedBlocks = await client.prisma.blockV2Config.findMany({
|
||||||
|
where: { guildId: message.guildId! },
|
||||||
|
select: { name: true, id: true, config: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
const refreshedPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x00ff88,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🤝 **Centro de Configuración de Alianzas**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `📊 **Estado Actual:**\n` +
|
||||||
|
`• **${refreshedChannels.length}** canales configurados\n` +
|
||||||
|
`• **${refreshedBlocks.length}** bloques disponibles\n` +
|
||||||
|
// @ts-ignore
|
||||||
|
`• **${refreshedChannels.filter(c => c.isActive).length}** canales activos\n\n` +
|
||||||
|
`⚙️ Selecciona una acción para continuar:`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshedMainActions = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Success,
|
||||||
|
label: "➕ Configurar Canal",
|
||||||
|
custom_id: "setup_new_channel",
|
||||||
|
emoji: { name: "🔧" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Primary,
|
||||||
|
label: "📋 Ver Configurados",
|
||||||
|
custom_id: "view_configured_channels",
|
||||||
|
emoji: { name: "📊" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "🧪 Crear Bloque",
|
||||||
|
custom_id: "help_create_block",
|
||||||
|
emoji: { name: "📝" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshedManagement = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "🔄 Refrescar",
|
||||||
|
custom_id: "refresh_status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Secondary,
|
||||||
|
label: "📖 Ayuda",
|
||||||
|
custom_id: "show_help"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: ButtonStyle.Danger,
|
||||||
|
label: "🗑️ Gestionar",
|
||||||
|
custom_id: "manage_channels",
|
||||||
|
disabled: refreshedChannels.length === 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [refreshedPanel, refreshedMainActions, refreshedManagement]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "manage_channels":
|
||||||
|
await interaction.reply({
|
||||||
|
content: `🔧 **Gestión Avanzada**\n\n⚠️ **Funciones de gestión avanzada:**\n\n🔄 Activar/desactivar canales\n🗑️ Eliminar configuraciones\n✏️ Cambiar bloques asignados\n\n💡 **Próximamente:** Panel interactivo completo`,
|
||||||
|
flags: 64 // Ephemeral
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("end", async (collected, reason) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
const timeoutPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x36393f,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "⏰ **Sesión Expirada**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: true,
|
||||||
|
spacing: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "El panel de configuración ha expirado.\nUsa el comando nuevamente para continuar."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await panelMessage.edit({
|
||||||
|
components: [timeoutPanel]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Mensaje eliminado o error de edición
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
433
src/commands/messages/help.ts
Normal file
433
src/commands/messages/help.ts
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
|
|
||||||
|
export const command: CommandMessage = {
|
||||||
|
name: 'ayuda',
|
||||||
|
type: "message",
|
||||||
|
aliases: ['help', 'comandos', 'cmds'],
|
||||||
|
cooldown: 5,
|
||||||
|
run: async (message: any, args: string[], client: any) => {
|
||||||
|
// Obtener información del servidor para mostrar el prefix actual
|
||||||
|
const server = await client.prisma.guild.findFirst({
|
||||||
|
where: { id: message.guild!.id }
|
||||||
|
});
|
||||||
|
const prefix = server?.prefix || "!";
|
||||||
|
|
||||||
|
// Definir categorías de comandos con nombres modernos
|
||||||
|
const commandCategories = {
|
||||||
|
"Alianzas": [
|
||||||
|
{
|
||||||
|
name: "crear-embed",
|
||||||
|
aliases: ["embed-crear", "nuevo-embed"],
|
||||||
|
description: "Crear nuevos embeds con DisplayComponents modernos",
|
||||||
|
usage: `${prefix}crear-embed <nombre>`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "editar-embed",
|
||||||
|
aliases: ["embed-editar", "modificar-embed"],
|
||||||
|
description: "Editor avanzado de embeds con interfaz interactiva",
|
||||||
|
usage: `${prefix}editar-embed <nombre>`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lista-embeds",
|
||||||
|
aliases: ["embeds", "ver-embeds"],
|
||||||
|
description: "Centro de gestión de embeds con paginación",
|
||||||
|
usage: `${prefix}lista-embeds`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "eliminar-embed",
|
||||||
|
aliases: ["embed-eliminar", "borrar-embed"],
|
||||||
|
description: "Panel interactivo para eliminar embeds",
|
||||||
|
usage: `${prefix}eliminar-embed [nombre]`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "canal-alianza",
|
||||||
|
aliases: ["configurar-canal", "setup-canal"],
|
||||||
|
description: "Configurar canales para sistema de alianzas",
|
||||||
|
usage: `${prefix}canal-alianza`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "demo-componentes",
|
||||||
|
aliases: ["demo", "prueba-componentes"],
|
||||||
|
description: "Demostración de DisplayComponents con accesorios",
|
||||||
|
usage: `${prefix}demo-componentes`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Red": [
|
||||||
|
{
|
||||||
|
name: "ping",
|
||||||
|
aliases: ["latencia", "pong"],
|
||||||
|
description: "Verificar latencia y estado del bot",
|
||||||
|
usage: `${prefix}ping`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Configuracion": [
|
||||||
|
{
|
||||||
|
name: "configuracion",
|
||||||
|
aliases: ["config", "ajustes", "settings"],
|
||||||
|
description: "Panel de configuración del servidor",
|
||||||
|
usage: `${prefix}configuracion`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Definir backRow una sola vez fuera de los casos
|
||||||
|
const backRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "↩️ Volver al Menú",
|
||||||
|
custom_id: "back_to_main"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Si se especifica un comando específico
|
||||||
|
if (args.length > 0) {
|
||||||
|
const searchCommand = args[0].toLowerCase();
|
||||||
|
let foundCommand = null;
|
||||||
|
let foundCategory = null;
|
||||||
|
|
||||||
|
// Buscar el comando en todas las categorías
|
||||||
|
for (const [category, commands] of Object.entries(commandCategories)) {
|
||||||
|
const command = commands.find(cmd =>
|
||||||
|
cmd.name === searchCommand || cmd.aliases.includes(searchCommand)
|
||||||
|
);
|
||||||
|
if (command) {
|
||||||
|
foundCommand = command;
|
||||||
|
foundCategory = category;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundCommand) {
|
||||||
|
// Panel detallado del comando específico - SIMPLIFICADO
|
||||||
|
const commandDetailPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x5865f2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `### 📖 **Ayuda: \`${foundCommand.name}\`**\n\n**Categoría:** ${foundCategory}\n**Descripción:** ${foundCommand.description}\n**Uso:** ${foundCommand.usage}\n\n**Aliases disponibles:**\n${foundCommand.aliases.map(alias => `\`${prefix}${alias}\``).join(", ")}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const detailActionsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "📋 Ver Todos",
|
||||||
|
custom_id: "show_all_commands"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "🔍 Buscar Otro",
|
||||||
|
custom_id: "search_command"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await message.reply({
|
||||||
|
flags: 32768,
|
||||||
|
components: [commandDetailPanel, detailActionsRow]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Comando no encontrado - SIMPLIFICADO
|
||||||
|
const notFoundPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xf04747,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `### ❌ **Comando no encontrado: \`${searchCommand}\`**\n\nNo se encontró ningún comando con el nombre o alias \`${searchCommand}\`.\n\n🔍 **Sugerencias:**\n• Verifica la ortografía\n• Usa \`${prefix}ayuda\` para ver todos los comandos\n• Usa \`${prefix}ayuda <categoría>\` para filtrar`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const notFoundRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
label: "📋 Ver Todos",
|
||||||
|
custom_id: "show_all_commands"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await message.reply({
|
||||||
|
flags: 32768,
|
||||||
|
components: [notFoundPanel, notFoundRow]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel principal de ayuda - OPTIMIZADO para no exceder límite de componentes
|
||||||
|
const helpPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x5865f2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `### 📚 **Centro de Ayuda - ${message.guild!.name}**`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 1,
|
||||||
|
divider: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `**Prefix actual:** \`${prefix}\`\n**Total de comandos:** ${Object.values(commandCategories).flat().length}\n**Categorías disponibles:** ${Object.keys(commandCategories).length}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Agregar resumen de categorías de forma compacta
|
||||||
|
for (const [categoryName, commands] of Object.entries(commandCategories)) {
|
||||||
|
const commandsList = commands.map(cmd => `\`${cmd.name}\``).join(", ");
|
||||||
|
helpPanel.components.push({
|
||||||
|
type: 10,
|
||||||
|
content: `🔹 **${categoryName}** (${commands.length})\n${commandsList}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Botones de navegación
|
||||||
|
const navigationRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 1,
|
||||||
|
label: "🤝 Alianzas",
|
||||||
|
custom_id: "category_alliances"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "🌐 Red",
|
||||||
|
custom_id: "category_network"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "⚙️ Config",
|
||||||
|
custom_id: "category_settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 3,
|
||||||
|
label: "📋 Exportar",
|
||||||
|
custom_id: "export_commands"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const panelMessage = await message.reply({
|
||||||
|
flags: 32768,
|
||||||
|
components: [helpPanel, navigationRow]
|
||||||
|
});
|
||||||
|
|
||||||
|
const collector = panelMessage.createMessageComponentCollector({
|
||||||
|
time: 600000,
|
||||||
|
filter: (i: any) => i.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("collect", async (interaction: any) => {
|
||||||
|
// Manejar información específica de comandos
|
||||||
|
if (interaction.customId.startsWith("cmd_info_")) {
|
||||||
|
const commandName = interaction.customId.replace("cmd_info_", "");
|
||||||
|
let foundCommand = null;
|
||||||
|
let foundCategory = null;
|
||||||
|
|
||||||
|
for (const [category, commands] of Object.entries(commandCategories)) {
|
||||||
|
const command = commands.find(cmd => cmd.name === commandName);
|
||||||
|
if (command) {
|
||||||
|
foundCommand = command;
|
||||||
|
foundCategory = category;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundCommand) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: `📖 **${foundCommand.name}**\n\n**Categoría:** ${foundCategory}\n**Descripción:** ${foundCommand.description}\n**Uso:** ${foundCommand.usage}\n**Aliases:** ${foundCommand.aliases.join(", ")}\n\n💡 **Tip:** Usa \`${foundCommand.usage}\` para ejecutar este comando.`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manejar categorías específicas - VERSIÓN COMPACTA
|
||||||
|
switch (interaction.customId) {
|
||||||
|
case "category_alliances":
|
||||||
|
const alliancePanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x00ff88,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### 🤝 **Comandos de Alianzas**\n\nSistema completo para gestionar alianzas entre servidores:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Agregar comandos de forma compacta
|
||||||
|
commandCategories["Alianzas"].forEach(cmd => {
|
||||||
|
alliancePanel.components.push({
|
||||||
|
type: 10,
|
||||||
|
content: `**${cmd.name}**\n${cmd.description}\n\`${cmd.usage}\``
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [alliancePanel, backRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "category_network":
|
||||||
|
const networkPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x5865f2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### 🌐 **Comandos de Red**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
commandCategories["Red"].forEach(cmd => {
|
||||||
|
networkPanel.components.push({
|
||||||
|
type: 10,
|
||||||
|
content: `**${cmd.name}**\n${cmd.description}\n\`${cmd.usage}\``
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [networkPanel, backRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "category_settings":
|
||||||
|
const settingsPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0xff9500,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### ⚙️ **Comandos de Configuración**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
commandCategories["Configuracion"].forEach(cmd => {
|
||||||
|
settingsPanel.components.push({
|
||||||
|
type: 10,
|
||||||
|
content: `**${cmd.name}**\n${cmd.description}\n\`${cmd.usage}\``
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [settingsPanel, backRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "back_to_main":
|
||||||
|
await interaction.update({
|
||||||
|
components: [helpPanel, navigationRow]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "export_commands":
|
||||||
|
let exportText = `📋 **Lista Completa de Comandos - ${message.guild!.name}**\n\n`;
|
||||||
|
exportText += `**Prefix:** ${prefix}\n\n`;
|
||||||
|
|
||||||
|
for (const [category, commands] of Object.entries(commandCategories)) {
|
||||||
|
exportText += `**${category}**\n`;
|
||||||
|
commands.forEach(cmd => {
|
||||||
|
exportText += `• ${cmd.name} - ${cmd.description}\n`;
|
||||||
|
exportText += ` Uso: ${cmd.usage}\n`;
|
||||||
|
if (cmd.aliases.length > 0) {
|
||||||
|
exportText += ` Aliases: ${cmd.aliases.join(", ")}\n`;
|
||||||
|
}
|
||||||
|
exportText += `\n`;
|
||||||
|
});
|
||||||
|
exportText += `\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: `\`\`\`\n${exportText}\`\`\``,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (interaction.customId.startsWith("use_")) {
|
||||||
|
const commandName = interaction.customId.replace("use_", "");
|
||||||
|
const foundCmd = Object.values(commandCategories).flat().find(cmd => cmd.name === commandName);
|
||||||
|
|
||||||
|
if (foundCmd) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: `🚀 **Ejecutar: \`${foundCmd.name}\`**\n\nUsa: \`${foundCmd.usage}\`\n\n💡 **Tip:** Copia y pega el comando en el chat para usarlo.`,
|
||||||
|
flags: 64
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("end", async (collected: any, reason: string) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
const timeoutPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 0x36393f,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `### ⏰ **Panel de Ayuda Expirado**\n\nEl panel de ayuda ha expirado por inactividad.\n\nUsa \`${prefix}ayuda\` para abrir un nuevo panel.`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await panelMessage.edit({
|
||||||
|
components: [timeoutPanel]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Mensaje eliminado o error de edición
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,51 +1,351 @@
|
|||||||
import { CommandMessage } from "../../../core/types/commands";
|
import { CommandMessage } from "../../../core/types/commands";
|
||||||
//@ts-ignore
|
|
||||||
import {
|
|
||||||
ButtonStyle, ChannelType,
|
|
||||||
ContainerBuilder,
|
|
||||||
MessageFlags,
|
|
||||||
SectionBuilder, SeparatorBuilder, SeparatorSpacingSize, TextChannel,
|
|
||||||
TextDisplayBuilder,
|
|
||||||
UserSelectMenuBuilder
|
|
||||||
} from "discord.js";
|
|
||||||
|
|
||||||
export const command: CommandMessage = {
|
export const command: CommandMessage = {
|
||||||
name: 'settings',
|
name: 'configuracion',
|
||||||
type: "message",
|
type: "message",
|
||||||
aliases: ['options', 'stts'],
|
aliases: ['config', 'ajustes', 'settings'],
|
||||||
cooldown: 5,
|
cooldown: 5,
|
||||||
run: async (message, args, client) => {
|
run: async (message, args, client) => {
|
||||||
const server = await client.prisma.guild.findFirst({ where: { id: message.guild!.id } });
|
if (!message.member?.permissions.has("Administrator")) {
|
||||||
const title = new TextDisplayBuilder()
|
await message.reply("❌ No tienes permisos de Administrador.");
|
||||||
.setContent("## ﹒⌒ Settings Seɾveɾ ╰୧﹒")
|
return;
|
||||||
const description = new TextDisplayBuilder()
|
}
|
||||||
.setContent("Panel de Administracion del bot dentro del servidor.")
|
|
||||||
const sect = new TextDisplayBuilder()
|
|
||||||
.setContent("**Prefix del bot:** " + ` \`\`\`${server.prefix}\`\`\``)
|
|
||||||
|
|
||||||
const section = new SectionBuilder()
|
const server = await client.prisma.guild.findFirst({
|
||||||
.addTextDisplayComponents(sect)
|
where: { id: message.guild!.id }
|
||||||
//@ts-ignore
|
});
|
||||||
.setButtonAccessory(button => button
|
|
||||||
.setCustomId('prefixsettings')
|
|
||||||
.setLabel('Prefix')
|
|
||||||
.setStyle(ButtonStyle.Primary),
|
|
||||||
)
|
|
||||||
|
|
||||||
const separator = new SeparatorBuilder()
|
const currentPrefix = server?.prefix || "!";
|
||||||
.setSpacing(SeparatorSpacingSize.Large)
|
|
||||||
.setDivider(false);
|
|
||||||
|
|
||||||
const main = new ContainerBuilder()
|
// Panel de configuración usando DisplayComponents
|
||||||
.addTextDisplayComponents(title, description)
|
const settingsPanel = {
|
||||||
.addSeparatorComponents(separator)
|
type: 17,
|
||||||
.addSectionComponents(section)
|
accent_color: 6178018, // Color del ejemplo
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### <:invisible:1418684224441028608> 梅,panel admin,📢\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 1,
|
||||||
|
divider: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "Configuracion del Servidor:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9, // Section
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `**Prefix:**<:invisible:1418684224441028608>\`${currentPrefix}\``
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2, // Button
|
||||||
|
style: 2, // Secondary
|
||||||
|
emoji: {
|
||||||
|
name: "⚙️"
|
||||||
|
},
|
||||||
|
custom_id: "open_prefix_modal",
|
||||||
|
label: "Cambiar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const panelMessage = await message.reply({
|
||||||
|
flags: 32768, // SuppressEmbeds
|
||||||
|
components: [settingsPanel]
|
||||||
|
});
|
||||||
|
|
||||||
//@ts-ignore
|
const collector = panelMessage.createMessageComponentCollector({
|
||||||
if (message.channel.type === ChannelType.GuildText) {
|
time: 300000, // 5 minutos
|
||||||
const channel = message.channel as TextChannel;
|
filter: (i: any) => i.user.id === message.author.id
|
||||||
await channel.send({ components: [main], flags: MessageFlags.IsComponentsV2});
|
});
|
||||||
|
|
||||||
|
collector.on("collect", async (interaction: any) => {
|
||||||
|
if (interaction.customId === "open_prefix_modal") {
|
||||||
|
// Crear y mostrar modal para cambiar prefix
|
||||||
|
const prefixModal = {
|
||||||
|
title: "⚙️ Configurar Prefix del Servidor",
|
||||||
|
custom_id: "prefix_settings_modal",
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 1, // ActionRow
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 4, // TextInput
|
||||||
|
custom_id: "new_prefix_input",
|
||||||
|
label: "Nuevo Prefix",
|
||||||
|
style: 1, // Short
|
||||||
|
placeholder: `Prefix actual: ${currentPrefix}`,
|
||||||
|
required: true,
|
||||||
|
max_length: 10,
|
||||||
|
min_length: 1,
|
||||||
|
value: currentPrefix
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 4,
|
||||||
|
custom_id: "prefix_description",
|
||||||
|
label: "¿Por qué cambiar el prefix? (Opcional)",
|
||||||
|
style: 2, // Paragraph
|
||||||
|
placeholder: "Ej: Evitar conflictos con otros bots...",
|
||||||
|
required: false,
|
||||||
|
max_length: 200
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.showModal(prefixModal);
|
||||||
|
|
||||||
|
// Crear un collector específico para este modal
|
||||||
|
const modalCollector = interaction.awaitModalSubmit({
|
||||||
|
time: 300000, // 5 minutos
|
||||||
|
filter: (modalInt: any) => modalInt.customId === "prefix_settings_modal" && modalInt.user.id === message.author.id
|
||||||
|
});
|
||||||
|
|
||||||
|
modalCollector.then(async (modalInteraction: any) => {
|
||||||
|
const newPrefix = modalInteraction.fields.getTextInputValue("new_prefix_input");
|
||||||
|
const description = modalInteraction.fields.getTextInputValue("prefix_description") || "Sin descripción";
|
||||||
|
|
||||||
|
// Validar prefix
|
||||||
|
if (!newPrefix || newPrefix.length > 10) {
|
||||||
|
await modalInteraction.reply({
|
||||||
|
content: "❌ **Error:** El prefix debe tener entre 1 y 10 caracteres.",
|
||||||
|
flags: 64 // Ephemeral
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Actualizar prefix en la base de datos
|
||||||
|
await client.prisma.guild.upsert({
|
||||||
|
where: { id: message.guild!.id },
|
||||||
|
create: {
|
||||||
|
id: message.guild!.id,
|
||||||
|
name: message.guild!.name,
|
||||||
|
prefix: newPrefix
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
prefix: newPrefix,
|
||||||
|
name: message.guild!.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Panel de confirmación
|
||||||
|
const successPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 3066993, // Verde
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### ✅ **Prefix Actualizado Exitosamente**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `**Prefix anterior:** \`${currentPrefix}\`\n**Prefix nuevo:** \`${newPrefix}\`\n\n**Motivo:** ${description}`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 3, // Success
|
||||||
|
label: "✓ Listo",
|
||||||
|
custom_id: "prefix_confirmed",
|
||||||
|
emoji: { name: "✅" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 1,
|
||||||
|
divider: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "🚀 **¡Listo!** Ahora puedes usar los comandos con el nuevo prefix.\n\n💡 **Ejemplo:** `" + newPrefix + "help`, `" + newPrefix + "embedlist`"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Botón para volver al panel principal
|
||||||
|
const backToSettingsRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2, // Secondary
|
||||||
|
label: "↩️ Volver a Configuración",
|
||||||
|
custom_id: "back_to_settings"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Actualizar el panel original
|
||||||
|
await modalInteraction.update({
|
||||||
|
components: [successPanel, backToSettingsRow]
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const errorPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 15548997, // Rojo
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### ❌ **Error al Actualizar Prefix**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 2,
|
||||||
|
divider: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `**Error:** No se pudo actualizar el prefix a \`${newPrefix}\`\n\n**Posibles causas:**\n• Error de conexión con la base de datos\n• Prefix contiene caracteres no válidos\n• Permisos insuficientes\n\n🔄 **Solución:** Intenta nuevamente con un prefix diferente.`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const retryRow = {
|
||||||
|
type: 1,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
label: "🔄 Reintentar",
|
||||||
|
custom_id: "open_prefix_modal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
style: 4, // Danger
|
||||||
|
label: "❌ Cancelar",
|
||||||
|
custom_id: "cancel_prefix_change"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await modalInteraction.update({
|
||||||
|
components: [errorPanel, retryRow]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(async (error: any) => {
|
||||||
|
// Modal timeout o cancelado
|
||||||
|
console.log("Modal timeout o error:", error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manejar botones adicionales
|
||||||
|
if (interaction.customId === "back_to_settings") {
|
||||||
|
// Volver al panel principal
|
||||||
|
const updatedServer = await client.prisma.guild.findFirst({
|
||||||
|
where: { id: message.guild!.id }
|
||||||
|
});
|
||||||
|
const newCurrentPrefix = updatedServer?.prefix || "!";
|
||||||
|
|
||||||
|
const updatedSettingsPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 6178018,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### <:invisible:1418684224441028608> 梅,panel admin,📢\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 1,
|
||||||
|
divider: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "Configuracion del Servidor:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 9,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: `**Prefix:** \`${newCurrentPrefix}\``
|
||||||
|
}
|
||||||
|
],
|
||||||
|
accessory: {
|
||||||
|
type: 2,
|
||||||
|
style: 2,
|
||||||
|
emoji: { name: "⚙️" },
|
||||||
|
custom_id: "open_prefix_modal",
|
||||||
|
label: "Cambiar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
divider: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await interaction.update({
|
||||||
|
components: [updatedSettingsPanel]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interaction.customId === "cancel_prefix_change") {
|
||||||
|
// Volver al panel original sin cambios
|
||||||
|
await interaction.update({
|
||||||
|
components: [settingsPanel]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collector.on("end", async (collected: any, reason: string) => {
|
||||||
|
if (reason === "time") {
|
||||||
|
const timeoutPanel = {
|
||||||
|
type: 17,
|
||||||
|
accent_color: 6178018,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "### ⏰ **Panel Expirado**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 14,
|
||||||
|
spacing: 1,
|
||||||
|
divider: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 10,
|
||||||
|
content: "El panel de configuración ha expirado por inactividad.\n\nUsa `!settings` para abrir un nuevo panel."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await panelMessage.edit({
|
||||||
|
components: [timeoutPanel]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// Mensaje eliminado o error de edición
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ export async function replaceVars(text: string, user: User | undefined, guild: G
|
|||||||
|
|
||||||
// Crear inviteObject solo si invite existe y tiene guild
|
// Crear inviteObject solo si invite existe y tiene guild
|
||||||
const inviteObject = invite?.guild ? {
|
const inviteObject = invite?.guild ? {
|
||||||
guild: {
|
name: invite.guild.name,
|
||||||
//@ts-ignore
|
icon: invite.guild.icon ? `https://cdn.discordapp.com/icons/${invite.guild.id}/${invite.guild.icon}.webp?size=256` : ''
|
||||||
icon: `https://cdn.discordapp.com/icons/${invite.guild.id}/${invite.guild.icon}.webp?size=256`
|
|
||||||
}
|
|
||||||
} : null;
|
} : null;
|
||||||
|
|
||||||
return text
|
return text
|
||||||
@@ -37,7 +35,6 @@ export async function replaceVars(text: string, user: User | undefined, guild: G
|
|||||||
/**
|
/**
|
||||||
* INVITE INFO
|
* INVITE INFO
|
||||||
*/
|
*/
|
||||||
.replace(/(invite\.name)/g, invite?.guild?.name ?? "")
|
.replace(/(invite\.name)/g, inviteObject?.name ?? "")
|
||||||
.replace(/(invite\.icon)/g, inviteObject?.guild.icon ?? '0')
|
.replace(/(invite\.icon)/g, inviteObject?.icon ?? '')
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -288,6 +288,7 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
|
|
||||||
// Añadir imagen de portada primero si existe
|
// Añadir imagen de portada primero si existe
|
||||||
if (config.coverImage && isValidUrl(config.coverImage)) {
|
if (config.coverImage && isValidUrl(config.coverImage)) {
|
||||||
|
// @ts-ignore
|
||||||
const processedCoverUrl = await replaceVars(config.coverImage, user, guild);
|
const processedCoverUrl = await replaceVars(config.coverImage, user, guild);
|
||||||
if (isValidUrl(processedCoverUrl)) {
|
if (isValidUrl(processedCoverUrl)) {
|
||||||
previewComponents.push({
|
previewComponents.push({
|
||||||
@@ -301,6 +302,7 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
if (config.title) {
|
if (config.title) {
|
||||||
previewComponents.push({
|
previewComponents.push({
|
||||||
type: 10,
|
type: 10,
|
||||||
|
// @ts-ignore
|
||||||
content: await replaceVars(config.title, user, guild)
|
content: await replaceVars(config.title, user, guild)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -310,6 +312,7 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
for (const c of config.components) {
|
for (const c of config.components) {
|
||||||
if (c.type === 10) {
|
if (c.type === 10) {
|
||||||
// Componente de texto con thumbnail opcional
|
// Componente de texto con thumbnail opcional
|
||||||
|
// @ts-ignore
|
||||||
const processedThumbnail = c.thumbnail ? await replaceVars(c.thumbnail, user, guild) : null;
|
const processedThumbnail = c.thumbnail ? await replaceVars(c.thumbnail, user, guild) : null;
|
||||||
|
|
||||||
if (processedThumbnail && isValidUrl(processedThumbnail)) {
|
if (processedThumbnail && isValidUrl(processedThumbnail)) {
|
||||||
@@ -319,6 +322,7 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
type: 10,
|
type: 10,
|
||||||
|
// @ts-ignore
|
||||||
content: await replaceVars(c.content || " ", user, guild)
|
content: await replaceVars(c.content || " ", user, guild)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -331,6 +335,7 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
// Sin thumbnail o thumbnail inválido, componente normal
|
// Sin thumbnail o thumbnail inválido, componente normal
|
||||||
previewComponents.push({
|
previewComponents.push({
|
||||||
type: 10,
|
type: 10,
|
||||||
|
// @ts-ignore
|
||||||
content: await replaceVars(c.content || " ", user, guild)
|
content: await replaceVars(c.content || " ", user, guild)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -343,6 +348,7 @@ async function convertConfigToDisplayComponent(config: any, user: any, guild: an
|
|||||||
});
|
});
|
||||||
} else if (c.type === 12) {
|
} else if (c.type === 12) {
|
||||||
// Imagen - validar URL también
|
// Imagen - validar URL también
|
||||||
|
// @ts-ignore
|
||||||
const processedImageUrl = await replaceVars(c.url, user, guild);
|
const processedImageUrl = await replaceVars(c.url, user, guild);
|
||||||
|
|
||||||
if (isValidUrl(processedImageUrl)) {
|
if (isValidUrl(processedImageUrl)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user