diff --git a/actions/discord-action/.gitignore b/actions/discord-action/.gitignore new file mode 100644 index 0000000..ddc3c2a --- /dev/null +++ b/actions/discord-action/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules/ +dist/ diff --git a/actions/discord-action/Dockerfile b/actions/discord-action/Dockerfile new file mode 100644 index 0000000..5871953 --- /dev/null +++ b/actions/discord-action/Dockerfile @@ -0,0 +1,13 @@ +FROM node:24.10.0-alpine + +WORKDIR /app + +COPY package.json package-lock.json* ./ + +RUN npm install + +COPY . . + +RUN npm run build + +CMD ["npm", "run", "start"] diff --git a/actions/discord-action/README.md b/actions/discord-action/README.md new file mode 100644 index 0000000..07ef67c --- /dev/null +++ b/actions/discord-action/README.md @@ -0,0 +1,9 @@ +# Discord Action + +Discord action integration for CodeZero Hercules. + +Features: +- Create Discord webhook URLs from Webhook ID and Webhook Token. +- Build custom Discord embed fields, footers, authors, and embeds. +- Build Discord Webhook payloads with custom content, username, avatar URL, and embeds. +- Execute Discord Webhooks over HTTP POST. diff --git a/actions/discord-action/package-lock.json b/actions/discord-action/package-lock.json new file mode 100644 index 0000000..fba9bdb --- /dev/null +++ b/actions/discord-action/package-lock.json @@ -0,0 +1,2386 @@ +{ + "name": "@code0-tech/discord-action", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@code0-tech/discord-action", + "version": "1.0.0", + "dependencies": { + "@code0-tech/hercules": "^1.1.2", + "discord.js": "^14.18.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.0.0", + "typescript": "^5.9.3", + "vite": "^8.0.3", + "vitest": "^4.1.10" + } + }, + "node_modules/@clack/core": { + "version": "1.0.0-alpha.4", + "license": "MIT", + "peer": true, + "dependencies": { + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@clack/prompts": { + "version": "1.0.0-alpha.4", + "license": "MIT", + "peer": true, + "dependencies": { + "@clack/core": "1.0.0-alpha.4", + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@code0-tech/hercules": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@code0-tech/hercules/-/hercules-1.1.2.tgz", + "integrity": "sha512-cxfSKSnAQ7pDnq3nqkVLHdYkoWMJxXzUYKyAJpo0T2j8EpgGYFQ1/h+kuhLQh+CRNuFy1kcOkKbWYR3hcG60fA==", + "license": "ISC", + "bin": { + "hercules": "bin/hercules.js" + }, + "peerDependencies": { + "@code0-tech/tucana": "0.0.74", + "@grpc/grpc-js": "^1.14.3", + "@protobuf-ts/grpc-backend": "^2.11.1", + "@protobuf-ts/grpc-transport": "^2.11.1", + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1", + "reflect-metadata": "^0.2.2", + "ts-morph": "^28.0.0", + "ts-to-zod": "^5.1.0", + "zod": "^4.4.3", + "zod-to-ts": "^2.1.0" + } + }, + "node_modules/@code0-tech/tucana": { + "version": "0.0.74", + "license": "MIT", + "peer": true + }, + "node_modules/@discordjs/builders": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.14.1.tgz", + "integrity": "sha512-gSKkhXLqs96TCzk66VZuHHl8z2bQMJFGwrXC0f33ngK+FLNau4hU1PYny3DNJfNdSH+gVMzE85/d5FQ2BpcNwQ==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/formatters": "^0.6.2", + "@discordjs/util": "^1.2.0", + "@sapphire/shapeshift": "^4.0.0", + "discord-api-types": "^0.38.40", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.4", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=16.11.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==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.2.tgz", + "integrity": "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ==", + "license": "Apache-2.0", + "dependencies": { + "discord-api-types": "^0.38.33" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.3.tgz", + "integrity": "sha512-wvOylxNYJkwKjctS/Mn5GP1w9r3/rzyH+ThD1JlAca6zEdlHs8QWBBUQJpU5Q+W6DoIj/Ljh1IPlZs7hTU+UAg==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/collection": "^2.1.1", + "@discordjs/util": "^1.2.0", + "@sapphire/async-queue": "^1.5.3", + "@sapphire/snowflake": "^3.5.5", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "^0.38.50", + "magic-bytes.js": "^1.13.0", + "tslib": "^2.6.3", + "undici": "^6.27.0" + }, + "engines": { + "node": ">=18" + }, + "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/util": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.2.0.tgz", + "integrity": "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==", + "license": "Apache-2.0", + "dependencies": { + "discord-api-types": "^0.38.33" + }, + "engines": { + "node": ">=18" + }, + "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==", + "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" + }, + "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" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@oclif/core": { + "version": "4.12.0", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.8.1", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.17", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@protobuf-ts/grpc-backend": { + "version": "2.11.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1" + }, + "peerDependencies": { + "@grpc/grpc-js": "^1.8.22" + } + }, + "node_modules/@protobuf-ts/grpc-transport": { + "version": "2.11.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1" + }, + "peerDependencies": { + "@grpc/grpc-js": "^1.6.0" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.11.1", + "license": "(Apache-2.0 AND BSD-3-Clause)", + "peer": true + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.11.1", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", + "license": "MIT", + "engines": { + "node": ">=v14.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": { + "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", + "npm": ">=7.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@ts-morph/common": { + "version": "0.29.0", + "license": "MIT", + "peer": true, + "dependencies": { + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.14" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.4", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.3" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz", + "integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/async": { + "version": "3.2.6", + "license": "MIT", + "peer": true + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "license": "MIT", + "peer": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "license": "MIT", + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "license": "MIT", + "peer": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "license": "MIT", + "peer": true, + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.2.2", + "license": "MIT", + "peer": true, + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "license": "MIT", + "peer": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT", + "peer": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "license": "MIT", + "peer": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/discord-api-types": { + "version": "0.38.50", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.50.tgz", + "integrity": "sha512-J2n/bpIETX3DQ6AJ7/0xbsTLmYiJQtO/LKcXKC1YDbB56OUwtDbdXOFE8Q4g8jVGHBR2VAy1+D4ngaIgkMNV9w==", + "license": "MIT", + "workspaces": [ + "scripts/actions/documentation" + ] + }, + "node_modules/discord.js": { + "version": "14.27.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.27.0.tgz", + "integrity": "sha512-qHbFlFG2N7y3LjPySYsL6A1+BnX6bkTVgo842EX0CqVPk/KTMwZkojPHEXKsQUpWZNyz5BISNHK1cPpQw0+m4A==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/builders": "^1.14.1", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.6.2", + "@discordjs/rest": "^2.6.2", + "@discordjs/util": "^1.2.0", + "@discordjs/ws": "^1.2.3", + "@sapphire/snowflake": "3.5.5", + "discord-api-types": "^0.38.49", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "magic-bytes.js": "^1.13.0", + "tslib": "^2.6.3", + "undici": "^6.27.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT", + "peer": true + }, + "node_modules/environment": { + "version": "1.1.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "license": "MIT", + "peer": true + }, + "node_modules/expect-type": { + "version": "1.4.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT", + "peer": true + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.2", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "license": "MIT", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "license": "MIT", + "peer": true, + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.6.0", + "license": "MIT", + "peer": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.2", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "license": "MIT", + "peer": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "license": "MIT" + }, + "node_modules/log-update": { + "version": "6.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.3.0", + "license": "MIT", + "peer": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.6.0", + "license": "MIT", + "peer": true + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/long": { + "version": "5.3.2", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/magic-bytes.js": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.13.0.tgz", + "integrity": "sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT", + "peer": true + }, + "node_modules/nanoid": { + "version": "3.3.16", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "license": "MIT", + "peer": true + }, + "node_modules/pathe": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.22", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/protobufjs": { + "version": "7.6.5", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "license": "MIT", + "peer": true + }, + "node_modules/rolldown": { + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "license": "MIT", + "peer": true + }, + "node_modules/slash": { + "version": "5.1.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/text-camel-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-pascal-case": "^1.2.11" + } + }, + "node_modules/text-capital-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11", + "text-upper-case-first": "^1.2.11" + } + }, + "node_modules/text-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-camel-case": "^1.2.11", + "text-capital-case": "^1.2.11", + "text-constant-case": "^1.2.11", + "text-dot-case": "^1.2.11", + "text-header-case": "^1.2.11", + "text-is-lower-case": "^1.2.11", + "text-is-upper-case": "^1.2.11", + "text-kebab-case": "^1.2.11", + "text-lower-case": "^1.2.11", + "text-lower-case-first": "^1.2.11", + "text-no-case": "^1.2.11", + "text-param-case": "^1.2.11", + "text-pascal-case": "^1.2.11", + "text-path-case": "^1.2.11", + "text-sentence-case": "^1.2.11", + "text-snake-case": "^1.2.11", + "text-swap-case": "^1.2.11", + "text-title-case": "^1.2.11", + "text-upper-case": "^1.2.11", + "text-upper-case-first": "^1.2.11" + } + }, + "node_modules/text-constant-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11", + "text-upper-case": "^1.2.11" + } + }, + "node_modules/text-dot-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11" + } + }, + "node_modules/text-header-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-capital-case": "^1.2.11" + } + }, + "node_modules/text-is-lower-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/text-is-upper-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/text-kebab-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11" + } + }, + "node_modules/text-lower-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/text-lower-case-first": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/text-no-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-lower-case": "^1.2.11" + } + }, + "node_modules/text-param-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-dot-case": "^1.2.11" + } + }, + "node_modules/text-pascal-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11" + } + }, + "node_modules/text-path-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-dot-case": "^1.2.11" + } + }, + "node_modules/text-sentence-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11", + "text-upper-case-first": "^1.2.11" + } + }, + "node_modules/text-snake-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-dot-case": "^1.2.11" + } + }, + "node_modules/text-swap-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/text-title-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true, + "dependencies": { + "text-no-case": "^1.2.11", + "text-upper-case-first": "^1.2.11" + } + }, + "node_modules/text-upper-case": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/text-upper-case-first": { + "version": "1.2.11", + "license": "MIT", + "peer": true + }, + "node_modules/tinybench": { + "version": "2.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", + "license": "MIT" + }, + "node_modules/ts-morph": { + "version": "28.0.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@ts-morph/common": "~0.29.0", + "code-block-writer": "^13.0.3" + } + }, + "node_modules/ts-to-zod": { + "version": "5.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "@clack/prompts": "1.0.0-alpha.4", + "@oclif/core": "^4.5.4", + "@typescript/vfs": "^1.5.0", + "chokidar": "^4.0.3", + "listr2": "^9.0.4", + "slash": "^5.1.0", + "text-case": "^1.2.4", + "tslib": "^2.3.1", + "tsutils": "^3.21.0", + "typescript": "^5.2.2", + "zod": "^4.1.5" + }, + "bin": { + "ts-to-zod": "bin/run" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD", + "peer": true + }, + "node_modules/tsx": { + "version": "4.23.1", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", + "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "license": "MIT", + "peer": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "license": "MIT", + "peer": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.3", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-ts": { + "version": "2.1.0", + "license": "MIT", + "peer": true, + "peerDependencies": { + "typescript": "^5 || ^6", + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/actions/discord-action/package.json b/actions/discord-action/package.json new file mode 100644 index 0000000..a495d3b --- /dev/null +++ b/actions/discord-action/package.json @@ -0,0 +1,25 @@ +{ + "name": "@code0-tech/discord-action", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "dist/index.js", + "scripts": { + "dev": "tsx watch src/index.ts", + "typecheck": "tsc --noEmit", + "build": "vite build", + "test": "vitest run", + "start": "node dist/index.js" + }, + "dependencies": { + "@code0-tech/hercules": "^1.1.2", + "discord.js": "^14.18.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.0.0", + "typescript": "^5.9.3", + "vite": "^8.0.3", + "vitest": "^4.1.10" + } +} diff --git a/actions/discord-action/src/client.ts b/actions/discord-action/src/client.ts new file mode 100644 index 0000000..622d0e2 --- /dev/null +++ b/actions/discord-action/src/client.ts @@ -0,0 +1,58 @@ +import { Client, GatewayIntentBits, Partials } from "discord.js"; + +let clientInstance: Client | null = null; +let currentToken: string | null = null; + +export function getDiscordClient(): Client | null { + return clientInstance; +} + +export async function initDiscordClient( + token: string, + onError?: (err: Error) => void +): Promise { + if (clientInstance && currentToken === token && clientInstance.isReady()) { + return clientInstance; + } + + if (clientInstance) { + try { + await clientInstance.destroy(); + } catch { + // Ignore destruction errors + } + clientInstance = null; + } + + const client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessageReactions, + ], + partials: [Partials.Message, Partials.Channel, Partials.Reaction], + }); + + client.on("error", (error) => { + console.error("Discord Client Error:", error.message); + if (onError) { + onError(error); + } + }); + + try { + await client.login(token); + clientInstance = client; + currentToken = token; + console.log(`Discord bot logged in as ${client.user?.tag}`); + return client; + } catch (err: any) { + console.error("Failed to log in Discord bot:", err?.message || err); + if (onError) { + onError(err instanceof Error ? err : new Error(String(err))); + } + throw err; + } +} diff --git a/actions/discord-action/src/data_types/discordAttachment.ts b/actions/discord-action/src/data_types/discordAttachment.ts new file mode 100644 index 0000000..ae2fec2 --- /dev/null +++ b/actions/discord-action/src/data_types/discordAttachment.ts @@ -0,0 +1,17 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordAttachmentSchema = z.object({ + id: z.string(), + filename: z.string(), + contentType: z.string().optional(), + size: z.number(), + url: z.string(), +}); +export type DiscordAttachmentType = z.infer; + +@Identifier("DISCORD_ATTACHMENT") +@Name({ code: "en-US", content: "Discord Attachment" }) +@DisplayMessage({ code: "en-US", content: "Attachment: ${filename}" }) +@Schema(DiscordAttachmentSchema) +export class DiscordAttachment {} diff --git a/actions/discord-action/src/data_types/discordChannel.ts b/actions/discord-action/src/data_types/discordChannel.ts new file mode 100644 index 0000000..76ff60c --- /dev/null +++ b/actions/discord-action/src/data_types/discordChannel.ts @@ -0,0 +1,36 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordChannelTypeEnum = z.enum([ + "text", + "voice", + "category", + "announcement", + "thread", + "forum", + "dm", + "unknown", +]); +export type DiscordChannelTypeType = z.infer; + +export const DiscordChannelSchema = z.object({ + id: z.string(), + guildId: z.string().optional(), + name: z.string(), + type: DiscordChannelTypeEnum, + topic: z.string().optional(), + parentId: z.string().optional(), +}); +export type DiscordChannelDataType = z.infer; + +@Identifier("DISCORD_CHANNEL_TYPE") +@Name({ code: "en-US", content: "Discord Channel Type" }) +@DisplayMessage({ code: "en-US", content: "Channel Type: ${type}" }) +@Schema(DiscordChannelTypeEnum) +export class DiscordChannelType {} + +@Identifier("DISCORD_CHANNEL") +@Name({ code: "en-US", content: "Discord Channel" }) +@DisplayMessage({ code: "en-US", content: "Channel: #${name}" }) +@Schema(DiscordChannelSchema) +export class DiscordChannel {} diff --git a/actions/discord-action/src/data_types/discordCommandInteraction.ts b/actions/discord-action/src/data_types/discordCommandInteraction.ts new file mode 100644 index 0000000..b8fe992 --- /dev/null +++ b/actions/discord-action/src/data_types/discordCommandInteraction.ts @@ -0,0 +1,24 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; +import { DiscordUserSchema } from "./discordUser.js"; + +export const DiscordCommandOptionSchema = z.object({ + name: z.string(), + value: z.union([z.string(), z.number(), z.boolean()]), +}); + +export const DiscordCommandInteractionSchema = z.object({ + id: z.string(), + commandName: z.string(), + options: z.array(DiscordCommandOptionSchema), + user: DiscordUserSchema, + channelId: z.string(), + guildId: z.string().optional(), +}); +export type DiscordCommandInteractionType = z.infer; + +@Identifier("DISCORD_COMMAND_INTERACTION") +@Name({ code: "en-US", content: "Discord Command Interaction" }) +@DisplayMessage({ code: "en-US", content: "Slash Command: /${commandName}" }) +@Schema(DiscordCommandInteractionSchema) +export class DiscordCommandInteraction {} diff --git a/actions/discord-action/src/data_types/discordEmbed.ts b/actions/discord-action/src/data_types/discordEmbed.ts new file mode 100644 index 0000000..2ca5ef1 --- /dev/null +++ b/actions/discord-action/src/data_types/discordEmbed.ts @@ -0,0 +1,60 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordEmbedFieldSchema = z.object({ + name: z.string(), + value: z.string(), + inline: z.boolean().optional(), +}); +export type DiscordEmbedFieldType = z.infer; + +export const DiscordEmbedFooterSchema = z.object({ + text: z.string(), + iconUrl: z.string().optional(), +}); +export type DiscordEmbedFooterType = z.infer; + +export const DiscordEmbedAuthorSchema = z.object({ + name: z.string(), + url: z.string().optional(), + iconUrl: z.string().optional(), +}); +export type DiscordEmbedAuthorType = z.infer; + +export const DiscordEmbedSchema = z.object({ + title: z.string().optional(), + description: z.string().optional(), + url: z.string().optional(), + color: z.union([z.number(), z.string()]).optional(), + timestamp: z.string().optional(), + footer: DiscordEmbedFooterSchema.optional(), + imageUrl: z.string().optional(), + thumbnailUrl: z.string().optional(), + author: DiscordEmbedAuthorSchema.optional(), + fields: z.array(DiscordEmbedFieldSchema).optional(), +}); +export type DiscordEmbedType = z.infer; + +@Identifier("DISCORD_EMBED_FIELD") +@Name({ code: "en-US", content: "Discord Embed Field" }) +@DisplayMessage({ code: "en-US", content: "Embed Field: ${name}" }) +@Schema(DiscordEmbedFieldSchema) +export class DiscordEmbedField {} + +@Identifier("DISCORD_EMBED_FOOTER") +@Name({ code: "en-US", content: "Discord Embed Footer" }) +@DisplayMessage({ code: "en-US", content: "Embed Footer: ${text}" }) +@Schema(DiscordEmbedFooterSchema) +export class DiscordEmbedFooter {} + +@Identifier("DISCORD_EMBED_AUTHOR") +@Name({ code: "en-US", content: "Discord Embed Author" }) +@DisplayMessage({ code: "en-US", content: "Embed Author: ${name}" }) +@Schema(DiscordEmbedAuthorSchema) +export class DiscordEmbedAuthor {} + +@Identifier("DISCORD_EMBED") +@Name({ code: "en-US", content: "Discord Embed" }) +@DisplayMessage({ code: "en-US", content: "Discord Embed" }) +@Schema(DiscordEmbedSchema) +export class DiscordEmbed {} diff --git a/actions/discord-action/src/data_types/discordGuild.ts b/actions/discord-action/src/data_types/discordGuild.ts new file mode 100644 index 0000000..07f6fd2 --- /dev/null +++ b/actions/discord-action/src/data_types/discordGuild.ts @@ -0,0 +1,17 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordGuildSchema = z.object({ + id: z.string(), + name: z.string(), + icon: z.string().optional(), + ownerId: z.string(), + memberCount: z.number(), +}); +export type DiscordGuildType = z.infer; + +@Identifier("DISCORD_GUILD") +@Name({ code: "en-US", content: "Discord Guild" }) +@DisplayMessage({ code: "en-US", content: "Server: ${name}" }) +@Schema(DiscordGuildSchema) +export class DiscordGuild {} diff --git a/actions/discord-action/src/data_types/discordGuildMember.ts b/actions/discord-action/src/data_types/discordGuildMember.ts new file mode 100644 index 0000000..51acfb3 --- /dev/null +++ b/actions/discord-action/src/data_types/discordGuildMember.ts @@ -0,0 +1,18 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; +import { DiscordUserSchema } from "./discordUser.js"; + +export const DiscordGuildMemberSchema = z.object({ + user: DiscordUserSchema, + nickname: z.string().optional(), + roles: z.array(z.string()), + joinedAt: z.string().optional(), + guildId: z.string(), +}); +export type DiscordGuildMemberType = z.infer; + +@Identifier("DISCORD_GUILD_MEMBER") +@Name({ code: "en-US", content: "Discord Guild Member" }) +@DisplayMessage({ code: "en-US", content: "Discord Member: ${user.username}" }) +@Schema(DiscordGuildMemberSchema) +export class DiscordGuildMember {} diff --git a/actions/discord-action/src/data_types/discordMessage.ts b/actions/discord-action/src/data_types/discordMessage.ts new file mode 100644 index 0000000..f8aff0f --- /dev/null +++ b/actions/discord-action/src/data_types/discordMessage.ts @@ -0,0 +1,25 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; +import { DiscordUserSchema } from "./discordUser.js"; +import { DiscordEmbedSchema } from "./discordEmbed.js"; +import { DiscordAttachmentSchema } from "./discordAttachment.js"; + +export const DiscordMessageSchema = z.object({ + id: z.string(), + channelId: z.string(), + guildId: z.string().optional(), + author: DiscordUserSchema, + content: z.string(), + embeds: z.array(DiscordEmbedSchema), + attachments: z.array(DiscordAttachmentSchema), + mentions: z.array(z.string()), + referencedMessageId: z.string().optional(), + createdAt: z.string(), +}); +export type DiscordMessageType = z.infer; + +@Identifier("DISCORD_MESSAGE") +@Name({ code: "en-US", content: "Discord Message" }) +@DisplayMessage({ code: "en-US", content: "Discord Message: ${id}" }) +@Schema(DiscordMessageSchema) +export class DiscordMessage {} diff --git a/actions/discord-action/src/data_types/discordReaction.ts b/actions/discord-action/src/data_types/discordReaction.ts new file mode 100644 index 0000000..a6b4504 --- /dev/null +++ b/actions/discord-action/src/data_types/discordReaction.ts @@ -0,0 +1,17 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; +import { DiscordUserSchema } from "./discordUser.js"; + +export const DiscordReactionSchema = z.object({ + emoji: z.string(), + messageId: z.string(), + channelId: z.string(), + user: DiscordUserSchema, +}); +export type DiscordReactionType = z.infer; + +@Identifier("DISCORD_REACTION") +@Name({ code: "en-US", content: "Discord Reaction" }) +@DisplayMessage({ code: "en-US", content: "Reaction: ${emoji}" }) +@Schema(DiscordReactionSchema) +export class DiscordReaction {} diff --git a/actions/discord-action/src/data_types/discordRole.ts b/actions/discord-action/src/data_types/discordRole.ts new file mode 100644 index 0000000..8d4cabe --- /dev/null +++ b/actions/discord-action/src/data_types/discordRole.ts @@ -0,0 +1,17 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordRoleSchema = z.object({ + id: z.string(), + guildId: z.string(), + name: z.string(), + color: z.number(), + position: z.number(), +}); +export type DiscordRoleType = z.infer; + +@Identifier("DISCORD_ROLE") +@Name({ code: "en-US", content: "Discord Role" }) +@DisplayMessage({ code: "en-US", content: "Role: @${name}" }) +@Schema(DiscordRoleSchema) +export class DiscordRole {} diff --git a/actions/discord-action/src/data_types/discordUser.ts b/actions/discord-action/src/data_types/discordUser.ts new file mode 100644 index 0000000..0908041 --- /dev/null +++ b/actions/discord-action/src/data_types/discordUser.ts @@ -0,0 +1,17 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordUserSchema = z.object({ + id: z.string(), + username: z.string(), + globalName: z.string().optional(), + avatar: z.string().optional(), + bot: z.boolean(), +}); +export type DiscordUserType = z.infer; + +@Identifier("DISCORD_USER") +@Name({ code: "en-US", content: "Discord User" }) +@DisplayMessage({ code: "en-US", content: "Discord User: ${username}" }) +@Schema(DiscordUserSchema) +export class DiscordUser {} diff --git a/actions/discord-action/src/data_types/discordWebhook.ts b/actions/discord-action/src/data_types/discordWebhook.ts new file mode 100644 index 0000000..0d3d486 --- /dev/null +++ b/actions/discord-action/src/data_types/discordWebhook.ts @@ -0,0 +1,15 @@ +import { DisplayMessage, Identifier, Name, Schema } from "@code0-tech/hercules"; +import { z } from "zod"; + +export const DiscordWebhookResponseDataSchema = z.object({ + success: z.boolean(), + statusCode: z.number(), + message: z.string(), +}); +export type DiscordWebhookResponseData = z.infer; + +@Identifier("DISCORD_WEBHOOK_RESPONSE") +@Name({ code: "en-US", content: "Discord Webhook Response" }) +@DisplayMessage({ code: "en-US", content: "Discord Webhook Response status: ${statusCode}" }) +@Schema(DiscordWebhookResponseDataSchema) +export class DiscordWebhookResponseDataType {} diff --git a/actions/discord-action/src/events/discordMemberJoined.ts b/actions/discord-action/src/events/discordMemberJoined.ts new file mode 100644 index 0000000..fdbab77 --- /dev/null +++ b/actions/discord-action/src/events/discordMemberJoined.ts @@ -0,0 +1,9 @@ +import { Description, DisplayIcon, DisplayMessage, Identifier, Name, Signature } from "@code0-tech/hercules"; + +@Identifier("DiscordMemberJoined") +@DisplayIcon("simple:discord") +@Name({ code: "en-US", content: "Discord member joined" }) +@Description({ code: "en-US", content: "Triggered when a new member joins a guild." }) +@DisplayMessage({ code: "en-US", content: "Discord member joined" }) +@Signature("(): DISCORD_GUILD_MEMBER") +export class DiscordMemberJoined {} diff --git a/actions/discord-action/src/events/discordMemberLeft.ts b/actions/discord-action/src/events/discordMemberLeft.ts new file mode 100644 index 0000000..65c9bd1 --- /dev/null +++ b/actions/discord-action/src/events/discordMemberLeft.ts @@ -0,0 +1,9 @@ +import { Description, DisplayIcon, DisplayMessage, Identifier, Name, Signature } from "@code0-tech/hercules"; + +@Identifier("DiscordMemberLeft") +@DisplayIcon("simple:discord") +@Name({ code: "en-US", content: "Discord member left" }) +@Description({ code: "en-US", content: "Triggered when a member leaves a guild." }) +@DisplayMessage({ code: "en-US", content: "Discord member left" }) +@Signature("(): DISCORD_GUILD_MEMBER") +export class DiscordMemberLeft {} diff --git a/actions/discord-action/src/events/discordMessageCreated.ts b/actions/discord-action/src/events/discordMessageCreated.ts new file mode 100644 index 0000000..3991d61 --- /dev/null +++ b/actions/discord-action/src/events/discordMessageCreated.ts @@ -0,0 +1,15 @@ +import { Description, DisplayIcon, DisplayMessage, EventSetting, Identifier, Name, Signature } from "@code0-tech/hercules"; + +@Identifier("DiscordMessageCreated") +@DisplayIcon("simple:discord") +@Name({ code: "en-US", content: "Discord message created" }) +@Description({ code: "en-US", content: "Triggered when a message is posted in a channel. Ignores bot messages." }) +@DisplayMessage({ code: "en-US", content: "Discord message created" }) +@Signature("(channelId?: string): DISCORD_MESSAGE") +@EventSetting({ + identifier: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "Optional channel ID to filter incoming messages." }], + optional: true, +}) +export class DiscordMessageCreated {} diff --git a/actions/discord-action/src/events/discordReactionAdded.ts b/actions/discord-action/src/events/discordReactionAdded.ts new file mode 100644 index 0000000..e2952cd --- /dev/null +++ b/actions/discord-action/src/events/discordReactionAdded.ts @@ -0,0 +1,15 @@ +import { Description, DisplayIcon, DisplayMessage, EventSetting, Identifier, Name, Signature } from "@code0-tech/hercules"; + +@Identifier("DiscordReactionAdded") +@DisplayIcon("simple:discord") +@Name({ code: "en-US", content: "Discord reaction added" }) +@Description({ code: "en-US", content: "Triggered when a user adds a reaction to a message." }) +@DisplayMessage({ code: "en-US", content: "Discord reaction added" }) +@Signature("(channelId?: string): DISCORD_REACTION") +@EventSetting({ + identifier: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "Optional channel ID to filter reactions." }], + optional: true, +}) +export class DiscordReactionAdded {} diff --git a/actions/discord-action/src/events/discordSlashCommandInvoked.ts b/actions/discord-action/src/events/discordSlashCommandInvoked.ts new file mode 100644 index 0000000..68027b3 --- /dev/null +++ b/actions/discord-action/src/events/discordSlashCommandInvoked.ts @@ -0,0 +1,14 @@ +import { Description, DisplayIcon, DisplayMessage, EventSetting, Identifier, Name, Signature } from "@code0-tech/hercules"; + +@Identifier("DiscordSlashCommandInvoked") +@DisplayIcon("simple:discord") +@Name({ code: "en-US", content: "Discord slash command invoked" }) +@Description({ code: "en-US", content: "Triggered when a slash command is executed by a user." }) +@DisplayMessage({ code: "en-US", content: "Slash command /${commandName} invoked" }) +@Signature("(commandName: string): DISCORD_COMMAND_INTERACTION") +@EventSetting({ + identifier: "commandName", + name: [{ code: "en-US", content: "Command Name" }], + description: [{ code: "en-US", content: "The slash command name (e.g. ping)." }], +}) +export class DiscordSlashCommandInvoked {} diff --git a/actions/discord-action/src/functions/members/discordAddRoleToMember.ts b/actions/discord-action/src/functions/members/discordAddRoleToMember.ts new file mode 100644 index 0000000..b407063 --- /dev/null +++ b/actions/discord-action/src/functions/members/discordAddRoleToMember.ts @@ -0,0 +1,46 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { handleDiscordError, mapDiscordGuildMember } from "../../helpers.js"; +import { DiscordGuildMemberType } from "../../data_types/discordGuildMember.js"; + +@Identifier("discordAddRoleToMember") +@Signature("(guildId: string, userId: string, roleId: string): DISCORD_GUILD_MEMBER") +@Name({ code: "en-US", content: "Add role to member" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Add role to member" }) +@Documentation({ code: "en-US", content: "Assigns a role to a guild member." }) +@Description({ code: "en-US", content: "Assign a role." }) +@Parameter({ + runtimeName: "guildId", + name: [{ code: "en-US", content: "Guild ID" }], + description: [{ code: "en-US", content: "The ID of the server/guild." }], +}) +@Parameter({ + runtimeName: "userId", + name: [{ code: "en-US", content: "User ID" }], + description: [{ code: "en-US", content: "The ID of the user." }], +}) +@Parameter({ + runtimeName: "roleId", + name: [{ code: "en-US", content: "Role ID" }], + description: [{ code: "en-US", content: "The ID of the role to assign." }], +}) +export class DiscordAddRoleToMemberFunction { + async run(_context: unknown, guildId: string, userId: string, roleId: string): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const guild = await client.guilds.fetch(guildId); + if (!guild) throw new Error(`Guild ${guildId} not found.`); + + const member = await guild.members.fetch(userId); + if (!member) throw new Error(`Member ${userId} not found in guild.`); + + const updatedMember = await member.roles.add(roleId); + return mapDiscordGuildMember(updatedMember); + } catch (error) { + return handleDiscordError(error, "ERROR_ADDING_ROLE", "Failed to add role to Discord member."); + } + } +} diff --git a/actions/discord-action/src/functions/members/discordRemoveRoleFromMember.ts b/actions/discord-action/src/functions/members/discordRemoveRoleFromMember.ts new file mode 100644 index 0000000..233dbf4 --- /dev/null +++ b/actions/discord-action/src/functions/members/discordRemoveRoleFromMember.ts @@ -0,0 +1,46 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { handleDiscordError, mapDiscordGuildMember } from "../../helpers.js"; +import { DiscordGuildMemberType } from "../../data_types/discordGuildMember.js"; + +@Identifier("discordRemoveRoleFromMember") +@Signature("(guildId: string, userId: string, roleId: string): DISCORD_GUILD_MEMBER") +@Name({ code: "en-US", content: "Remove role from member" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Remove role from member" }) +@Documentation({ code: "en-US", content: "Removes a role from a guild member." }) +@Description({ code: "en-US", content: "Remove a role." }) +@Parameter({ + runtimeName: "guildId", + name: [{ code: "en-US", content: "Guild ID" }], + description: [{ code: "en-US", content: "The ID of the server/guild." }], +}) +@Parameter({ + runtimeName: "userId", + name: [{ code: "en-US", content: "User ID" }], + description: [{ code: "en-US", content: "The ID of the user." }], +}) +@Parameter({ + runtimeName: "roleId", + name: [{ code: "en-US", content: "Role ID" }], + description: [{ code: "en-US", content: "The ID of the role to remove." }], +}) +export class DiscordRemoveRoleFromMemberFunction { + async run(_context: unknown, guildId: string, userId: string, roleId: string): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const guild = await client.guilds.fetch(guildId); + if (!guild) throw new Error(`Guild ${guildId} not found.`); + + const member = await guild.members.fetch(userId); + if (!member) throw new Error(`Member ${userId} not found in guild.`); + + const updatedMember = await member.roles.remove(roleId); + return mapDiscordGuildMember(updatedMember); + } catch (error) { + return handleDiscordError(error, "ERROR_REMOVING_ROLE", "Failed to remove role from Discord member."); + } + } +} diff --git a/actions/discord-action/src/functions/messaging/discordAddReaction.ts b/actions/discord-action/src/functions/messaging/discordAddReaction.ts new file mode 100644 index 0000000..169ce1e --- /dev/null +++ b/actions/discord-action/src/functions/messaging/discordAddReaction.ts @@ -0,0 +1,47 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { handleDiscordError } from "../../helpers.js"; + +@Identifier("discordAddReaction") +@Signature("(channelId: string, messageId: string, emoji: string): BOOLEAN") +@Name({ code: "en-US", content: "Add reaction" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Add reaction ${emoji}" }) +@Documentation({ code: "en-US", content: "Reacts to a message with a unicode or custom emoji." }) +@Description({ code: "en-US", content: "React with a unicode or custom emoji." }) +@Parameter({ + runtimeName: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "The ID of the channel." }], +}) +@Parameter({ + runtimeName: "messageId", + name: [{ code: "en-US", content: "Message ID" }], + description: [{ code: "en-US", content: "The ID of the message to react to." }], +}) +@Parameter({ + runtimeName: "emoji", + name: [{ code: "en-US", content: "Emoji" }], + description: [{ code: "en-US", content: "Unicode emoji or custom emoji (e.g. 👍 or emoji ID)." }], +}) +export class DiscordAddReactionFunction { + async run(_context: unknown, channelId: string, messageId: string, emoji: string): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const channel = await client.channels.fetch(channelId); + if (!channel || !channel.isTextBased() || !("messages" in channel)) { + throw new Error(`Channel ${channelId} is not a text channel.`); + } + + const targetMsg = await channel.messages.fetch(messageId); + if (!targetMsg) throw new Error(`Message ${messageId} not found.`); + + await targetMsg.react(emoji); + return true; + } catch (error) { + return handleDiscordError(error, "ERROR_ADDING_REACTION", "Failed to add reaction to Discord message."); + } + } +} diff --git a/actions/discord-action/src/functions/messaging/discordDeleteMessage.ts b/actions/discord-action/src/functions/messaging/discordDeleteMessage.ts new file mode 100644 index 0000000..a1f7491 --- /dev/null +++ b/actions/discord-action/src/functions/messaging/discordDeleteMessage.ts @@ -0,0 +1,42 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { handleDiscordError } from "../../helpers.js"; + +@Identifier("discordDeleteMessage") +@Signature("(channelId: string, messageId: string): BOOLEAN") +@Name({ code: "en-US", content: "Delete message" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Delete message" }) +@Documentation({ code: "en-US", content: "Deletes a message from a channel." }) +@Description({ code: "en-US", content: "Delete a message." }) +@Parameter({ + runtimeName: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "The ID of the channel." }], +}) +@Parameter({ + runtimeName: "messageId", + name: [{ code: "en-US", content: "Message ID" }], + description: [{ code: "en-US", content: "The ID of the message to delete." }], +}) +export class DiscordDeleteMessageFunction { + async run(_context: unknown, channelId: string, messageId: string): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const channel = await client.channels.fetch(channelId); + if (!channel || !channel.isTextBased() || !("messages" in channel)) { + throw new Error(`Channel ${channelId} is not a text channel.`); + } + + const targetMsg = await channel.messages.fetch(messageId); + if (!targetMsg) throw new Error(`Message ${messageId} not found.`); + + await targetMsg.delete(); + return true; + } catch (error) { + return handleDiscordError(error, "ERROR_DELETING_MESSAGE", "Failed to delete Discord message."); + } + } +} diff --git a/actions/discord-action/src/functions/messaging/discordEditMessage.ts b/actions/discord-action/src/functions/messaging/discordEditMessage.ts new file mode 100644 index 0000000..3679bf9 --- /dev/null +++ b/actions/discord-action/src/functions/messaging/discordEditMessage.ts @@ -0,0 +1,51 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { handleDiscordError, mapDiscordMessage } from "../../helpers.js"; +import { DiscordMessageType } from "../../data_types/discordMessage.js"; + +@Identifier("discordEditMessage") +@Signature("(channelId: string, messageId: string, content: string): DISCORD_MESSAGE") +@Name({ code: "en-US", content: "Edit message" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Edit message" }) +@Documentation({ code: "en-US", content: "Edits a message previously sent by the bot." }) +@Description({ code: "en-US", content: "Edit a message previously sent by the bot." }) +@Parameter({ + runtimeName: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "The ID of the target channel." }], +}) +@Parameter({ + runtimeName: "messageId", + name: [{ code: "en-US", content: "Message ID" }], + description: [{ code: "en-US", content: "The ID of the message to edit." }], +}) +@Parameter({ + runtimeName: "content", + name: [{ code: "en-US", content: "Content" }], + description: [{ code: "en-US", content: "The new content for the message." }], +}) +export class DiscordEditMessageFunction { + async run(_context: unknown, channelId: string, messageId: string, content: string): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const channel = await client.channels.fetch(channelId); + if (!channel || !channel.isTextBased() || !("messages" in channel)) { + throw new Error(`Channel ${channelId} is not a text channel.`); + } + + const targetMsg = await channel.messages.fetch(messageId); + if (!targetMsg) throw new Error(`Message ${messageId} not found.`); + + const editedMsg = await targetMsg.edit({ + content: content, + }); + + return mapDiscordMessage(editedMsg); + } catch (error) { + return handleDiscordError(error, "ERROR_EDITING_MESSAGE", "Failed to edit Discord message."); + } + } +} diff --git a/actions/discord-action/src/functions/messaging/discordReplyToMessage.ts b/actions/discord-action/src/functions/messaging/discordReplyToMessage.ts new file mode 100644 index 0000000..4d05d89 --- /dev/null +++ b/actions/discord-action/src/functions/messaging/discordReplyToMessage.ts @@ -0,0 +1,71 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { createDiscordEmbedBuilder, handleDiscordError, mapDiscordMessage } from "../../helpers.js"; +import { DiscordEmbedType } from "../../data_types/discordEmbed.js"; +import { DiscordMessageType } from "../../data_types/discordMessage.js"; + +@Identifier("discordReplyToMessage") +@Signature("(channelId: string, messageId: string, content: string, embed?: DISCORD_EMBED): DISCORD_MESSAGE") +@Name({ code: "en-US", content: "Reply to message" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Reply to message" }) +@Documentation({ code: "en-US", content: "Replies to a specific message in a channel." }) +@Description({ code: "en-US", content: "Reply with message reference." }) +@Parameter({ + runtimeName: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "The ID of the target channel." }], +}) +@Parameter({ + runtimeName: "messageId", + name: [{ code: "en-US", content: "Message ID" }], + description: [{ code: "en-US", content: "The ID of the message to reply to." }], +}) +@Parameter({ + runtimeName: "content", + name: [{ code: "en-US", content: "Content" }], + description: [{ code: "en-US", content: "The text content of the reply." }], +}) +@Parameter({ + runtimeName: "embed", + name: [{ code: "en-US", content: "Embed" }], + description: [{ code: "en-US", content: "Optional DISCORD_EMBED object." }], + optional: true, +}) +export class DiscordReplyToMessageFunction { + async run( + _context: unknown, + channelId: string, + messageId: string, + content: string, + embed?: DiscordEmbedType + ): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const channel = await client.channels.fetch(channelId); + if (!channel || !channel.isTextBased() || !("messages" in channel)) { + throw new Error(`Channel ${channelId} is not a text channel.`); + } + + const targetMsg = await channel.messages.fetch(messageId); + if (!targetMsg) throw new Error(`Message ${messageId} not found.`); + + const embeds = []; + if (embed) { + const builder = createDiscordEmbedBuilder(embed); + if (builder) embeds.push(builder); + } + + const replyMsg = await targetMsg.reply({ + content: content || undefined, + embeds: embeds.length > 0 ? embeds : undefined, + }); + + return mapDiscordMessage(replyMsg); + } catch (error) { + return handleDiscordError(error, "ERROR_REPLYING_TO_MESSAGE", "Failed to reply to Discord message."); + } + } +} diff --git a/actions/discord-action/src/functions/messaging/discordSendDirectMessage.ts b/actions/discord-action/src/functions/messaging/discordSendDirectMessage.ts new file mode 100644 index 0000000..e045423 --- /dev/null +++ b/actions/discord-action/src/functions/messaging/discordSendDirectMessage.ts @@ -0,0 +1,55 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { createDiscordEmbedBuilder, handleDiscordError, mapDiscordMessage } from "../../helpers.js"; +import { DiscordEmbedType } from "../../data_types/discordEmbed.js"; +import { DiscordMessageType } from "../../data_types/discordMessage.js"; + +@Identifier("discordSendDirectMessage") +@Signature("(userId: string, content: string, embed?: DISCORD_EMBED): DISCORD_MESSAGE") +@Name({ code: "en-US", content: "Send direct message" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Send direct message" }) +@Documentation({ code: "en-US", content: "Opens a DM channel with a user and sends a message." }) +@Description({ code: "en-US", content: "Open a DM channel and send a message." }) +@Parameter({ + runtimeName: "userId", + name: [{ code: "en-US", content: "User ID" }], + description: [{ code: "en-US", content: "The ID of the target user." }], +}) +@Parameter({ + runtimeName: "content", + name: [{ code: "en-US", content: "Content" }], + description: [{ code: "en-US", content: "The text content of the message." }], +}) +@Parameter({ + runtimeName: "embed", + name: [{ code: "en-US", content: "Embed" }], + description: [{ code: "en-US", content: "Optional DISCORD_EMBED object." }], + optional: true, +}) +export class DiscordSendDirectMessageFunction { + async run(_context: unknown, userId: string, content: string, embed?: DiscordEmbedType): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const user = await client.users.fetch(userId); + if (!user) throw new Error(`User ${userId} not found.`); + + const embeds = []; + if (embed) { + const builder = createDiscordEmbedBuilder(embed); + if (builder) embeds.push(builder); + } + + const msg = await user.send({ + content: content || undefined, + embeds: embeds.length > 0 ? embeds : undefined, + }); + + return mapDiscordMessage(msg); + } catch (error) { + return handleDiscordError(error, "ERROR_SENDING_DM", "Failed to send DM to Discord user."); + } + } +} diff --git a/actions/discord-action/src/functions/messaging/discordSendMessage.ts b/actions/discord-action/src/functions/messaging/discordSendMessage.ts new file mode 100644 index 0000000..8a103eb --- /dev/null +++ b/actions/discord-action/src/functions/messaging/discordSendMessage.ts @@ -0,0 +1,57 @@ +import { Description, DisplayIcon, DisplayMessage, Documentation, Identifier, Name, Parameter, Signature } from "@code0-tech/hercules"; +import { getDiscordClient } from "../../client.js"; +import { createDiscordEmbedBuilder, handleDiscordError, mapDiscordMessage } from "../../helpers.js"; +import { DiscordEmbedType } from "../../data_types/discordEmbed.js"; +import { DiscordMessageType } from "../../data_types/discordMessage.js"; + +@Identifier("discordSendMessage") +@Signature("(channelId: string, content: string, embed?: DISCORD_EMBED): DISCORD_MESSAGE") +@Name({ code: "en-US", content: "Send message" }) +@DisplayIcon("simple:discord") +@DisplayMessage({ code: "en-US", content: "Send message to channel" }) +@Documentation({ code: "en-US", content: "Sends a message with optional text content and embed to a Discord channel." }) +@Description({ code: "en-US", content: "Send a message to a channel." }) +@Parameter({ + runtimeName: "channelId", + name: [{ code: "en-US", content: "Channel ID" }], + description: [{ code: "en-US", content: "The ID of the target channel." }], +}) +@Parameter({ + runtimeName: "content", + name: [{ code: "en-US", content: "Content" }], + description: [{ code: "en-US", content: "The text content of the message." }], +}) +@Parameter({ + runtimeName: "embed", + name: [{ code: "en-US", content: "Embed" }], + description: [{ code: "en-US", content: "Optional DISCORD_EMBED object." }], + optional: true, +}) +export class DiscordSendMessageFunction { + async run(_context: unknown, channelId: string, content: string, embed?: DiscordEmbedType): Promise { + try { + const client = getDiscordClient(); + if (!client) throw new Error("Discord client is not initialized."); + + const channel = await client.channels.fetch(channelId); + if (!channel || !channel.isTextBased() || !("send" in channel)) { + throw new Error(`Channel ${channelId} is not a valid text channel.`); + } + + const embeds = []; + if (embed) { + const builder = createDiscordEmbedBuilder(embed); + if (builder) embeds.push(builder); + } + + const msg = await channel.send({ + content: content || undefined, + embeds: embeds.length > 0 ? embeds : undefined, + }); + + return mapDiscordMessage(msg); + } catch (error) { + return handleDiscordError(error, "ERROR_SENDING_MESSAGE", "Failed to send message to Discord channel."); + } + } +} diff --git a/actions/discord-action/src/helpers.ts b/actions/discord-action/src/helpers.ts new file mode 100644 index 0000000..ad09f6b --- /dev/null +++ b/actions/discord-action/src/helpers.ts @@ -0,0 +1,311 @@ +import { + User, + GuildMember, + Message, + Channel, + Guild, + Role, + ChannelType as DiscordJsChannelType, + EmbedBuilder, + DiscordAPIError, +} from "discord.js"; +import { RuntimeError } from "@code0-tech/hercules"; +import { DiscordUserType } from "./data_types/discordUser.js"; +import { DiscordGuildMemberType } from "./data_types/discordGuildMember.js"; +import { DiscordMessageType } from "./data_types/discordMessage.js"; +import { DiscordChannelDataType, DiscordChannelTypeType } from "./data_types/discordChannel.js"; +import { DiscordGuildType } from "./data_types/discordGuild.js"; +import { DiscordRoleType } from "./data_types/discordRole.js"; +import { DiscordAttachmentType } from "./data_types/discordAttachment.js"; +import { DiscordEmbedType } from "./data_types/discordEmbed.js"; + +export function handleDiscordError( + error: any, + fallbackCode = "DISCORD_ERROR", + fallbackMessage = "A Discord API error occurred." +): never { + if (error instanceof DiscordAPIError || (error && typeof error.code === "number")) { + const code = error.code; + const msg = error.message || fallbackMessage; + switch (code) { + case 10003: + throw new RuntimeError("DISCORD_UNKNOWN_CHANNEL", `Unknown channel: ${msg}`); + case 10008: + throw new RuntimeError("DISCORD_UNKNOWN_MESSAGE", `Unknown message: ${msg}`); + case 10013: + throw new RuntimeError("DISCORD_UNKNOWN_USER", `Unknown user: ${msg}`); + case 10007: + throw new RuntimeError("DISCORD_UNKNOWN_MEMBER", `Unknown member: ${msg}`); + case 10004: + throw new RuntimeError("DISCORD_UNKNOWN_GUILD", `Unknown guild: ${msg}`); + case 10011: + throw new RuntimeError("DISCORD_UNKNOWN_ROLE", `Unknown role: ${msg}`); + case 50013: + throw new RuntimeError("DISCORD_MISSING_PERMISSIONS", `Missing permissions: ${msg}`); + case 50001: + throw new RuntimeError("DISCORD_MISSING_ACCESS", `Missing access: ${msg}`); + case 50035: + throw new RuntimeError("DISCORD_INVALID_FORM_BODY", `Invalid form body: ${msg}`); + default: + throw new RuntimeError(`DISCORD_API_ERROR_${code}`, msg); + } + } + + if (error instanceof RuntimeError) { + throw error; + } + + if (typeof error === "string") { + throw new RuntimeError(fallbackCode, error); + } + + throw new RuntimeError(fallbackCode, error?.message || fallbackMessage); +} + +export function cleanString(val?: any): string | undefined { + if (val === null || val === undefined) return undefined; + const str = String(val).trim(); + return str.length > 0 ? str : undefined; +} + +export function cleanUrl(val?: any): string | undefined { + const str = cleanString(val); + if (!str) return undefined; + if (str.startsWith("http://") || str.startsWith("https://")) { + return str; + } + return undefined; +} + +export function mapDiscordUser(user: User): DiscordUserType { + return { + id: user.id, + username: user.username, + globalName: user.globalName || undefined, + avatar: user.avatarURL() || undefined, + bot: user.bot, + }; +} + +export function mapDiscordGuildMember(member: GuildMember): DiscordGuildMemberType { + return { + user: mapDiscordUser(member.user), + nickname: member.nickname || undefined, + roles: Array.from(member.roles.cache.keys()), + joinedAt: member.joinedAt?.toISOString(), + guildId: member.guild.id, + }; +} + +export function mapChannelType(type: DiscordJsChannelType): DiscordChannelTypeType { + switch (type) { + case DiscordJsChannelType.GuildText: + return "text"; + case DiscordJsChannelType.GuildVoice: + return "voice"; + case DiscordJsChannelType.GuildCategory: + return "category"; + case DiscordJsChannelType.GuildAnnouncement: + return "announcement"; + case DiscordJsChannelType.PublicThread: + case DiscordJsChannelType.PrivateThread: + case DiscordJsChannelType.AnnouncementThread: + return "thread"; + case DiscordJsChannelType.GuildForum: + return "forum"; + case DiscordJsChannelType.DM: + case DiscordJsChannelType.GroupDM: + return "dm"; + default: + return "unknown"; + } +} + +export function mapDiscordChannel(channel: Channel): DiscordChannelDataType { + const ch = channel as any; + return { + id: channel.id, + guildId: ch.guildId || ch.guild?.id || undefined, + name: ch.name || "DM", + type: mapChannelType(ch.type), + topic: ch.topic || undefined, + parentId: ch.parentId || undefined, + }; +} + +export function mapDiscordGuild(guild: Guild): DiscordGuildType { + return { + id: guild.id, + name: guild.name, + icon: guild.iconURL() || undefined, + ownerId: guild.ownerId, + memberCount: guild.memberCount, + }; +} + +export function mapDiscordRole(role: Role): DiscordRoleType { + return { + id: role.id, + guildId: role.guild.id, + name: role.name, + color: role.color, + position: role.position, + }; +} + +export function mapDiscordMessage(msg: Message): DiscordMessageType { + const embeds: DiscordEmbedType[] = msg.embeds.map((e) => ({ + title: e.title || undefined, + description: e.description || undefined, + url: e.url || undefined, + color: e.color || undefined, + timestamp: e.timestamp ? new Date(e.timestamp).toISOString() : undefined, + footer: e.footer ? { text: e.footer.text, iconUrl: e.footer.iconURL || undefined } : undefined, + imageUrl: e.image?.url || undefined, + thumbnailUrl: e.thumbnail?.url || undefined, + author: e.author ? { name: e.author.name, url: e.author.url || undefined, iconUrl: e.author.iconURL || undefined } : undefined, + fields: e.fields ? e.fields.map((f) => ({ name: f.name, value: f.value, inline: f.inline })) : undefined, + })); + + const attachments: DiscordAttachmentType[] = Array.from(msg.attachments.values()).map((a) => ({ + id: a.id, + filename: a.name, + contentType: a.contentType || undefined, + size: a.size, + url: a.url, + })); + + return { + id: msg.id, + channelId: msg.channelId, + guildId: msg.guildId || undefined, + author: mapDiscordUser(msg.author), + content: msg.content, + embeds, + attachments, + mentions: Array.from(msg.mentions.users.keys()), + referencedMessageId: msg.reference?.messageId || undefined, + createdAt: msg.createdAt.toISOString(), + }; +} + +export function createDiscordEmbedBuilder(rawEmbed: any): EmbedBuilder | null { + if (!rawEmbed) return null; + + let embedData = rawEmbed; + if (typeof embedData === "string") { + try { + embedData = JSON.parse(embedData); + } catch { + return null; + } + } + + if (typeof embedData !== "object") return null; + + const builder = new EmbedBuilder(); + let hasContent = false; + + const title = cleanString(embedData.title); + if (title) { + builder.setTitle(title); + hasContent = true; + } + + const description = cleanString(embedData.description); + if (description) { + builder.setDescription(description); + hasContent = true; + } + + const url = cleanUrl(embedData.url); + if (url) { + builder.setURL(url); + } + + if (embedData.color !== undefined && embedData.color !== null) { + if (typeof embedData.color === "number" && !isNaN(embedData.color)) { + builder.setColor(embedData.color); + } else if (typeof embedData.color === "string") { + let hex = embedData.color.trim(); + if (hex.startsWith("#")) { + hex = hex.slice(1); + } else if (hex.startsWith("0x") || hex.startsWith("0X")) { + hex = hex.slice(2); + } + const colorNum = parseInt(hex, 16); + if (!isNaN(colorNum) && colorNum >= 0 && colorNum <= 0xffffff) { + builder.setColor(colorNum); + } + } + } + + const timestampStr = cleanString(embedData.timestamp); + if (timestampStr) { + const date = new Date(timestampStr); + if (!isNaN(date.getTime())) { + builder.setTimestamp(date); + } + } + + const footer = embedData.footer; + if (footer && typeof footer === "object") { + const footerText = cleanString(footer.text); + if (footerText) { + const footerIcon = cleanUrl(footer.iconUrl || footer.icon_url || footer.iconURL); + builder.setFooter({ + text: footerText, + iconURL: footerIcon, + }); + hasContent = true; + } + } + + const imageUrl = cleanUrl( + embedData.imageUrl || embedData.image_url || (embedData.image && embedData.image.url) + ); + if (imageUrl) { + builder.setImage(imageUrl); + hasContent = true; + } + + const thumbnailUrl = cleanUrl( + embedData.thumbnailUrl || embedData.thumbnail_url || (embedData.thumbnail && embedData.thumbnail.url) + ); + if (thumbnailUrl) { + builder.setThumbnail(thumbnailUrl); + hasContent = true; + } + + const author = embedData.author; + if (author && typeof author === "object") { + const authorName = cleanString(author.name); + if (authorName) { + const authorUrl = cleanUrl(author.url); + const authorIcon = cleanUrl(author.iconUrl || author.icon_url || author.iconURL); + builder.setAuthor({ + name: authorName, + url: authorUrl, + iconURL: authorIcon, + }); + hasContent = true; + } + } + + if (Array.isArray(embedData.fields) && embedData.fields.length > 0) { + const validFields = embedData.fields + .map((f: any) => ({ + name: cleanString(f.name) || "", + value: cleanString(f.value) || "", + inline: Boolean(f.inline), + })) + .filter((f: any) => f.name.length > 0 && f.value.length > 0); + + if (validFields.length > 0) { + builder.addFields(validFields); + hasContent = true; + } + } + + return hasContent ? builder : null; +} diff --git a/actions/discord-action/src/index.ts b/actions/discord-action/src/index.ts new file mode 100644 index 0000000..a088ef7 --- /dev/null +++ b/actions/discord-action/src/index.ts @@ -0,0 +1,89 @@ +import "reflect-metadata"; +import { Action, CodeZeroEvent } from "@code0-tech/hercules"; + +import { + DiscordEmbedAuthorDataType, + DiscordEmbedDataType, + DiscordEmbedFooterDataType, +} from "./data_types/discordEmbed.js"; +import { + DiscordWebhookResponseDataType, +} from "./data_types/discordWebhook.js"; + +import { CreateDiscordEmbedAuthorFunction } from "./functions/createDiscordEmbedAuthorFunction.js"; +import { CreateDiscordEmbedFooterFunction } from "./functions/createDiscordEmbedFooterFunction.js"; +import { CreateDiscordEmbedFunction } from "./functions/createDiscordEmbedFunction.js"; +import { CreateDiscordWebhookUrlFunction } from "./functions/createDiscordWebhookUrlFunction.js"; +import { SendDiscordWebhookFunction } from "./functions/sendDiscordWebhookFunction.js"; + +const action = new Action( + process.env.ACTION_ID ?? "discord-action", + process.env.VERSION ?? "1.0.0", + process.env.AQUILA_URL ?? "127.0.0.1:8081", + "code0-tech", + "simple:discord", + "Discord integration: create webhook URLs, construct rich embeds, and send webhooks.", + [{ code: "en-US", content: "Discord" }], + [ + { + identifier: "webhook_url", + type: "TEXT", + defaultValue: "", + name: [{ code: "en-US", content: "Webhook URL" }], + description: [{ code: "en-US", content: "Default Discord Webhook URL to use as fallback." }], + linkedDataTypes: ["TEXT"], + }, + { + identifier: "username", + type: "TEXT", + defaultValue: "", + name: [{ code: "en-US", content: "Username" }], + description: [{ code: "en-US", content: "Default username for the Discord webhook poster." }], + linkedDataTypes: ["TEXT"], + }, + { + identifier: "avatar_url", + type: "TEXT", + defaultValue: "", + name: [{ code: "en-US", content: "Avatar URL" }], + description: [{ code: "en-US", content: "Default avatar image URL for the Discord webhook poster." }], + linkedDataTypes: ["TEXT"], + }, + ] +); + +action.registerDataTypeClass(DiscordEmbedFooterDataType); +action.registerDataTypeClass(DiscordEmbedAuthorDataType); +action.registerDataTypeClass(DiscordEmbedDataType); +action.registerDataTypeClass(DiscordWebhookResponseDataType); + +action.registerRuntimeFunction(CreateDiscordWebhookUrlFunction); +action.registerRuntimeFunction(CreateDiscordEmbedFooterFunction); +action.registerRuntimeFunction(CreateDiscordEmbedAuthorFunction); +action.registerRuntimeFunction(CreateDiscordEmbedFunction); +action.registerRuntimeFunction(SendDiscordWebhookFunction); + +action.on(CodeZeroEvent.connected, () => { + console.log("Connected to aquila"); +}); + +action.on(CodeZeroEvent.error, (error: Error) => { + console.error("Stream error:", error.message); + console.log("Attempting to reconnect in 5s..."); + setTimeout(() => { + action.connect(process.env.AUTH_TOKEN ?? "your_auth_token_here").catch((err: Error) => { + action.emit(CodeZeroEvent.error, err); + }); + }, 5000); +}); + +action.connect(process.env.AUTH_TOKEN ?? "your_auth_token_here").catch((err: Error) => { + action.emit(CodeZeroEvent.error, err); +}); + +action.on(CodeZeroEvent.moduleUpdated, (message: any) => { + console.dir(message, { depth: null }); + console.dir(action.configs.values(), { depth: null }); +}); + +export { action }; diff --git a/actions/discord-action/test/index.test.ts b/actions/discord-action/test/index.test.ts new file mode 100644 index 0000000..5bc18fa --- /dev/null +++ b/actions/discord-action/test/index.test.ts @@ -0,0 +1,115 @@ +import "reflect-metadata"; +import { describe, expect, it } from "vitest"; +import { createDiscordEmbedBuilder, sendDiscordWebhook } from "../src/helpers.js"; +import { CreateDiscordWebhookUrlFunction } from "../src/functions/createDiscordWebhookUrlFunction.js"; +import { CreateDiscordEmbedFunction } from "../src/functions/createDiscordEmbedFunction.js"; +import { CreateDiscordEmbedAuthorFunction } from "../src/functions/createDiscordEmbedAuthorFunction.js"; +import { CreateDiscordEmbedFooterFunction } from "../src/functions/createDiscordEmbedFooterFunction.js"; +import { SendDiscordWebhookFunction } from "../src/functions/sendDiscordWebhookFunction.js"; + +describe("Discord Action", () => { + it("should format webhook URL correctly", () => { + const fn = new CreateDiscordWebhookUrlFunction(); + const url = fn.run(null, "123456789", "token-abc"); + expect(url).toEqual("https://discord.com/api/webhooks/123456789/token-abc"); + }); + + it("should parse hex color string (#ff22ff) correctly into EmbedBuilder", () => { + const embedFn = new CreateDiscordEmbedFunction(); + const embedData = embedFn.run( + null, + "Hex Test", + "Description", + undefined, + "#ff22ff" + ); + + expect(embedData.color).toEqual("#ff22ff"); + + const builder = createDiscordEmbedBuilder(embedData); + expect(builder).not.toBeNull(); + const json = builder!.toJSON(); + expect(json.color).toEqual(parseInt("ff22ff", 16)); + }); + + it("should parse raw hex string (ff22ff) without hashtag into EmbedBuilder", () => { + const embedFn = new CreateDiscordEmbedFunction(); + const embedData = embedFn.run( + null, + "Hex Test 2", + "Description", + undefined, + "ff22ff" + ); + + const builder = createDiscordEmbedBuilder(embedData); + expect(builder).not.toBeNull(); + const json = builder!.toJSON(); + expect(json.color).toEqual(parseInt("ff22ff", 16)); + }); + + it("should safely handle empty strings in URL and author fields", () => { + const authorFn = new CreateDiscordEmbedAuthorFunction(); + const author = authorFn.run(null, "John", "", ""); + + const embedFn = new CreateDiscordEmbedFunction(); + const embedData = embedFn.run( + null, + "Title", + "Description", + "", + "#70ffb2", + "", + undefined, + "", + "", + author + ); + + const builder = createDiscordEmbedBuilder(embedData); + expect(builder).not.toBeNull(); + const json = builder!.toJSON(); + + expect(json.title).toEqual("Title"); + expect(json.description).toEqual("Description"); + expect(json.author?.name).toEqual("John"); + expect(json.author?.url).toBeUndefined(); + expect(json.url).toBeUndefined(); + }); + + it("should return null for empty embed objects", () => { + const builder = createDiscordEmbedBuilder({}); + expect(builder).toBeNull(); + }); + + it("should use matchedConfig fallback if node parameters are empty", async () => { + const mockContext: any = { + matchedConfig: { + findConfig: (key: string) => { + if (key === "webhook_url") return "https://discord.com/api/webhooks/fallback/test"; + if (key === "username") return "FallbackBot"; + if (key === "avatar_url") return "https://example.com/fallback.png"; + return ""; + }, + }, + }; + + const sendFn = new SendDiscordWebhookFunction(); + const result = await sendFn.run(mockContext, undefined, "Hello!"); + expect(result).toBeDefined(); + }); + + it("should validate missing or invalid webhook URL when no config fallback exists", async () => { + const mockContext: any = { + matchedConfig: { + findConfig: () => "", + }, + }; + + const sendFn = new SendDiscordWebhookFunction(); + const result = await sendFn.run(mockContext, "invalid-url", "Hello!"); + expect(result.success).toBe(false); + expect(result.statusCode).toBe(400); + expect(result.message).toEqual("Invalid or missing webhook URL."); + }); +}); diff --git a/actions/discord-action/tsconfig.json b/actions/discord-action/tsconfig.json new file mode 100644 index 0000000..65d780f --- /dev/null +++ b/actions/discord-action/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Bundler", + "declaration": true, + "outDir": "dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "baseUrl": ".", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "types": ["node", "vite/client"] + }, + "include": [ + "src/**/*", + "test/**/*", + "vite.config.ts" + ] +} diff --git a/actions/discord-action/vite.config.ts b/actions/discord-action/vite.config.ts new file mode 100644 index 0000000..485ef08 --- /dev/null +++ b/actions/discord-action/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +import { resolve } from 'path'; + +export default defineConfig({ + build: { + target: "node18", + ssr: resolve(__dirname, 'src/index.ts'), + rollupOptions: { + external: (id) => + [ + 'fs', + 'path', + 'typescript', + '@code0-tech/hercules', + 'discord.js' + ].includes(id) || id.startsWith('node:') + } + } +});