diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 40e7779..5424ac8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,31 +1,38 @@ -# Custom instructions for GitHub Copilot +# Custom instructions for GitHub Copilot (discord.js 15.0.0-dev) -Whenever generating code, commit messages, or explanations related to Discord.js or the Discord API: +When generating or modifying code for Discord.js version `15.0.0-dev.1759363313-f510b5ffa`: -1. Always **consult and reference** the following sources for accurate and up-to-date information: - - [Discord.js Guide](https://discordjs.guide) - - [discord.js GitHub Repository](https://github.com/discordjs/discord.js) - - [Discord API Types GitHub Repository](https://github.com/discordjs/discord-api-types) - - [Discord Developer Documentation](https://discord.com/developers/docs/intro) +1. **Primary Source of Truth** + - Do not assume the official docs, guide, or GitHub repositories are fully up-to-date. + - Always prioritize the installed package in `node_modules/discord.js` as the most reliable source. + - Always cross-reference with the provided file `example.ts.txt` to understand new or experimental APIs. -2. Always **prefer official documentation** over blogs, forums, or tutorials. - - If no clear answer is found in docs, **inspect the installed dependency in `node_modules`** to confirm if a method, property, or class truly exists. +2. **Secondary Sources** + - Only use these as references when the information is still valid: + - [Discord.js Guide](https://discordjs.guide) + - [discord.js GitHub](https://github.com/discordjs/discord.js) + - [Discord API Types GitHub](https://github.com/discordjs/discord-api-types) + - [Discord Developer Documentation](https://discord.com/developers/docs/intro) + - Mark clearly if the information is outdated compared to the development version. -3. When suggesting code, ensure it matches the **latest stable release of Discord.js and related packages (June 2025 or newer)**. - - If examples belong to an older version, adapt them to the most recent stable release. - - If uncertain, indicate that verification against the `node_modules` implementation is required. +3. **Code Analysis** + - Inspect the type definitions and source files inside `node_modules/discord.js` before suggesting new methods or classes. + - If `example.ts.txt` shows usage not covered in documentation, assume that is the intended API for this development version. -4. When installing or suggesting npm packages related to Discord.js: - - Verify against their **GitHub repository** for recent updates, breaking changes, or migration notes. - - Always check if the changelog or releases page contains updates after **June 2025** and adjust suggestions accordingly. - -5. Always provide **links to relevant official documentation** (Discord.js Guide, GitHub repos, or Discord Developer Docs). - - If the info is missing in docs, but exists in the actual installed package, explain that and provide the discovered usage. - -6. **Error validation requirement:** - - After generating or modifying code, always remind the user to check for TypeScript errors by running: +4. **Validation** + - Always suggest running: ```bash npx tsc --noEmit ``` - (or equivalent `tsc` command). - - If type errors are found, propose fixes before finalizing the solution. + to validate typings. + - Remind to check runtime memory and CPU usage with: + ```js + console.log(process.memoryUsage()); + ``` + and external profilers if needed. + +5. **Communication** + - When suggesting code, state explicitly whether it comes from: + - `node_modules` (preferred, authoritative) + - `example.ts.txt` (author-provided experimental reference) + - official docs (secondary, possibly outdated) diff --git a/.github/prompts/discord.js-v15-dev.prompt.md b/.github/prompts/discord.js-v15-dev.prompt.md new file mode 100644 index 0000000..824a870 --- /dev/null +++ b/.github/prompts/discord.js-v15-dev.prompt.md @@ -0,0 +1,11 @@ +# Prompt: Discord.js 15 Development Mode + +When assisting with Discord.js version `15.0.0-dev.1759363313-f510b5ffa`: + +- Treat the installed code in `node_modules/discord.js` as the primary and most reliable reference. +- Always analyze the file `example.ts.txt` to extract API patterns, since it reflects the intended usage of this dev version. +- Do not rely blindly on official docs or the guide; use them only to compare and note differences with the dev version. +- Highlight when functionality has changed from v14 to v15, based on observed code and `example.ts.txt`. +- Encourage verification by: + ```bash + npx tsc --noEmit diff --git a/.github/prompts/error-strict.prompt.md b/.github/prompts/error-strict.prompt.md new file mode 100644 index 0000000..350461d --- /dev/null +++ b/.github/prompts/error-strict.prompt.md @@ -0,0 +1,28 @@ +# Prompt: TypeScript Strict Error Resolver + +Whenever Copilot generates or modifies code: + +1. **Never ignore compiler errors** like `TS2339`, `TS2345`, or similar. + - Always explain what the error means in plain language. + - Always suggest at least one concrete fix. + +2. **For missing properties or methods (e.g. `TS2339`)**: + - Check if the method exists in the installed `node_modules`. + - If it doesn’t exist, assume the API changed in `discord.js@15-dev`. + - Propose alternatives based on actual available methods (`example.ts.txt` must be consulted). + +3. **For type mismatches (e.g. `TS2345`)**: + - Suggest code changes that handle `null`/`undefined` safely. + - Show how to cast or coerce types **without breaking strict typing**. + +4. **Validation step**: + - Always remind to rerun: + ```bash + npx tsc --noEmit + ``` + until the project compiles cleanly. + - Do not consider the task “done” if compiler errors remain. + +5. **Explicitness**: + - Always specify: “This code suggestion resolves TS2339 / TS2345.” + - Never produce a code snippet that still triggers the same error. diff --git a/package-lock.json b/package-lock.json index 7ed6756..621a691 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "appwrite": "20.1.0", "chrono-node": "2.9.0", "discord-api-types": "0.38.24", - "discord.js": "14.22.1", + "discord.js": "15.0.0-dev.1759363313-f510b5ffa", "node-appwrite": "19.1.0", "pino": "9.13.0", "prisma": "6.16.2", @@ -41,127 +41,117 @@ } }, "node_modules/@discordjs/builders": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.11.3.tgz", - "integrity": "sha512-p3kf5eV49CJiRTfhtutUCeivSyQ/l2JlKodW1ZquRwwvlOWmG9+6jFShX6x8rUiYhnP6wKI96rgN/SXMy5e5aw==", + "version": "2.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-2.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-MORqSl+/RtNHavjgRA4GqYdE4URoFL4J52vu0OCV3UNhQmCj/WQTL/FQHxeGSaPfkDdYNOmwnUn4XEA6dZhc5Q==", "license": "Apache-2.0", "dependencies": { - "@discordjs/formatters": "^0.6.1", - "@discordjs/util": "^1.1.1", - "@sapphire/shapeshift": "^4.0.0", - "discord-api-types": "^0.38.16", - "fast-deep-equal": "^3.1.3", + "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa", + "discord-api-types": "^0.38.23", "ts-mixer": "^6.0.4", - "tslib": "^2.6.3" + "tslib": "^2.8.1", + "zod": "^4.0.17" }, "engines": { - "node": ">=16.11.0" + "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" } }, "node_modules/@discordjs/collection": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", - "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "version": "3.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-3.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-qEeT3PdItXKrZgr3Mx4+Glp2gPylguxAFKFFICe/YIs6RqW7ln5fh+5mW11ekPyhdsE4EZdOY3vP03SuQwC1VQ==", "license": "Apache-2.0", "engines": { - "node": ">=16.11.0" + "node": ">=22.12.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, "node_modules/@discordjs/formatters": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.1.tgz", - "integrity": "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==", + "version": "1.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-1.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-94/RDfYgdHRc1JOaMg687O5h53v4FdnJAG8fuRPpkOqZgTLtLSsnfGRUEKqwSr5dtyTOPIO3quTP23B2ImIjpA==", "license": "Apache-2.0", "dependencies": { - "discord-api-types": "^0.38.1" + "discord-api-types": "^0.38.23" }, "engines": { - "node": ">=16.11.0" + "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" } }, "node_modules/@discordjs/rest": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.0.tgz", - "integrity": "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==", + "version": "3.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-3.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-4LCncMfpZwtPG68Lv36th7WvwDZeTs0zok6AKNXt0Hp6E5CZ76bw92hACJmfergtPsD5/Ds75O9Fh0xptd2B1w==", "license": "Apache-2.0", "dependencies": { - "@discordjs/collection": "^2.1.1", - "@discordjs/util": "^1.1.1", - "@sapphire/async-queue": "^1.5.3", - "@sapphire/snowflake": "^3.5.3", + "@discordjs/collection": "^3.0.0-dev.1759363313-f510b5ffa", + "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa", + "@sapphire/async-queue": "^1.5.5", + "@sapphire/snowflake": "^3.5.5", "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "^0.38.16", - "magic-bytes.js": "^1.10.0", - "tslib": "^2.6.3", - "undici": "6.21.3" + "discord-api-types": "^0.38.23", + "magic-bytes.js": "^1.12.1", + "tslib": "^2.8.1", + "undici": "7.11.0", + "uuid": "^11.1.0" }, "engines": { - "node": ">=18" + "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", - "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", - "license": "Apache-2.0", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" + "node_modules/@discordjs/rest/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" } }, "node_modules/@discordjs/util": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", - "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", + "version": "2.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-2.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-84PwcSH7ZQnsMluBr43IfoCbhPN1V18nH2FOzm6+JdUPKeh0peStaRljSwiTRRFDcdY5CufcNvde/5kksH0kJA==", "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" } }, "node_modules/@discordjs/ws": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz", - "integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==", + "version": "3.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-3.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-/FZX2LaPkTt3E8KAWB7dJ6uGRFmbtGPdgZ3a/hunePznXDU9syTbWoA4Vh6PNbJN09VKJxW3ihSxgLM/P87KkA==", "license": "Apache-2.0", "dependencies": { - "@discordjs/collection": "^2.1.0", - "@discordjs/rest": "^2.5.1", - "@discordjs/util": "^1.1.0", - "@sapphire/async-queue": "^1.5.2", - "@types/ws": "^8.5.10", - "@vladfrangu/async_event_emitter": "^2.2.4", - "discord-api-types": "^0.38.1", - "tslib": "^2.6.2", - "ws": "^8.17.0" + "@discordjs/collection": "^3.0.0-dev.1759363313-f510b5ffa", + "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa", + "@sapphire/async-queue": "^1.5.5", + "@types/ws": "^8.18.1", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "^0.38.23", + "tslib": "^2.8.1", + "ws": "^8.18.3" }, "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", - "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", - "license": "Apache-2.0", - "engines": { - "node": ">=18" + "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" @@ -374,23 +364,10 @@ "npm": ">=7.0.0" } }, - "node_modules/@sapphire/shapeshift": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz", - "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v16" - } - }, "node_modules/@sapphire/snowflake": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", - "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.5.tgz", + "integrity": "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==", "license": "MIT", "engines": { "node": ">=v14.0.0", @@ -701,27 +678,28 @@ ] }, "node_modules/discord.js": { - "version": "14.22.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.22.1.tgz", - "integrity": "sha512-3k+Kisd/v570Jr68A1kNs7qVhNehDwDJAPe4DZ2Syt+/zobf9zEcuYFvsfIaAOgCa0BiHMfOOKQY4eYINl0z7w==", + "version": "15.0.0-dev.1759363313-f510b5ffa", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-15.0.0-dev.1759363313-f510b5ffa.tgz", + "integrity": "sha512-SfnkqY+pzYiEugGKUuhIFgn1PbX2Tn0KntwDR3ggAzZRBZKgByrg2gqCpNb05VeNGUjC7m75gctKLZwSSXbB6A==", "license": "Apache-2.0", "dependencies": { - "@discordjs/builders": "^1.11.2", - "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.6.1", - "@discordjs/rest": "^2.6.0", - "@discordjs/util": "^1.1.1", - "@discordjs/ws": "^1.2.3", - "@sapphire/snowflake": "3.5.3", - "discord-api-types": "^0.38.16", + "@discordjs/builders": "^2.0.0-dev.1759363313-f510b5ffa", + "@discordjs/collection": "^3.0.0-dev.1759363313-f510b5ffa", + "@discordjs/formatters": "^1.0.0-dev.1759363313-f510b5ffa", + "@discordjs/rest": "^3.0.0-dev.1759363313-f510b5ffa", + "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa", + "@discordjs/ws": "^3.0.0-dev.1759363313-f510b5ffa", + "@sapphire/snowflake": "3.5.5", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "^0.38.23", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", - "magic-bytes.js": "^1.10.0", - "tslib": "^2.6.3", - "undici": "6.21.3" + "magic-bytes.js": "^1.12.1", + "tslib": "^2.8.1", + "undici": "7.11.0" }, "engines": { - "node": ">=18" + "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" @@ -957,12 +935,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -1379,12 +1351,12 @@ } }, "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", + "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", "license": "MIT", "engines": { - "node": ">=18.17" + "node": ">=20.18.1" } }, "node_modules/undici-types": { @@ -1459,6 +1431,15 @@ "engines": { "node": ">=6" } + }, + "node_modules/zod": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz", + "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/src/commands/messages/alliaces/listChannels.ts b/src/.backup/listChannels.backup.ts similarity index 99% rename from src/commands/messages/alliaces/listChannels.ts rename to src/.backup/listChannels.backup.ts index f1218d8..35dde72 100644 --- a/src/commands/messages/alliaces/listChannels.ts +++ b/src/.backup/listChannels.backup.ts @@ -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"; diff --git a/.github/prompts/discord-api-expert.prompt.md b/src/.backup/prompts/discord-api-expert.prompt.md similarity index 100% rename from .github/prompts/discord-api-expert.prompt.md rename to src/.backup/prompts/discord-api-expert.prompt.md diff --git a/.github/prompts/discord-helper.prompt.md b/src/.backup/prompts/discord-helper.prompt.md similarity index 100% rename from .github/prompts/discord-helper.prompt.md rename to src/.backup/prompts/discord-helper.prompt.md diff --git a/.github/prompts/resource-checker.prompt.md b/src/.backup/prompts/resource-checker.prompt.md similarity index 100% rename from .github/prompts/resource-checker.prompt.md rename to src/.backup/prompts/resource-checker.prompt.md diff --git a/.github/prompts/ts-validation.prompt.md b/src/.backup/prompts/ts-validation.prompt.md similarity index 100% rename from .github/prompts/ts-validation.prompt.md rename to src/.backup/prompts/ts-validation.prompt.md diff --git a/src/commands/messages/alliaces/listChannelsV2.ts b/src/commands/messages/alliaces/listChannelsV2.ts new file mode 100644 index 0000000..0de3ac6 --- /dev/null +++ b/src/commands/messages/alliaces/listChannelsV2.ts @@ -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 `' }, + + { 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 \``, + // @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 \` - Crear bloque\n• \`!blockeditv2 \` - 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.', + }); + } + } +}; diff --git a/src/components/selectmenus/ldSelectUser.ts b/src/components/selectmenus/ldSelectUser.ts deleted file mode 100644 index 8c47826..0000000 --- a/src/components/selectmenus/ldSelectUser.ts +++ /dev/null @@ -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 - }); - } - } -};