Version Estable
This commit is contained in:
292
src/commands/messages/alliaces/createEmbed.ts
Normal file
292
src/commands/messages/alliaces/createEmbed.ts
Normal file
@@ -0,0 +1,292 @@
|
||||
import { CommandMessage } from "../../../core/types/commands";
|
||||
// @ts-ignore
|
||||
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, TextChannel, ChannelType } from "discord.js";
|
||||
//@ts-ignore
|
||||
import { ButtonStyle, ComponentType } from "discord.js";
|
||||
import { replaceVars } from "../../../core/lib/vars";
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: "embedcreate",
|
||||
type: "message",
|
||||
aliases: ["crearembed", "newembed"],
|
||||
cooldown: 20,
|
||||
// @ts-ignore
|
||||
run: async (message, args, client) => {
|
||||
if (!message.member?.permissions.has("Administrator")) {
|
||||
return message.reply("❌ No tienes permisos de Administrador.");
|
||||
}
|
||||
|
||||
const embedName: string | null = args[0] ?? null;
|
||||
if (!embedName) {
|
||||
return message.reply(
|
||||
"Debes proporcionar un nombre para el embed. Uso: `!embedcreate <nombre>`"
|
||||
);
|
||||
}
|
||||
|
||||
const nameIsValid = await client.prisma.embedConfig.findFirst({ where: {
|
||||
//@ts-ignore
|
||||
guildId: message.guild.id,
|
||||
name: embedName
|
||||
}})
|
||||
if(nameIsValid) return message.reply("❌ Nombre del embed ya fue tomado!")
|
||||
|
||||
// 📌 Estado independiente
|
||||
let embedState: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
color?: number;
|
||||
footer?: string;
|
||||
} = {
|
||||
title: `Editor de Embed: ${embedName}`,
|
||||
description:
|
||||
"Usa los botones de abajo para configurar este embed.\n\n_Ejemplo de variable: `{user.name}`_",
|
||||
color: 0x5865f2,
|
||||
footer: "Haz clic en Guardar cuando termines.",
|
||||
};
|
||||
|
||||
// 📌 Función para construir un embed a partir del estado
|
||||
const renderPreview = async () => {
|
||||
const preview = new EmbedBuilder()
|
||||
.setColor(embedState.color ?? 0x5865f2);
|
||||
|
||||
if (embedState.title)
|
||||
preview.setTitle(
|
||||
//@ts-ignore
|
||||
await replaceVars(embedState.title, message.member)
|
||||
);
|
||||
if (embedState.description)
|
||||
preview.setDescription(
|
||||
//@ts-ignore
|
||||
await replaceVars(embedState.description, message.member)
|
||||
);
|
||||
if (embedState.footer)
|
||||
preview.setFooter({
|
||||
//@ts-ignore
|
||||
text: await replaceVars(embedState.footer, message.member),
|
||||
});
|
||||
|
||||
return preview;
|
||||
};
|
||||
|
||||
// 📌 Botones
|
||||
const generateButtonRows = (disabled = false) => {
|
||||
const primaryRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_title")
|
||||
.setLabel("Título")
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(disabled),
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_description")
|
||||
.setLabel("Descripción")
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(disabled),
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_color")
|
||||
.setLabel("Color")
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(disabled)
|
||||
);
|
||||
|
||||
const secondaryRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_footer")
|
||||
.setLabel("Footer")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setDisabled(disabled)
|
||||
);
|
||||
|
||||
const controlRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("save_embed")
|
||||
.setLabel("Guardar")
|
||||
.setStyle(ButtonStyle.Success)
|
||||
.setDisabled(disabled),
|
||||
new ButtonBuilder()
|
||||
.setCustomId("cancel_embed")
|
||||
.setLabel("Cancelar")
|
||||
.setStyle(ButtonStyle.Danger)
|
||||
.setDisabled(disabled)
|
||||
);
|
||||
|
||||
return [primaryRow, secondaryRow, controlRow];
|
||||
};
|
||||
|
||||
if (message.channel.type === ChannelType.GuildText) {
|
||||
const channel = message.channel as TextChannel;
|
||||
|
||||
const editorMessage = await channel.send({
|
||||
embeds: [await renderPreview()],
|
||||
components: generateButtonRows(),
|
||||
});
|
||||
|
||||
const collector = editorMessage.createMessageComponentCollector({
|
||||
componentType: ComponentType.Button,
|
||||
time: 300000,
|
||||
});
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
if (i.user.id !== message.author.id) {
|
||||
await i.reply({
|
||||
content: "No puedes usar este menú.",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await i.deferUpdate();
|
||||
await editorMessage.edit({ components: generateButtonRows(true) });
|
||||
|
||||
// Guardar
|
||||
if (i.customId === "save_embed") {
|
||||
try {
|
||||
const dataForDb = {
|
||||
title: embedState.title,
|
||||
description: embedState.description,
|
||||
color: embedState.color ? `#${embedState.color.toString(16).padStart(6, '0')}` : null,
|
||||
footerText: embedState.footer,
|
||||
};
|
||||
|
||||
await client.prisma.embedConfig.upsert({
|
||||
where: {
|
||||
guildId_name: {
|
||||
guildId: message.guildId!,
|
||||
name: embedName,
|
||||
},
|
||||
},
|
||||
update: dataForDb,
|
||||
create: {
|
||||
name: embedName,
|
||||
...dataForDb,
|
||||
// ✅ ESTA ES LA SOLUCIÓN:
|
||||
// Le decimos a Prisma que se conecte al Guild o lo cree si no existe.
|
||||
guild: {
|
||||
connectOrCreate: {
|
||||
where: { id: message.guildId! },
|
||||
create: {
|
||||
id: message.guildId!,
|
||||
name: message.guild!.name, // Asegura que el nombre del servidor se guarde
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const saved = new EmbedBuilder()
|
||||
.setColor(0x00ff00)
|
||||
.setTitle(`✅ Guardado: ${embedName}`)
|
||||
.setDescription("La configuración se guardó en la base de datos.");
|
||||
|
||||
await editorMessage.edit({
|
||||
embeds: [saved],
|
||||
components: [],
|
||||
});
|
||||
} catch (e) {
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error al Guardar")
|
||||
.setDescription("No se pudo guardar en la base de datos. Revisa la consola.");
|
||||
await editorMessage.edit({
|
||||
embeds: [errorEmbed],
|
||||
components: [],
|
||||
});
|
||||
console.error("Error de Prisma al guardar el embed:", e);
|
||||
}
|
||||
collector.stop();
|
||||
return;
|
||||
}
|
||||
// Cancelar
|
||||
if (i.customId === "cancel_embed") {
|
||||
await editorMessage.delete();
|
||||
collector.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Edición
|
||||
let promptContent = "";
|
||||
let fieldToEdit: "title" | "description" | "color" | "footer" | null =
|
||||
null;
|
||||
|
||||
switch (i.customId) {
|
||||
case "edit_title":
|
||||
promptContent =
|
||||
"Escribe el nuevo **título** (puedes usar variables como `{user.name}`).";
|
||||
fieldToEdit = "title";
|
||||
break;
|
||||
case "edit_description":
|
||||
promptContent =
|
||||
"Escribe la nueva **descripción** (puedes usar variables).";
|
||||
fieldToEdit = "description";
|
||||
break;
|
||||
case "edit_color":
|
||||
promptContent =
|
||||
"Escribe el nuevo **color** en formato hexadecimal (ej: `#FF0000`).";
|
||||
fieldToEdit = "color";
|
||||
break;
|
||||
case "edit_footer":
|
||||
promptContent =
|
||||
"Escribe el nuevo **texto del footer** (puedes usar variables).";
|
||||
fieldToEdit = "footer";
|
||||
break;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const promptMessage = await i.channel.send(promptContent);
|
||||
|
||||
//@ts-ignore
|
||||
const messageCollector = i.channel!.createMessageCollector({
|
||||
//@ts-ignore
|
||||
filter: (m: Message) => m.author.id === i.user.id,
|
||||
max: 1,
|
||||
time: 60000,
|
||||
});
|
||||
//@ts-ignore
|
||||
messageCollector.on("collect", async (collectedMessage) => {
|
||||
const newValue = collectedMessage.content;
|
||||
|
||||
if (fieldToEdit === "title") embedState.title = newValue;
|
||||
if (fieldToEdit === "description") embedState.description = newValue;
|
||||
if (fieldToEdit === "footer") embedState.footer = newValue;
|
||||
|
||||
if (fieldToEdit === "color") {
|
||||
try {
|
||||
const hex = newValue.replace("#", "");
|
||||
embedState.color = parseInt(hex, 16);
|
||||
} catch {
|
||||
embedState.color = 0x5865f2;
|
||||
}
|
||||
}
|
||||
|
||||
await collectedMessage.delete();
|
||||
await promptMessage.delete();
|
||||
|
||||
await editorMessage.edit({
|
||||
embeds: [await renderPreview()],
|
||||
components: generateButtonRows(false),
|
||||
});
|
||||
});
|
||||
//@ts-ignore
|
||||
messageCollector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
await promptMessage.delete();
|
||||
await editorMessage.edit({
|
||||
components: generateButtonRows(false),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
collector.on("end", async (_, reason) => {
|
||||
if (reason === "time") {
|
||||
const timeoutEmbed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("Editor finalizado por inactividad.");
|
||||
|
||||
await editorMessage.edit({
|
||||
embeds: [timeoutEmbed],
|
||||
components: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
276
src/commands/messages/alliaces/editEmbed.ts
Normal file
276
src/commands/messages/alliaces/editEmbed.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import { CommandMessage } from "../../../core/types/commands";
|
||||
// @ts-ignore
|
||||
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, TextChannel, ChannelType } from "discord.js";
|
||||
//@ts-ignore
|
||||
import { ButtonStyle, ComponentType } from "discord.js";
|
||||
import { replaceVars } from "../../../core/lib/vars";
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: "editembed",
|
||||
type: "message",
|
||||
aliases: ["modembed", "updateembed"],
|
||||
cooldown: 20,
|
||||
// @ts-ignore
|
||||
run: async (message, args, client) => {
|
||||
if (!message.member?.permissions.has("Administrator")) {
|
||||
return message.reply("❌ No tienes permisos de Administrador.");
|
||||
}
|
||||
|
||||
const embedName: string | null = args[0] ?? null;
|
||||
if (!embedName) {
|
||||
return message.reply(
|
||||
"Debes proporcionar un nombre para el embed. Uso: `!editembed <nombre>`"
|
||||
);
|
||||
}
|
||||
|
||||
// 📌 Buscar en la base de datos
|
||||
const existing = await client.prisma.embedConfig.findUnique({
|
||||
where: {
|
||||
guildId_name: {
|
||||
guildId: message.guildId!,
|
||||
name: embedName,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
return message.reply("❌ No encontré un embed con ese nombre.");
|
||||
}
|
||||
|
||||
// 📌 Estado inicial desde DB
|
||||
let embedState: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
color?: number;
|
||||
footer?: string;
|
||||
} = {
|
||||
title: existing.title ?? undefined,
|
||||
description: existing.description ?? undefined,
|
||||
color: existing.color ? parseInt(existing.color.replace("#", ""), 16) : 0x5865f2,
|
||||
footer: existing.footerText ?? undefined,
|
||||
};
|
||||
|
||||
// 📌 Función para renderizar preview
|
||||
const renderPreview = async () => {
|
||||
const preview = new EmbedBuilder().setColor(embedState.color ?? 0x5865f2);
|
||||
|
||||
if (embedState.title)
|
||||
//@ts-ignore
|
||||
preview.setTitle(await replaceVars(embedState.title, message.member));
|
||||
if (embedState.description)
|
||||
//@ts-ignore
|
||||
preview.setDescription(await replaceVars(embedState.description, message.member));
|
||||
if (embedState.footer)
|
||||
preview.setFooter({
|
||||
//@ts-ignore
|
||||
text: await replaceVars(embedState.footer, message.member),
|
||||
});
|
||||
|
||||
return preview;
|
||||
};
|
||||
|
||||
const generateButtonRows = (disabled = false) => {
|
||||
const primaryRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_title")
|
||||
.setLabel("Título")
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(disabled),
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_description")
|
||||
.setLabel("Descripción")
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(disabled),
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_color")
|
||||
.setLabel("Color")
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(disabled)
|
||||
);
|
||||
|
||||
const secondaryRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("edit_footer")
|
||||
.setLabel("Footer")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setDisabled(disabled)
|
||||
);
|
||||
|
||||
const controlRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("save_embed")
|
||||
.setLabel("Guardar cambios")
|
||||
.setStyle(ButtonStyle.Success)
|
||||
.setDisabled(disabled),
|
||||
new ButtonBuilder()
|
||||
.setCustomId("cancel_embed")
|
||||
.setLabel("Cancelar")
|
||||
.setStyle(ButtonStyle.Danger)
|
||||
.setDisabled(disabled)
|
||||
);
|
||||
|
||||
return [primaryRow, secondaryRow, controlRow];
|
||||
};
|
||||
|
||||
if (message.channel.type === ChannelType.GuildText) {
|
||||
const channel = message.channel as TextChannel;
|
||||
|
||||
const editorMessage = await channel.send({
|
||||
embeds: [await renderPreview()],
|
||||
components: generateButtonRows(),
|
||||
});
|
||||
|
||||
const collector = editorMessage.createMessageComponentCollector({
|
||||
componentType: ComponentType.Button,
|
||||
time: 300000,
|
||||
});
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
if (i.user.id !== message.author.id) {
|
||||
await i.reply({
|
||||
content: "No puedes usar este menú.",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await i.deferUpdate();
|
||||
await editorMessage.edit({ components: generateButtonRows(true) });
|
||||
|
||||
// Guardar cambios
|
||||
if (i.customId === "save_embed") {
|
||||
try {
|
||||
const dataForDb = {
|
||||
title: embedState.title,
|
||||
description: embedState.description,
|
||||
color: embedState.color ? `#${embedState.color.toString(16).padStart(6, '0')}` : null,
|
||||
footerText: embedState.footer,
|
||||
};
|
||||
|
||||
await client.prisma.embedConfig.update({
|
||||
where: {
|
||||
guildId_name: {
|
||||
guildId: message.guildId!,
|
||||
name: embedName,
|
||||
},
|
||||
},
|
||||
data: dataForDb,
|
||||
});
|
||||
|
||||
const saved = new EmbedBuilder()
|
||||
.setColor(0x00ff00)
|
||||
.setTitle(`✅ Actualizado: ${embedName}`)
|
||||
.setDescription("Los cambios fueron guardados en la base de datos.");
|
||||
|
||||
await editorMessage.edit({
|
||||
embeds: [saved],
|
||||
components: [],
|
||||
});
|
||||
} catch (e) {
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error al Guardar")
|
||||
.setDescription("No se pudo guardar en la base de datos. Revisa la consola.");
|
||||
await editorMessage.edit({
|
||||
embeds: [errorEmbed],
|
||||
components: [],
|
||||
});
|
||||
console.error("Error de Prisma al actualizar el embed:", e);
|
||||
}
|
||||
collector.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancelar
|
||||
if (i.customId === "cancel_embed") {
|
||||
await editorMessage.delete();
|
||||
collector.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Edición
|
||||
let promptContent = "";
|
||||
let fieldToEdit: "title" | "description" | "color" | "footer" | null =
|
||||
null;
|
||||
|
||||
switch (i.customId) {
|
||||
case "edit_title":
|
||||
promptContent = "Escribe el nuevo **título** (puedes usar variables).";
|
||||
fieldToEdit = "title";
|
||||
break;
|
||||
case "edit_description":
|
||||
promptContent = "Escribe la nueva **descripción**.";
|
||||
fieldToEdit = "description";
|
||||
break;
|
||||
case "edit_color":
|
||||
promptContent = "Escribe el nuevo **color** en formato hexadecimal (ej: `#FF0000`).";
|
||||
fieldToEdit = "color";
|
||||
break;
|
||||
case "edit_footer":
|
||||
promptContent = "Escribe el nuevo **texto del footer**.";
|
||||
fieldToEdit = "footer";
|
||||
break;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const promptMessage = await i.channel.send(promptContent);
|
||||
|
||||
//@ts-ignore
|
||||
const messageCollector = i.channel!.createMessageCollector({
|
||||
//@ts-ignore
|
||||
filter: (m: Message) => m.author.id === i.user.id,
|
||||
max: 1,
|
||||
time: 60000,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
messageCollector.on("collect", async (collectedMessage) => {
|
||||
const newValue = collectedMessage.content;
|
||||
|
||||
if (fieldToEdit === "title") embedState.title = newValue;
|
||||
if (fieldToEdit === "description") embedState.description = newValue;
|
||||
if (fieldToEdit === "footer") embedState.footer = newValue;
|
||||
|
||||
if (fieldToEdit === "color") {
|
||||
try {
|
||||
const hex = newValue.replace("#", "");
|
||||
embedState.color = parseInt(hex, 16);
|
||||
} catch {
|
||||
embedState.color = 0x5865f2;
|
||||
}
|
||||
}
|
||||
|
||||
await collectedMessage.delete();
|
||||
await promptMessage.delete();
|
||||
|
||||
await editorMessage.edit({
|
||||
embeds: [await renderPreview()],
|
||||
components: generateButtonRows(false),
|
||||
});
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
messageCollector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
await promptMessage.delete();
|
||||
await editorMessage.edit({
|
||||
components: generateButtonRows(false),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
collector.on("end", async (_, reason) => {
|
||||
if (reason === "time") {
|
||||
const timeoutEmbed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("Editor finalizado por inactividad.");
|
||||
|
||||
await editorMessage.edit({
|
||||
embeds: [timeoutEmbed],
|
||||
components: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
34
src/commands/messages/alliaces/embedDelete.ts
Normal file
34
src/commands/messages/alliaces/embedDelete.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { CommandMessage } from "../../../core/types/commands";
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: "embeddelete",
|
||||
type: "message",
|
||||
aliases: ["delembed", "removeembed"],
|
||||
cooldown: 10,
|
||||
//@ts-ignore
|
||||
run: async (message, args, client) => {
|
||||
if (!message.member?.permissions.has("Administrator")) {
|
||||
return message.reply("❌ No tienes permisos de Administrador.");
|
||||
}
|
||||
|
||||
const embedName = args[0];
|
||||
if (!embedName) {
|
||||
return message.reply("Debes proporcionar el nombre del embed a eliminar. Uso: `!embeddelete <nombre>`");
|
||||
}
|
||||
|
||||
try {
|
||||
await client.prisma.embedConfig.delete({
|
||||
where: {
|
||||
guildId_name: {
|
||||
guildId: message.guildId!,
|
||||
name: embedName,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return message.reply(`✅ El embed **${embedName}** fue eliminado con éxito.`);
|
||||
} catch {
|
||||
return message.reply("❌ No encontré un embed con ese nombre.");
|
||||
}
|
||||
},
|
||||
};
|
||||
75
src/commands/messages/alliaces/embedList.ts
Normal file
75
src/commands/messages/alliaces/embedList.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
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 = {
|
||||
name: "embedlist",
|
||||
type: "message",
|
||||
aliases: ["listembeds", "embeds"],
|
||||
cooldown: 10,
|
||||
//@ts-ignore
|
||||
run: async (message, args, client) => {
|
||||
if (!message.member?.permissions.has("Administrator")) {
|
||||
return message.reply("❌ No tienes permisos de Administrador.");
|
||||
}
|
||||
|
||||
const embeds = await client.prisma.embedConfig.findMany({
|
||||
where: { guildId: message.guildId! },
|
||||
});
|
||||
|
||||
if (embeds.length === 0) {
|
||||
return message.reply("📭 No hay ningún embed guardado en este servidor.");
|
||||
}
|
||||
|
||||
const title = new TextDisplayBuilder()
|
||||
.setContent('﹒⌒ Embed List ╰୧﹒');
|
||||
|
||||
// Combina la lista de embeds en la misma sección que la miniatura
|
||||
// para un mejor diseño.
|
||||
//@ts-ignore
|
||||
const embedListContent = embeds.map((e, i) => `**${i + 1}.** ${e.name}`).join("\n");
|
||||
|
||||
// Obtenemos la URL del icono de forma segura
|
||||
const guildIconURL = message.guild?.iconURL({ forceStatic: false });
|
||||
|
||||
// Creamos la sección que contendrá el texto Y la miniatura
|
||||
const mainSection = new SectionBuilder()
|
||||
.addTextDisplayComponents(text => text.setContent(embedListContent)); // <--- Componente principal requerido
|
||||
|
||||
// Solo añadimos la miniatura si la URL existe
|
||||
if (guildIconURL) {
|
||||
//@ts-ignore
|
||||
mainSection.setThumbnailAccessory(thumbnail => thumbnail
|
||||
.setURL(guildIconURL)
|
||||
.setDescription('Icono del servidor')
|
||||
);
|
||||
}
|
||||
|
||||
const separator = new SeparatorBuilder()
|
||||
.setSpacing(SeparatorSpacingSize.Large)
|
||||
.setDivider(false);
|
||||
|
||||
const container = new ContainerBuilder()
|
||||
.setAccentColor(0x49225B)
|
||||
.addTextDisplayComponents(title)
|
||||
.addSeparatorComponents(separator)
|
||||
.addSectionComponents(mainSection); // <--- Añadimos la sección ya completa
|
||||
|
||||
|
||||
if (message.channel.type === ChannelType.GuildText) {
|
||||
const channel = message.channel as TextChannel;
|
||||
await channel.send({ components: [container], flags: MessageFlags.IsComponentsV2});
|
||||
}
|
||||
},
|
||||
};
|
||||
11
src/commands/messages/net/ping.ts
Normal file
11
src/commands/messages/net/ping.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {CommandMessage} from "../../../core/types/commands";
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'ping',
|
||||
type: "message",
|
||||
aliases: ['latency', 'pong'],
|
||||
cooldown: 5,
|
||||
run: async (message, args) => {
|
||||
await message.reply('pong!')
|
||||
}
|
||||
}
|
||||
45
src/commands/messages/others/embedtested.ts
Normal file
45
src/commands/messages/others/embedtested.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import {CommandMessage} from "../../../core/types/commands";
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: 'test1',
|
||||
type: "message",
|
||||
cooldown: 5,
|
||||
run: async (message, args) => {
|
||||
//@ts-ignore
|
||||
await message.channel.send({
|
||||
"flags": 32768,
|
||||
"components": [
|
||||
{
|
||||
"type": 17,
|
||||
"components": [
|
||||
{
|
||||
"type": 10,
|
||||
"content": "## ﹒⌒ 🌹 Navegacion 🌹 ╰୧﹒"
|
||||
},
|
||||
{
|
||||
"type": 14,
|
||||
"spacing": 2,
|
||||
"divider": false
|
||||
},
|
||||
{
|
||||
"type": 9,
|
||||
"components": [
|
||||
{
|
||||
"type": 10,
|
||||
"content": "### Reglas dentro del Servidor"
|
||||
}
|
||||
],
|
||||
"accessory": {
|
||||
"style": 2,
|
||||
"type": 5,
|
||||
"label": "Ver",
|
||||
"url": "https://discord.com/channels/1316592320954630144/1417682278762676264/1417901305434734656",
|
||||
}
|
||||
}
|
||||
],
|
||||
"accent_color": 4393549
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
51
src/commands/messages/settings-server/settings.ts
Normal file
51
src/commands/messages/settings-server/settings.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
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 = {
|
||||
name: 'settings',
|
||||
type: "message",
|
||||
aliases: ['options', 'stts'],
|
||||
cooldown: 5,
|
||||
run: async (message, args, client) => {
|
||||
const server = await client.prisma.guild.findFirst({ where: { id: message.guild!.id } });
|
||||
const title = new TextDisplayBuilder()
|
||||
.setContent("## ﹒⌒ Settings Seɾveɾ ╰୧﹒")
|
||||
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()
|
||||
.addTextDisplayComponents(sect)
|
||||
//@ts-ignore
|
||||
.setButtonAccessory(button => button
|
||||
.setCustomId('prefixsettings')
|
||||
.setLabel('Prefix')
|
||||
.setStyle(ButtonStyle.Primary),
|
||||
)
|
||||
|
||||
const separator = new SeparatorBuilder()
|
||||
.setSpacing(SeparatorSpacingSize.Large)
|
||||
.setDivider(false);
|
||||
|
||||
const main = new ContainerBuilder()
|
||||
.addTextDisplayComponents(title, description)
|
||||
.addSeparatorComponents(separator)
|
||||
.addSectionComponents(section)
|
||||
|
||||
|
||||
//@ts-ignore
|
||||
if (message.channel.type === ChannelType.GuildText) {
|
||||
const channel = message.channel as TextChannel;
|
||||
await channel.send({ components: [main], flags: MessageFlags.IsComponentsV2});
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/commands/splashcmd/net/ping.ts
Normal file
12
src/commands/splashcmd/net/ping.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {CommandSlash} from "../../../core/types/commands";
|
||||
|
||||
|
||||
export const command: CommandSlash = {
|
||||
name: 'ping',
|
||||
description: 'Ping',
|
||||
type: "slash",
|
||||
cooldown: 10,
|
||||
run: async (interaction, client) => {
|
||||
await interaction.reply('pong!')
|
||||
}
|
||||
}
|
||||
22
src/components/buttons/prefixSettings.ts
Normal file
22
src/components/buttons/prefixSettings.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type {ButtonInteraction} from "discord.js";
|
||||
//@ts-ignore
|
||||
import { ActionRowBuilder, Events, ModalBuilder, TextInputBuilder, TextInputStyle } from 'discord.js'
|
||||
|
||||
export default {
|
||||
customId: "prefixsettings",
|
||||
run: async(interaction: ButtonInteraction) => {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId('prefixsettingsmodal')
|
||||
.setTitle('Prefix');
|
||||
|
||||
const prefixInput = new TextInputBuilder()
|
||||
.setCustomId('prefixInput')
|
||||
.setLabel("Change Prefix")
|
||||
.setStyle(TextInputStyle.Short);
|
||||
|
||||
const secondActionRow = new ActionRowBuilder().addComponents(prefixInput);
|
||||
modal.addComponents(secondActionRow);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
}
|
||||
}
|
||||
10
src/components/modals/prefixSettingsModal.ts
Normal file
10
src/components/modals/prefixSettingsModal.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {ModalSubmitInteraction} from "discord.js";
|
||||
|
||||
export default {
|
||||
customId: "prefixsettingsmodal",
|
||||
run: async (interaction: ModalSubmitInteraction) => {
|
||||
const newPrefix = interaction.fields.getTextInputValue("prefixInput")
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
41
src/core/api/discordAPI.ts
Normal file
41
src/core/api/discordAPI.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { REST } from "discord.js";
|
||||
// @ts-ignore
|
||||
import { Routes } from "discord-api-types/v10";
|
||||
import { commands } from "../loader";
|
||||
|
||||
export async function registeringCommands(): Promise<void> {
|
||||
const commandsToRegister: any[] = [];
|
||||
|
||||
// Recorremos la Collection que ya cargó loadCommands()
|
||||
for (const [name, cmd] of commands) {
|
||||
if (cmd.type === "slash") {
|
||||
commandsToRegister.push({
|
||||
name: cmd.name,
|
||||
description: cmd.description ?? "Sin descripción",
|
||||
type: 1, // CHAT_INPUT
|
||||
options: cmd.options ?? []
|
||||
});
|
||||
|
||||
console.log(`✅ Preparado para registrar: ${cmd.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
const rest = new REST().setToken(process.env.TOKEN ?? "");
|
||||
|
||||
try {
|
||||
console.log(`🚀 Registrando ${commandsToRegister.length} comandos slash...`);
|
||||
|
||||
const data: any = await rest.put(
|
||||
Routes.applicationGuildCommands(
|
||||
process.env.CLIENT!,
|
||||
process.env.guildTest!
|
||||
),
|
||||
{ body: commandsToRegister }
|
||||
);
|
||||
|
||||
console.log(`✅ ${data.length} comandos registrados correctamente.`);
|
||||
} catch (error) {
|
||||
console.error("❌ Error registrando comandos:", error);
|
||||
}
|
||||
}
|
||||
|
||||
49
src/core/client.ts
Normal file
49
src/core/client.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
// @ts-ignore
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
// 1. Importa PrismaClient
|
||||
// @ts-ignore
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
process.loadEnvFile();
|
||||
|
||||
class Amayo extends Client {
|
||||
public key: string;
|
||||
// 2. Declara la propiedad prisma
|
||||
public prisma: PrismaClient;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.GuildMessageTyping
|
||||
],
|
||||
rest: {
|
||||
retries: 10
|
||||
}
|
||||
});
|
||||
|
||||
this.key = process.env.TOKEN ?? '';
|
||||
// 3. Instancia PrismaClient en el constructor
|
||||
this.prisma = new PrismaClient();
|
||||
}
|
||||
|
||||
async play () {
|
||||
if(!this.key) {
|
||||
return console.error('No key provided');
|
||||
} else {
|
||||
// Ejemplo de cómo usarías prisma antes de iniciar sesión
|
||||
try {
|
||||
await this.prisma.$connect();
|
||||
console.log('Successfully connected to the database.');
|
||||
await this.login(this.key);
|
||||
} catch (error) {
|
||||
console.error('Failed to connect to the database:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Amayo;
|
||||
49
src/core/components.ts
Normal file
49
src/core/components.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { Collection } from "discord.js";
|
||||
|
||||
export const buttons: Collection<string, any> = new Collection<string, any>();
|
||||
export const modals = new Collection<string, any>();
|
||||
export const selectmenus = new Collection<string, any>();
|
||||
export const contextmenus = new Collection<string, any>();
|
||||
|
||||
export function loadComponents(dir: string = path.join(__dirname, "..", "components")) {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
loadComponents(fullPath); // recursivo
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
|
||||
|
||||
const imported = require(fullPath);
|
||||
const component = imported.default ?? imported;
|
||||
|
||||
if (!component?.customId) {
|
||||
console.warn(`⚠️ Archivo ignorado: ${file} (no tiene "customId")`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detectamos el tipo según la carpeta en la que está
|
||||
if (fullPath.includes("buttons")) {
|
||||
buttons.set(component.customId, component);
|
||||
console.log(`🔘 Botón cargado: ${component.customId}`);
|
||||
} else if (fullPath.includes("modals")) {
|
||||
modals.set(component.customId, component);
|
||||
console.log(`📄 Modal cargado: ${component.customId}`);
|
||||
} else if (fullPath.includes("selectmenus")) {
|
||||
selectmenus.set(component.customId, component);
|
||||
console.log(`📜 SelectMenu cargado: ${component.customId}`);
|
||||
} else if (fullPath.includes("contextmenu")) {
|
||||
contextmenus.set(component.customId, component);
|
||||
console.log(`📑 ContextMenu cargado: ${component.customId}`);
|
||||
} else {
|
||||
console.log(`⚠️ Componente desconocido: ${component.customId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/core/lib/vars.ts
Normal file
12
src/core/lib/vars.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {Guild, User} from "discord.js";
|
||||
|
||||
export async function replaceVars(text: string, user: User | undefined, guild:
|
||||
Guild | undefined, stats: any) {
|
||||
if(!text) return;
|
||||
|
||||
return text
|
||||
.replace(/(user.name)/g, user!.username ?? '')
|
||||
.replace(/(user.id)/g, user!.id ?? '')
|
||||
.replace(/(user.mention)/g, `<@${user!.id}>`)
|
||||
.replace(/(user.avatar)/g, user!.displayAvatarURL({ forceStatic: false }))
|
||||
}
|
||||
43
src/core/loader.ts
Normal file
43
src/core/loader.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { Collection } from "discord.js";
|
||||
|
||||
export const commands = new Collection<string, any>();
|
||||
|
||||
export function loadCommands(dir: string = path.join(__dirname, '..', 'commands')) {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
loadCommands(fullPath); // recursivo
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!file.endsWith('.ts')) continue;
|
||||
|
||||
const imported = require(fullPath);
|
||||
const command = imported.command ?? imported.default ?? imported;
|
||||
|
||||
if (!command?.data?.name && !command?.name) {
|
||||
console.warn(`⚠️ Archivo ignorado: ${file} (no es un comando válido)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = command.data?.name ?? command.name;
|
||||
console.log(`📦 Loading command: ${name}`);
|
||||
|
||||
// @ts-ignore
|
||||
commands.set(name, command);
|
||||
|
||||
if (command.aliases?.length) {
|
||||
for (const alias of command.aliases) {
|
||||
commands.set(alias, command);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Cargado comando: ${name}`);
|
||||
}
|
||||
}
|
||||
32
src/core/loaderEvents.ts
Normal file
32
src/core/loaderEvents.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { bot } from "../main";
|
||||
import path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
|
||||
export function loadEvents(dir: string = path.join(__dirname, "../events")) {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
loadEvents(fullPath); // recursión para subcarpetas
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!file.endsWith(".ts") && !file.endsWith(".js")) continue;
|
||||
|
||||
const imported = require(fullPath);
|
||||
const event = imported.default ?? imported;
|
||||
|
||||
if (!event?.name || !event?.execute) continue;
|
||||
|
||||
if (event.once) {
|
||||
bot.once(event.name, (...args: any[]) => event.execute(...args));
|
||||
} else {
|
||||
bot.on(event.name, (...args: any[]) => event.execute(...args));
|
||||
}
|
||||
|
||||
console.log(`Evento cargado: ${event.name}`);
|
||||
}
|
||||
}
|
||||
13
src/core/redis.ts
Normal file
13
src/core/redis.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createClient } from "redis";
|
||||
|
||||
export const redis = createClient({
|
||||
url: process.env.REDIS_URL,
|
||||
})
|
||||
|
||||
redis.on("error", (err: any) => console.error("Redis error:", err));
|
||||
redis.on("connect", () => console.log("✅ Conectado a Redis"));
|
||||
redis.on("reconnecting", () => console.warn("♻️ Reintentando conexión Redis"));
|
||||
|
||||
export async function redisConnect () {
|
||||
if (!redis.isOpen) await redis.connect();
|
||||
}
|
||||
19
src/core/types/commands.ts
Normal file
19
src/core/types/commands.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type {ChatInputCommandInteraction, Client, Message} from "discord.js";
|
||||
import Amayo from "../client";
|
||||
|
||||
export interface CommandMessage {
|
||||
name: string;
|
||||
type: 'message';
|
||||
aliases?: string[];
|
||||
cooldown?: number;
|
||||
run: (message: Message, args: string[], client: Amayo) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface CommandSlash {
|
||||
name: string;
|
||||
description: string;
|
||||
type: 'slash';
|
||||
options?: string[];
|
||||
cooldown?: number;
|
||||
run: (i: ChatInputCommandInteraction, client: Client) => Promise<void>;
|
||||
}
|
||||
7
src/core/types/components.ts
Normal file
7
src/core/types/components.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type {ButtonInteraction} from "discord.js";
|
||||
|
||||
|
||||
export interface button {
|
||||
customId: string;
|
||||
run: (interaction: ButtonInteraction) => Promise<void>;
|
||||
}
|
||||
53
src/events/interactionCreate.ts
Normal file
53
src/events/interactionCreate.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { bot } from "../main";
|
||||
import type { BaseInteraction } from "discord.js";
|
||||
import { Events } from "discord.js";
|
||||
import { redis } from "../core/redis";
|
||||
import { commands } from "../core/loader";
|
||||
import { buttons, modals, selectmenus } from "../core/components";
|
||||
|
||||
bot.on(Events.InteractionCreate, async (interaction: BaseInteraction) => {
|
||||
try {
|
||||
// 🔹 Slash commands
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const cmd = commands.get(interaction.commandName);
|
||||
if (!cmd) return;
|
||||
|
||||
const cooldown = Math.floor(Number(cmd.cooldown) || 0);
|
||||
|
||||
if (cooldown > 0) {
|
||||
const key = `cooldown:${cmd.name}:${interaction.user.id}`;
|
||||
const ttl = await redis.ttl(key);
|
||||
if (ttl > 0) {
|
||||
return interaction.reply(`⏳ Espera ${ttl}s antes de volver a usar **${cmd.name}**.`);
|
||||
}
|
||||
await redis.set(key, "1", { EX: cooldown });
|
||||
}
|
||||
|
||||
await cmd.run(interaction, bot);
|
||||
}
|
||||
|
||||
// 🔹 Botones
|
||||
if (interaction.isButton()) {
|
||||
//@ts-ignore
|
||||
const btn = buttons.get(interaction.customId);
|
||||
if (btn) await btn.run(interaction, bot);
|
||||
}
|
||||
|
||||
// 🔹 Select menus
|
||||
if (interaction.isStringSelectMenu()) {
|
||||
const menu = selectmenus.get(interaction.customId);
|
||||
if (menu) await menu.run(interaction, bot);
|
||||
}
|
||||
|
||||
// 🔹 Modales
|
||||
if (interaction.isModalSubmit()) {
|
||||
const modal = modals.get(interaction.customId);
|
||||
if (modal) await modal.run(interaction, bot);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (interaction.isRepliable()) {
|
||||
await interaction.reply({ content: "❌ Hubo un error ejecutando la interacción.", ephemeral: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
40
src/events/messageCreate.ts
Normal file
40
src/events/messageCreate.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import {bot} from "../main";
|
||||
import {Events} from "discord.js";
|
||||
import {redis} from "../core/redis";
|
||||
import {commands} from "../core/loader";
|
||||
|
||||
|
||||
bot.on(Events.MessageCreate, async (message) => {
|
||||
if (message.author.bot) return;
|
||||
const server = await bot.prisma.guild.findFirst({ where: { id: message.guild!.id } }) || "!";
|
||||
const PREFIX = server.prefix
|
||||
if (!message.content.startsWith(PREFIX)) return;
|
||||
|
||||
const [cmdName, ...args] = message.content.slice(PREFIX.length).trim().split(/\s+/);
|
||||
console.log(cmdName);
|
||||
const command = commands.get(cmdName);
|
||||
if (!command) return;
|
||||
|
||||
const cooldown = Math.floor(Number(command.cooldown) || 0);
|
||||
|
||||
if (cooldown > 0) {
|
||||
const key = `cooldown:${command.name}:${message.author.id}`;
|
||||
const ttl = await redis.ttl(key);
|
||||
console.log(`Key: ${key}, TTL: ${ttl}`);
|
||||
|
||||
if (ttl > 0) {
|
||||
return message.reply(`⏳ Espera ${ttl}s antes de volver a usar **${command.name}**.`);
|
||||
}
|
||||
|
||||
// SET con expiración correcta para redis v4+
|
||||
await redis.set(key, "1", { EX: cooldown });
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await command.run(message, args, message.client);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await message.reply("❌ Hubo un error ejecutando el comando.");
|
||||
}
|
||||
})
|
||||
6
src/events/ready.ts
Normal file
6
src/events/ready.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {bot} from "../main";
|
||||
import {Events} from "discord.js";
|
||||
|
||||
bot.on(Events.ClientReady, () => {
|
||||
console.log("Ready!");
|
||||
})
|
||||
28
src/main.ts
Normal file
28
src/main.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import Amayo from "./core/client";
|
||||
import { loadCommands } from "./core/loader";
|
||||
import { loadEvents } from "./core/loaderEvents";
|
||||
import { redisConnect } from "./core/redis";
|
||||
import { registeringCommands } from "./core/api/discordAPI";
|
||||
import {loadComponents} from "./core/components";
|
||||
|
||||
export const bot = new Amayo();
|
||||
|
||||
async function bootstrap() {
|
||||
console.log("🚀 Iniciando bot...");
|
||||
|
||||
loadCommands(); // 1️⃣ Cargar comandos en la Collection
|
||||
loadComponents()
|
||||
loadEvents(); // 2️⃣ Cargar eventos
|
||||
|
||||
await registeringCommands(); // 3️⃣ Registrar los slash en Discord
|
||||
|
||||
await redisConnect(); // 4️⃣ Conectar Redis
|
||||
|
||||
await bot.play();
|
||||
console.log("✅ Bot conectado a Discord");
|
||||
}
|
||||
|
||||
bootstrap().catch((err) => {
|
||||
console.error("❌ Error en el arranque:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user