feat: update Copilot instructions for Discord.js 15.0.0-dev and enhance validation steps
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { CommandMessage } from "../../../core/types/commands";
|
||||
import { CommandMessage } from "../core/types/commands";
|
||||
// @ts-ignore
|
||||
import { EmbedBuilder, ButtonStyle, MessageFlags, ChannelType } from "discord.js";
|
||||
|
||||
15
src/.backup/prompts/discord-api-expert.prompt.md
Normal file
15
src/.backup/prompts/discord-api-expert.prompt.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Prompt: Discord.js + Discord API Expert Mode
|
||||
|
||||
Whenever assisting with Discord.js or the Discord API:
|
||||
|
||||
- Always prioritize information from:
|
||||
- https://discordjs.guide
|
||||
- https://github.com/discordjs/discord.js
|
||||
- https://github.com/discordjs/discord-api-types
|
||||
- https://discord.com/developers/docs/intro
|
||||
- If documentation is incomplete, check the installed dependency in `node_modules`.
|
||||
- Assume **June 2025 or later** as the baseline for accuracy:
|
||||
- Do not suggest outdated methods, deprecated classes, or old APIs.
|
||||
- Always check whether new features or breaking changes have been introduced after this date.
|
||||
- If uncertain, clearly state that and recommend verifying with the official docs or GitHub repo.
|
||||
- Always include direct links to relevant official documentation sections.
|
||||
14
src/.backup/prompts/discord-helper.prompt.md
Normal file
14
src/.backup/prompts/discord-helper.prompt.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Prompt: Discord.js Expert Mode (Post-June 2025)
|
||||
|
||||
Whenever assisting with Discord.js or related dependencies:
|
||||
|
||||
- Always check official resources:
|
||||
- https://discordjs.guide
|
||||
- https://github.com/discordjs/discord.js
|
||||
- https://github.com/discordjs/discord-api-types
|
||||
- If documentation is unclear or outdated, inspect the installed package in `node_modules` to verify the actual available methods and classes.
|
||||
- Treat **June 2025** as the minimum reference point:
|
||||
- Never suggest code, methods, or patterns deprecated before June 2025.
|
||||
- Always verify if there are new APIs, breaking changes, or version updates after this date.
|
||||
- Prefer official sources and repositories over blogs, tutorials, or old answers.
|
||||
- Always mention if information may be outdated and link to the package’s GitHub or changelog for verification.
|
||||
41
src/.backup/prompts/resource-checker.prompt.md
Normal file
41
src/.backup/prompts/resource-checker.prompt.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Prompt: Node.js Resource Checker (Memory & CPU)
|
||||
|
||||
Whenever Copilot generates, modifies, or reviews code in Node.js:
|
||||
|
||||
1. **Memory Monitoring**
|
||||
- Always remind to check memory usage with:
|
||||
```js
|
||||
logger.info(process.memoryUsage());
|
||||
```
|
||||
which returns RSS, Heap Used, Heap Total, External, and Array Buffers.
|
||||
- If `Heap Used` grows continuously without freeing, suggest potential memory leaks (unreleased timers, listeners, or large cached data).
|
||||
- If `RSS` (Resident Set Size) is unusually higher than `Heap Total`, warn that external/native modules might be consuming memory.
|
||||
|
||||
2. **CPU Usage**
|
||||
- Remind to check CPU load using:
|
||||
```bash
|
||||
top -p <pid>
|
||||
```
|
||||
or Node.js profiling (`--inspect`, `clinic flame`).
|
||||
- Flag infinite loops, synchronous blocking calls, or heavy computations running on the main thread as potential CPU overuse.
|
||||
- Encourage offloading heavy tasks to Workers, child processes, or queues when appropriate.
|
||||
|
||||
3. **Best Practices**
|
||||
- Always validate memory and CPU usage after implementing new features.
|
||||
- Suggest garbage collection checks (`global.gc()`) in development when Node is run with `--expose-gc`.
|
||||
- Warn if long-living references (e.g., large Maps, Sets, caches, or listeners) might not be released.
|
||||
- Highlight that CPU and memory efficiency are as important as correctness.
|
||||
|
||||
4. **Output Analysis**
|
||||
- When given memory stats like:
|
||||
```
|
||||
RSS: 186.9MB
|
||||
Heap Used: 67.6MB
|
||||
Heap Total: 71.2MB
|
||||
External: 5.0MB
|
||||
```
|
||||
- Compare `Heap Used` vs `Heap Total`: if close to the limit, risk of OOM.
|
||||
- Compare `RSS` vs `Heap Total`: if RSS is much larger, check for native module or buffer leaks.
|
||||
- If growth is unbounded, warn about potential memory leaks.
|
||||
|
||||
5. **Always remind** to rerun tests with `npx tsc --noEmit` (for type safety) and memory checks together, ensuring both correctness and performance.
|
||||
7
src/.backup/prompts/ts-validation.prompt.md
Normal file
7
src/.backup/prompts/ts-validation.prompt.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Prompt: TypeScript Validation Mode
|
||||
|
||||
Whenever Copilot generates or modifies TypeScript code:
|
||||
|
||||
- Immediately remind the user to validate changes by running:
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
392
src/commands/messages/alliaces/listChannelsV2.ts
Normal file
392
src/commands/messages/alliaces/listChannelsV2.ts
Normal file
@@ -0,0 +1,392 @@
|
||||
import { CommandMessage } from "../../../core/types/commands";
|
||||
import type { Message } from "discord.js";
|
||||
import { PermissionFlagsBits } from "discord.js";
|
||||
|
||||
function codeBlock(lines: string[]): string {
|
||||
return [
|
||||
'```',
|
||||
...lines,
|
||||
'```'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
async function buildChannelListPanel(message: Message) {
|
||||
const guild = message.guild!;
|
||||
const guildId = guild.id;
|
||||
const client = message.client as any;
|
||||
|
||||
// Obtener canales configurados existentes con estadísticas
|
||||
const existingChannels = await client.prisma.allianceChannel.findMany({
|
||||
where: { guildId },
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
pointsHistory: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
});
|
||||
|
||||
// Obtener estadísticas generales
|
||||
const totalPointsHistory = await client.prisma.pointHistory.count({
|
||||
where: { guildId }
|
||||
});
|
||||
|
||||
const availableBlocks = await client.prisma.blockV2Config.count({
|
||||
where: { guildId }
|
||||
});
|
||||
|
||||
if (existingChannels.length === 0) {
|
||||
// Panel cuando no hay canales configurados
|
||||
const panel = {
|
||||
type: 17,
|
||||
accent_color: 0x36393f,
|
||||
components: [
|
||||
{ type: 10, content: '## 📋 Canales de Alianza Configurados' },
|
||||
{ type: 10, content: '-# Lista de canales configurados para alianzas.' },
|
||||
{ type: 14, divider: true, spacing: 1 },
|
||||
|
||||
{ type: 10, content: '### 🗂️ Lista vacía' },
|
||||
{ type: 10, content: '📭 **No hay canales configurados** para alianzas en este servidor.' },
|
||||
|
||||
{ type: 14, divider: false, spacing: 1 },
|
||||
{ type: 10, content: '### 🚀 ¿Quieres empezar?' },
|
||||
{ type: 10, content: '• Usa `!setchannel-alliance` para configurar tu primer canal' },
|
||||
{ type: 10, content: '• Crea bloques con `!blockcreate <nombre>`' },
|
||||
|
||||
{ type: 14, divider: true, spacing: 1 },
|
||||
{ type: 10, content: '### 📊 Estadísticas Generales' },
|
||||
{ type: 10, content: `🧩 **Bloques disponibles:** ${availableBlocks}` },
|
||||
{ type: 10, content: `📈 **Puntos totales otorgados:** ${totalPointsHistory}` },
|
||||
|
||||
{ type: 14, divider: false, spacing: 1 },
|
||||
{ type: 10, content: `📅 ${new Date().toLocaleDateString('es-ES', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}` },
|
||||
|
||||
{ type: 14, divider: false, spacing: 1 },
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{
|
||||
type: 2,
|
||||
style: 3,
|
||||
label: "Configurar Canal",
|
||||
custom_id: "setup_first_channel",
|
||||
emoji: { name: "🔧" }
|
||||
},
|
||||
{
|
||||
type: 2,
|
||||
style: 2,
|
||||
label: "Ayuda",
|
||||
custom_id: "show_setup_help",
|
||||
emoji: { name: "📖" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return { panel, channelDetails: null, totalPointsHistory };
|
||||
}
|
||||
|
||||
// Crear descripción detallada de canales
|
||||
const channelDetails = await Promise.all(
|
||||
existingChannels.map(async (config: any, index: number) => {
|
||||
const channel = guild.channels.cache.get(config.channelId);
|
||||
const channelName = channel ? `#${channel.name}` : "❌ *Canal Eliminado*";
|
||||
const status = config.isActive ? "🟢 **Activo**" : "🔴 **Inactivo**";
|
||||
const pointsCount = config._count.pointsHistory;
|
||||
|
||||
// Obtener información del bloque
|
||||
const blockInfo = await client.prisma.blockV2Config.findFirst({
|
||||
where: {
|
||||
guildId,
|
||||
name: config.blockConfigName
|
||||
},
|
||||
select: { name: true, id: true }
|
||||
});
|
||||
|
||||
const blockStatus = blockInfo ? "✅ Válido" : "⚠️ Bloque Eliminado";
|
||||
|
||||
const createdDate = new Date(config.createdAt).toLocaleDateString('es-ES', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
});
|
||||
|
||||
return {
|
||||
index: index + 1,
|
||||
channelName,
|
||||
status,
|
||||
pointsCount,
|
||||
blockName: config.blockConfigName,
|
||||
blockStatus,
|
||||
createdDate,
|
||||
isValid: !!channel && !!blockInfo
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Agrupar por estado
|
||||
const activeChannels = channelDetails.filter(c => c.status.includes("Activo"));
|
||||
const inactiveChannels = channelDetails.filter(c => c.status.includes("Inactivo"));
|
||||
|
||||
// Construir lista de canales activos
|
||||
const activeList = activeChannels.length > 0
|
||||
? activeChannels.slice(0, 10).map(c =>
|
||||
`${c.index}. ${c.channelName} - ${c.blockName} (${c.pointsCount} pts)`
|
||||
)
|
||||
: ['Ninguno'];
|
||||
|
||||
// Construir lista de canales inactivos
|
||||
const inactiveList = inactiveChannels.length > 0
|
||||
? inactiveChannels.slice(0, 5).map(c =>
|
||||
`${c.index}. ${c.channelName} - ${c.blockName}`
|
||||
)
|
||||
: [];
|
||||
|
||||
// Obtener canales más activos para estadísticas
|
||||
const topChannels = channelDetails
|
||||
.sort((a, b) => b.pointsCount - a.pointsCount)
|
||||
.slice(0, 3)
|
||||
.map((c, i) => `${i + 1}. ${c.channelName.replace(/[#❌*]/g, '').trim()}`)
|
||||
.join(', ') || 'N/A';
|
||||
|
||||
const now = new Date();
|
||||
const ts = now.toLocaleDateString('es-ES');
|
||||
|
||||
// Crear el panel con components V2
|
||||
const components: any[] = [
|
||||
{ type: 10, content: '## 📋 Canales de Alianza Configurados' },
|
||||
{ type: 10, content: `-# ${existingChannels.length} canal(es) configurado(s) • 🟢 Activos: ${activeChannels.length} • 🔴 Inactivos: ${inactiveChannels.length}` },
|
||||
{ type: 14, divider: true, spacing: 1 }
|
||||
];
|
||||
|
||||
// Añadir canales activos
|
||||
if (activeChannels.length > 0) {
|
||||
components.push(
|
||||
{ type: 10, content: `### 🟢 Canales Activos (${activeChannels.length})` },
|
||||
{ type: 10, content: codeBlock(activeList) }
|
||||
);
|
||||
}
|
||||
|
||||
// Añadir canales inactivos si los hay
|
||||
if (inactiveChannels.length > 0) {
|
||||
components.push(
|
||||
{ type: 14, divider: false, spacing: 1 },
|
||||
{ type: 10, content: `### 🔴 Canales Inactivos (${inactiveChannels.length})` },
|
||||
{ type: 10, content: codeBlock(inactiveList) }
|
||||
);
|
||||
}
|
||||
|
||||
// Añadir estadísticas
|
||||
components.push(
|
||||
{ type: 14, divider: true, spacing: 1 },
|
||||
{ type: 10, content: '### 📊 Estadísticas del Servidor' },
|
||||
{ type: 10, content: `🧩 **Bloques disponibles:** ${availableBlocks}` },
|
||||
{ type: 10, content: `📈 **Total puntos otorgados:** ${totalPointsHistory}` },
|
||||
{ type: 10, content: `⚡ **Canales más activos:** ${topChannels}` },
|
||||
{ type: 14, divider: false, spacing: 1 },
|
||||
{ type: 10, content: `📅 Actualizado: ${ts}` }
|
||||
);
|
||||
|
||||
// Añadir botones
|
||||
components.push(
|
||||
{ type: 14, divider: false, spacing: 1 },
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{
|
||||
type: 2,
|
||||
style: 3,
|
||||
label: "Añadir Canal",
|
||||
custom_id: "add_channel",
|
||||
emoji: { name: "➕" }
|
||||
},
|
||||
{
|
||||
type: 2,
|
||||
style: 4,
|
||||
label: "Eliminar Canal",
|
||||
custom_id: "remove_channel",
|
||||
emoji: { name: "🗑️" }
|
||||
},
|
||||
{
|
||||
type: 2,
|
||||
style: 1,
|
||||
label: "Actualizar",
|
||||
custom_id: "refresh_list",
|
||||
emoji: { name: "🔄" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 1,
|
||||
components: [
|
||||
{
|
||||
type: 2,
|
||||
style: 2,
|
||||
label: "Estadísticas",
|
||||
custom_id: "show_stats",
|
||||
emoji: { name: "📊" }
|
||||
},
|
||||
{
|
||||
type: 2,
|
||||
style: 2,
|
||||
label: "Ver Bloques",
|
||||
custom_id: "show_blocks",
|
||||
emoji: { name: "🧩" }
|
||||
},
|
||||
{
|
||||
type: 2,
|
||||
style: 2,
|
||||
label: "Ayuda",
|
||||
custom_id: "show_help",
|
||||
emoji: { name: "❓" }
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
const panel = {
|
||||
type: 17,
|
||||
accent_color: 0x5865f2,
|
||||
components
|
||||
};
|
||||
|
||||
return { panel, channelDetails, totalPointsHistory };
|
||||
}
|
||||
|
||||
export const command: CommandMessage = {
|
||||
name: "listar-canales-alianza-v2",
|
||||
type: "message",
|
||||
aliases: ["listchannels-alliance-v2", "listalchannel-v2", "channelsally-v2", "alliancechannels-v2"],
|
||||
cooldown: 5,
|
||||
description: "Lista todos los canales configurados para alianzas (versión V2 con components)",
|
||||
category: "Alianzas",
|
||||
usage: "listar-canales-alianza-v2",
|
||||
run: async (message) => {
|
||||
if (!message.guild) {
|
||||
await message.reply({ content: '❌ Este comando solo puede usarse en servidores.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const client = message.client as any;
|
||||
const guildId = message.guild.id;
|
||||
|
||||
try {
|
||||
const result = await buildChannelListPanel(message);
|
||||
|
||||
const response = await message.reply({
|
||||
// @ts-ignore Flag de componentes V2
|
||||
flags: 32768,
|
||||
components: [result.panel]
|
||||
});
|
||||
|
||||
// Solo crear collector si hay canales o para el caso vacío
|
||||
const collector = response.createMessageComponentCollector({
|
||||
time: 600000, // 10 minutos
|
||||
filter: (i) => i.user.id === message.author.id
|
||||
});
|
||||
|
||||
collector.on("collect", async (interaction) => {
|
||||
try {
|
||||
switch (interaction.customId) {
|
||||
case "setup_first_channel":
|
||||
case "add_channel":
|
||||
await interaction.reply({
|
||||
content: "➕ **Añadir Canal**\n\nUsa el comando: `!setchannel-alliance`\n\nEste comando te guiará para configurar un nuevo canal de alianzas.",
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
break;
|
||||
|
||||
case "remove_channel":
|
||||
await interaction.reply({
|
||||
content: "🗑️ **Eliminar Canal**\n\nUsa el comando: `!removechannel-alliance`\n\nEste comando te permitirá eliminar canales de la configuración de alianzas.",
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
break;
|
||||
|
||||
case "refresh_list":
|
||||
await interaction.reply({
|
||||
content: "🔄 **Lista Actualizada**\n\nUsa el comando nuevamente: `!listchannels-alliance-v2`\n\nEsto mostrará la información más reciente.",
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
break;
|
||||
|
||||
case "show_stats":
|
||||
if (result.channelDetails) {
|
||||
const detailedStats = result.channelDetails.map((c: any) =>
|
||||
`• ${c.channelName}: **${c.pointsCount}** puntos`
|
||||
).join('\n');
|
||||
|
||||
await interaction.reply({
|
||||
content: `📊 **Estadísticas Detalladas**\n\n**Puntos por Canal:**\n${detailedStats}\n\n**Total del Servidor:** ${result.totalPointsHistory} puntos`,
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
} else {
|
||||
await interaction.reply({
|
||||
content: "📊 **Estadísticas**\n\nNo hay canales configurados aún para mostrar estadísticas.",
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "show_blocks":
|
||||
const blocksList = await client.prisma.blockV2Config.findMany({
|
||||
where: { guildId },
|
||||
select: { name: true, id: true }
|
||||
});
|
||||
|
||||
const blocksText = blocksList.length > 0
|
||||
? blocksList.map((block: any, i: number) => `${i + 1}. \`${block.name}\``).join('\n')
|
||||
: "No hay bloques configurados";
|
||||
|
||||
await interaction.reply({
|
||||
content: `🧩 **Bloques Disponibles (${blocksList.length})**\n\n${blocksText}\n\n💡 Crea bloques con: \`!blockcreate <nombre>\``,
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
break;
|
||||
|
||||
case "show_setup_help":
|
||||
case "show_help":
|
||||
await interaction.reply({
|
||||
content: `📖 **Ayuda - Sistema de Alianzas**\n\n**Comandos principales:**\n• \`!setchannel-alliance\` - Configurar canal\n• \`!removechannel-alliance\` - Eliminar canal\n• \`!listchannels-alliance-v2\` - Ver configurados\n\n**Comandos de bloques:**\n• \`!blockcreate <nombre>\` - Crear bloque\n• \`!blockeditv2 <nombre>\` - Editar bloque\n• \`!embedlist\` - Ver todos los bloques`,
|
||||
// @ts-ignore Flag efímero
|
||||
flags: 64
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error en collector:', error);
|
||||
}
|
||||
});
|
||||
|
||||
collector.on("end", async () => {
|
||||
try {
|
||||
// Los components V2 no necesitan ser editados al expirar
|
||||
// ya que Discord los maneja automáticamente
|
||||
} catch (error) {
|
||||
// Ignorar errores
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error en listChannelsV2:', error);
|
||||
await message.reply({
|
||||
content: '❌ **Error**\n\nHubo un problema al cargar la lista de canales. Inténtalo de nuevo.',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
import logger from "../../core/lib/logger";
|
||||
import {
|
||||
StringSelectMenuInteraction,
|
||||
MessageFlags,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ActionRowBuilder
|
||||
} from 'discord.js';
|
||||
|
||||
export default {
|
||||
customId: 'ld_select_user',
|
||||
run: async (interaction: StringSelectMenuInteraction) => {
|
||||
if (!interaction.guild) {
|
||||
return interaction.reply({
|
||||
content: '❌ Solo disponible en servidores.',
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const selectedUserId = interaction.values[0];
|
||||
|
||||
// Obtener información del usuario seleccionado para mostrar en el modal
|
||||
let userName = 'Usuario';
|
||||
try {
|
||||
const member = await interaction.guild.members.fetch(selectedUserId);
|
||||
userName = member.displayName || member.user.username;
|
||||
} catch {
|
||||
try {
|
||||
const user = await interaction.client.users.fetch(selectedUserId);
|
||||
userName = user.username;
|
||||
} catch {
|
||||
userName = selectedUserId;
|
||||
}
|
||||
}
|
||||
|
||||
// Crear modal para ingresar la cantidad de puntos
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId(`ld_points_modal:${selectedUserId}`)
|
||||
.setTitle(`Gestionar puntos de ${userName}`);
|
||||
|
||||
// Input para puntos totales (simplificado)
|
||||
const totalInput = new TextInputBuilder()
|
||||
.setCustomId('total_points')
|
||||
.setLabel('Modificar Puntos Totales')
|
||||
.setPlaceholder('+50 (añadir) / -2 (quitar últimos 2) / =100 (establecer)')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setRequired(true);
|
||||
|
||||
// Añadir el input al modal
|
||||
// @ts-ignore
|
||||
modal.addComponents(
|
||||
// @ts-ignore
|
||||
new ActionRowBuilder().addComponents(totalInput)
|
||||
);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
} catch (e) {
|
||||
logger.error({ err: e }, 'Error en ldSelectUser');
|
||||
await interaction.reply({
|
||||
content: '❌ Error al procesar la selección.',
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user