{"id":47810003,"url":"https://github.com/almeidx/discore","last_synced_at":"2026-04-03T18:06:51.517Z","repository":{"id":342153369,"uuid":"1173029886","full_name":"almeidx/discore","owner":"almeidx","description":"A lightweight, functional Discord bot framework built on @discordjs/core","archived":false,"fork":false,"pushed_at":"2026-03-26T12:27:06.000Z","size":246,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T18:30:04.702Z","etag":null,"topics":["bot","discord","discordjs","framework","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/almeidx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-05T00:00:41.000Z","updated_at":"2026-03-25T16:52:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/almeidx/discore","commit_stats":null,"previous_names":["almeidx/discore"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/almeidx/discore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almeidx%2Fdiscore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almeidx%2Fdiscore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almeidx%2Fdiscore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almeidx%2Fdiscore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/almeidx","download_url":"https://codeload.github.com/almeidx/discore/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/almeidx%2Fdiscore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31368157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","discord","discordjs","framework","typescript"],"created_at":"2026-04-03T18:06:48.137Z","updated_at":"2026-04-03T18:06:51.505Z","avatar_url":"https://github.com/almeidx.png","language":"TypeScript","readme":"# @almeidx/discore\n\nA lightweight, functional Discord bot framework built on [`@discordjs/core`](https://github.com/discordjs/discord.js/tree/main/packages/core).\n\n## Features\n\n- **Functional API** — No classes, just functions. `defineCommand`, `defineEvent`, `defineButton`, etc.\n- **Type-safe options** — Command options inferred from definitions. `ctx.options.user` is typed automatically.\n- **Component routing** — Regex-based `customId` matching with named capture groups as `ctx.params`.\n- **Collectors** — `awaitComponent` (single, Promise-based) and `collectComponents` (async iterator).\n- **Hooks** — Per-command and global `beforeCommand`/`afterCommand`/`onError` hooks.\n- **Command publishing** — `publishCommands` maps definitions to the Discord API format.\n\n## Requirements\n\n- Node.js \u003e= 24.0.0\n- `@discordjs/core` \u003e= 3.0.0-dev\n\n## Install\n\n```sh\nnpm install @almeidx/discore @discordjs/core @discordjs/rest @discordjs/ws\n```\n\n## Quick start\n\n```ts\nimport { REST } from \"@discordjs/rest\";\nimport { WebSocketManager } from \"@discordjs/ws\";\nimport { GatewayIntentBits, Routes, type RESTGetAPIGatewayBotResult } from \"discord-api-types/v10\";\nimport { createBot, defineCommand, publishCommands } from \"@almeidx/discore\";\n\nconst token = process.env.DISCORD_TOKEN!;\n\nconst ping = defineCommand({\n\tdata: {\n\t\tname: \"ping\",\n\t\tdescription: \"Pong!\",\n\t},\n\thandler: async (ctx) =\u003e {\n\t\tawait ctx.reply({ content: \"Pong!\" });\n\t},\n});\n\nconst rest = new REST().setToken(token);\nconst gateway = new WebSocketManager({\n\ttoken,\n\tintents: GatewayIntentBits.Guilds,\n\tfetchGatewayInformation: () =\u003e rest.get(Routes.gatewayBot()) as Promise\u003cRESTGetAPIGatewayBotResult\u003e,\n});\n\ncreateBot({ rest, gateway, commands: [ping] });\nawait publishCommands({ rest, commands: [ping] });\nawait gateway.connect();\n```\n\n## Typed options\n\n```ts\nimport { ApplicationCommandOptionType } from \"discord-api-types/v10\";\nimport { defineCommand } from \"@almeidx/discore\";\n\nconst ban = defineCommand({\n\tdata: {\n\t\tname: \"ban\",\n\t\tdescription: \"Ban a member\",\n\t\toptions: [\n\t\t\t{ name: \"user\", type: ApplicationCommandOptionType.User, description: \"Target user\", required: true },\n\t\t\t{ name: \"reason\", type: ApplicationCommandOptionType.String, description: \"Ban reason\" },\n\t\t],\n\t},\n\thandler: async (ctx) =\u003e {\n\t\t// ctx.options.user: { user, member } (required — always present)\n\t\t// ctx.options.reason: string | undefined (optional)\n\t\tawait ctx.reply({ content: `Banned \u003c@${ctx.options.user.user.id}\u003e` });\n\t},\n});\n```\n\n## Components\n\nButtons, select menus, and modals use regex patterns with named capture groups:\n\n```ts\nimport { MessageFlags } from \"discord-api-types/v10\";\nimport { defineButton } from \"@almeidx/discore\";\n\nconst verify = defineButton({\n\tcustomId: /^verify:(?\u003cuserId\u003e\\d+)$/,\n\thandler: async (ctx) =\u003e {\n\t\tconst userId = ctx.params.userId; // extracted from the regex match\n\t\tawait ctx.reply({ content: `Verified ${userId}!`, flags: MessageFlags.Ephemeral });\n\t},\n});\n```\n\n## Collectors\n\n```ts\nconst response = await ctx.awaitComponent({\n\tfilter: (i) =\u003e i.customId === \"confirm\",\n\ttimeout: 30_000,\n});\n\n// Or iterate over multiple interactions:\nconst collector = ctx.collectComponents({\n\tfilter: (i) =\u003e i.customId.startsWith(\"vote:\"),\n\ttimeout: 60_000,\n});\n\nfor await (const interaction of collector) {\n\tawait interaction.reply({ content: \"Recorded!\", flags: MessageFlags.Ephemeral });\n}\n```\n\n## More examples\n\nSee the [`examples/`](./examples) directory.\n\n## License\n\n[Apache-2.0](./LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falmeidx%2Fdiscore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falmeidx%2Fdiscore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falmeidx%2Fdiscore/lists"}