feat: update Copilot instructions for Discord.js 15.0.0-dev and enhance validation steps

This commit is contained in:
2025-10-03 22:39:16 -05:00
parent 1c769289c3
commit 97bd198f6e
11 changed files with 555 additions and 203 deletions

View File

@@ -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: 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. **Secondary Sources**
- Only use these as references when the information is still valid:
- [Discord.js Guide](https://discordjs.guide) - [Discord.js Guide](https://discordjs.guide)
- [discord.js GitHub Repository](https://github.com/discordjs/discord.js) - [discord.js GitHub](https://github.com/discordjs/discord.js)
- [Discord API Types GitHub Repository](https://github.com/discordjs/discord-api-types) - [Discord API Types GitHub](https://github.com/discordjs/discord-api-types)
- [Discord Developer Documentation](https://discord.com/developers/docs/intro) - [Discord Developer Documentation](https://discord.com/developers/docs/intro)
- Mark clearly if the information is outdated compared to the development version.
2. Always **prefer official documentation** over blogs, forums, or tutorials. 3. **Code Analysis**
- 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. - 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.
3. When suggesting code, ensure it matches the **latest stable release of Discord.js and related packages (June 2025 or newer)**. 4. **Validation**
- If examples belong to an older version, adapt them to the most recent stable release. - Always suggest running:
- If uncertain, indicate that verification against the `node_modules` implementation is required.
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:
```bash ```bash
npx tsc --noEmit npx tsc --noEmit
``` ```
(or equivalent `tsc` command). to validate typings.
- If type errors are found, propose fixes before finalizing the solution. - 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)

View File

@@ -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

28
.github/prompts/error-strict.prompt.md vendored Normal file
View File

@@ -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 doesnt 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.

205
package-lock.json generated
View File

@@ -15,7 +15,7 @@
"appwrite": "20.1.0", "appwrite": "20.1.0",
"chrono-node": "2.9.0", "chrono-node": "2.9.0",
"discord-api-types": "0.38.24", "discord-api-types": "0.38.24",
"discord.js": "14.22.1", "discord.js": "15.0.0-dev.1759363313-f510b5ffa",
"node-appwrite": "19.1.0", "node-appwrite": "19.1.0",
"pino": "9.13.0", "pino": "9.13.0",
"prisma": "6.16.2", "prisma": "6.16.2",
@@ -41,127 +41,117 @@
} }
}, },
"node_modules/@discordjs/builders": { "node_modules/@discordjs/builders": {
"version": "1.11.3", "version": "2.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.11.3.tgz", "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-2.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-p3kf5eV49CJiRTfhtutUCeivSyQ/l2JlKodW1ZquRwwvlOWmG9+6jFShX6x8rUiYhnP6wKI96rgN/SXMy5e5aw==", "integrity": "sha512-MORqSl+/RtNHavjgRA4GqYdE4URoFL4J52vu0OCV3UNhQmCj/WQTL/FQHxeGSaPfkDdYNOmwnUn4XEA6dZhc5Q==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@discordjs/formatters": "^0.6.1", "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa",
"@discordjs/util": "^1.1.1", "discord-api-types": "^0.38.23",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "^0.38.16",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4", "ts-mixer": "^6.0.4",
"tslib": "^2.6.3" "tslib": "^2.8.1",
"zod": "^4.0.17"
}, },
"engines": { "engines": {
"node": ">=16.11.0" "node": ">=22.12.0"
}, },
"funding": { "funding": {
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
} }
}, },
"node_modules/@discordjs/collection": { "node_modules/@discordjs/collection": {
"version": "1.5.3", "version": "3.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-3.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", "integrity": "sha512-qEeT3PdItXKrZgr3Mx4+Glp2gPylguxAFKFFICe/YIs6RqW7ln5fh+5mW11ekPyhdsE4EZdOY3vP03SuQwC1VQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">=16.11.0" "node": ">=22.12.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
} }
}, },
"node_modules/@discordjs/formatters": { "node_modules/@discordjs/formatters": {
"version": "0.6.1", "version": "1.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.1.tgz", "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-1.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==", "integrity": "sha512-94/RDfYgdHRc1JOaMg687O5h53v4FdnJAG8fuRPpkOqZgTLtLSsnfGRUEKqwSr5dtyTOPIO3quTP23B2ImIjpA==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"discord-api-types": "^0.38.1" "discord-api-types": "^0.38.23"
}, },
"engines": { "engines": {
"node": ">=16.11.0" "node": ">=22.12.0"
}, },
"funding": { "funding": {
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
} }
}, },
"node_modules/@discordjs/rest": { "node_modules/@discordjs/rest": {
"version": "2.6.0", "version": "3.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.0.tgz", "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-3.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==", "integrity": "sha512-4LCncMfpZwtPG68Lv36th7WvwDZeTs0zok6AKNXt0Hp6E5CZ76bw92hACJmfergtPsD5/Ds75O9Fh0xptd2B1w==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@discordjs/collection": "^2.1.1", "@discordjs/collection": "^3.0.0-dev.1759363313-f510b5ffa",
"@discordjs/util": "^1.1.1", "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa",
"@sapphire/async-queue": "^1.5.3", "@sapphire/async-queue": "^1.5.5",
"@sapphire/snowflake": "^3.5.3", "@sapphire/snowflake": "^3.5.5",
"@vladfrangu/async_event_emitter": "^2.4.6", "@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.16", "discord-api-types": "^0.38.23",
"magic-bytes.js": "^1.10.0", "magic-bytes.js": "^1.12.1",
"tslib": "^2.6.3", "tslib": "^2.8.1",
"undici": "6.21.3" "undici": "7.11.0",
"uuid": "^11.1.0"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=22.12.0"
}, },
"funding": { "funding": {
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
} }
}, },
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": { "node_modules/@discordjs/rest/node_modules/uuid": {
"version": "2.1.1", "version": "11.1.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"license": "Apache-2.0", "funding": [
"engines": { "https://github.com/sponsors/broofa",
"node": ">=18" "https://github.com/sponsors/ctavan"
}, ],
"funding": { "license": "MIT",
"url": "https://github.com/discordjs/discord.js?sponsor" "bin": {
"uuid": "dist/esm/bin/uuid"
} }
}, },
"node_modules/@discordjs/util": { "node_modules/@discordjs/util": {
"version": "1.1.1", "version": "2.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-2.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", "integrity": "sha512-84PwcSH7ZQnsMluBr43IfoCbhPN1V18nH2FOzm6+JdUPKeh0peStaRljSwiTRRFDcdY5CufcNvde/5kksH0kJA==",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">=18" "node": ">=22.12.0"
}, },
"funding": { "funding": {
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
} }
}, },
"node_modules/@discordjs/ws": { "node_modules/@discordjs/ws": {
"version": "1.2.3", "version": "3.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-3.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==", "integrity": "sha512-/FZX2LaPkTt3E8KAWB7dJ6uGRFmbtGPdgZ3a/hunePznXDU9syTbWoA4Vh6PNbJN09VKJxW3ihSxgLM/P87KkA==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@discordjs/collection": "^2.1.0", "@discordjs/collection": "^3.0.0-dev.1759363313-f510b5ffa",
"@discordjs/rest": "^2.5.1", "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa",
"@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.5",
"@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.18.1",
"@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.4.6",
"@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.23",
"discord-api-types": "^0.38.1", "tslib": "^2.8.1",
"tslib": "^2.6.2", "ws": "^8.18.3"
"ws": "^8.17.0"
}, },
"engines": { "engines": {
"node": ">=16.11.0" "node": ">=22.12.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"
}, },
"funding": { "funding": {
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
@@ -374,23 +364,10 @@
"npm": ">=7.0.0" "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": { "node_modules/@sapphire/snowflake": {
"version": "3.5.3", "version": "3.5.5",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.5.tgz",
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", "integrity": "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=v14.0.0", "node": ">=v14.0.0",
@@ -701,27 +678,28 @@
] ]
}, },
"node_modules/discord.js": { "node_modules/discord.js": {
"version": "14.22.1", "version": "15.0.0-dev.1759363313-f510b5ffa",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.22.1.tgz", "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-15.0.0-dev.1759363313-f510b5ffa.tgz",
"integrity": "sha512-3k+Kisd/v570Jr68A1kNs7qVhNehDwDJAPe4DZ2Syt+/zobf9zEcuYFvsfIaAOgCa0BiHMfOOKQY4eYINl0z7w==", "integrity": "sha512-SfnkqY+pzYiEugGKUuhIFgn1PbX2Tn0KntwDR3ggAzZRBZKgByrg2gqCpNb05VeNGUjC7m75gctKLZwSSXbB6A==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@discordjs/builders": "^1.11.2", "@discordjs/builders": "^2.0.0-dev.1759363313-f510b5ffa",
"@discordjs/collection": "1.5.3", "@discordjs/collection": "^3.0.0-dev.1759363313-f510b5ffa",
"@discordjs/formatters": "^0.6.1", "@discordjs/formatters": "^1.0.0-dev.1759363313-f510b5ffa",
"@discordjs/rest": "^2.6.0", "@discordjs/rest": "^3.0.0-dev.1759363313-f510b5ffa",
"@discordjs/util": "^1.1.1", "@discordjs/util": "^2.0.0-dev.1759363313-f510b5ffa",
"@discordjs/ws": "^1.2.3", "@discordjs/ws": "^3.0.0-dev.1759363313-f510b5ffa",
"@sapphire/snowflake": "3.5.3", "@sapphire/snowflake": "3.5.5",
"discord-api-types": "^0.38.16", "@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.23",
"fast-deep-equal": "3.1.3", "fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1", "lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0", "magic-bytes.js": "^1.12.1",
"tslib": "^2.6.3", "tslib": "^2.8.1",
"undici": "6.21.3" "undici": "7.11.0"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=22.12.0"
}, },
"funding": { "funding": {
"url": "https://github.com/discordjs/discord.js?sponsor" "url": "https://github.com/discordjs/discord.js?sponsor"
@@ -957,12 +935,6 @@
"safe-buffer": "^5.0.1" "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": { "node_modules/lodash.snakecase": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
@@ -1379,12 +1351,12 @@
} }
}, },
"node_modules/undici": { "node_modules/undici": {
"version": "6.21.3", "version": "7.11.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz",
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18.17" "node": ">=20.18.1"
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
@@ -1459,6 +1431,15 @@
"engines": { "engines": {
"node": ">=6" "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"
}
} }
} }
} }

View File

@@ -1,4 +1,4 @@
import { CommandMessage } from "../../../core/types/commands"; import { CommandMessage } from "../core/types/commands";
// @ts-ignore // @ts-ignore
import { EmbedBuilder, ButtonStyle, MessageFlags, ChannelType } from "discord.js"; import { EmbedBuilder, ButtonStyle, MessageFlags, ChannelType } from "discord.js";

View 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.',
});
}
}
};

View File

@@ -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
});
}
}
};