init
This commit is contained in:
commit
76a7335c7c
9 changed files with 963 additions and 0 deletions
140
.gitignore
vendored
Normal file
140
.gitignore
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Sveltekit cache directory
|
||||
.svelte-kit/
|
||||
|
||||
# vitepress build output
|
||||
**/.vitepress/dist
|
||||
|
||||
# vitepress cache directory
|
||||
**/.vitepress/cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Firebase cache directory
|
||||
.firebase/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Vite logs files
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Eden Discord Bot
|
||||
|
||||
No readme yet, no ci/cd yet. If you make changes and want them to be reviewed, create a PR and ping Maufeat
|
382
package-lock.json
generated
Normal file
382
package-lock.json
generated
Normal file
|
@ -0,0 +1,382 @@
|
|||
{
|
||||
"name": "eden-bot",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "eden-bot",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.21.0",
|
||||
"dotenv": "^17.0.0",
|
||||
"node-fetch": "^3.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders": {
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.11.2.tgz",
|
||||
"integrity": "sha512-F1WTABdd8/R9D1icJzajC4IuLyyS8f3rTOz66JsSI3pKvpCAtsMBweu8cyNYsIyvcrKAVn9EPK+Psoymq+XC0A==",
|
||||
"dependencies": {
|
||||
"@discordjs/formatters": "^0.6.1",
|
||||
"@discordjs/util": "^1.1.1",
|
||||
"@sapphire/shapeshift": "^4.0.0",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/formatters": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.1.tgz",
|
||||
"integrity": "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==",
|
||||
"dependencies": {
|
||||
"discord-api-types": "^0.38.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/rest": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.5.1.tgz",
|
||||
"integrity": "sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw==",
|
||||
"dependencies": {
|
||||
"@discordjs/collection": "^2.1.1",
|
||||
"@discordjs/util": "^1.1.1",
|
||||
"@sapphire/async-queue": "^1.5.3",
|
||||
"@sapphire/snowflake": "^3.5.3",
|
||||
"@vladfrangu/async_event_emitter": "^2.4.6",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"magic-bytes.js": "^1.10.0",
|
||||
"tslib": "^2.6.3",
|
||||
"undici": "6.21.3"
|
||||
},
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/util": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
|
||||
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
|
||||
"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==",
|
||||
"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==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=v16"
|
||||
}
|
||||
},
|
||||
"node_modules/@sapphire/snowflake": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
|
||||
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
|
||||
"engines": {
|
||||
"node": ">=v14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.7.tgz",
|
||||
"integrity": "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.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==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vladfrangu/async_event_emitter": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
|
||||
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
|
||||
"engines": {
|
||||
"node": ">=v14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.38.13",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.13.tgz",
|
||||
"integrity": "sha512-FELWJRgLVQuR7Az8RhdEZE0k6QNjSW9PCUcU1iyP2Gke8HrJmnMceSS9pD93UM64s3tvZzJPajpPLjWZJylf4g=="
|
||||
},
|
||||
"node_modules/discord.js": {
|
||||
"version": "14.21.0",
|
||||
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.21.0.tgz",
|
||||
"integrity": "sha512-U5w41cEmcnSfwKYlLv5RJjB8Joa+QJyRwIJz5i/eg+v2Qvv6EYpCRhN9I2Rlf0900LuqSDg8edakUATrDZQncQ==",
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "^1.11.2",
|
||||
"@discordjs/collection": "1.5.3",
|
||||
"@discordjs/formatters": "^0.6.1",
|
||||
"@discordjs/rest": "^2.5.1",
|
||||
"@discordjs/util": "^1.1.1",
|
||||
"@discordjs/ws": "^1.2.3",
|
||||
"@sapphire/snowflake": "3.5.3",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"magic-bytes.js": "^1.10.0",
|
||||
"tslib": "^2.6.3",
|
||||
"undici": "6.21.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.0.tgz",
|
||||
"integrity": "sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/magic-bytes.js": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.12.1.tgz",
|
||||
"integrity": "sha512-ThQLOhN86ZkJ7qemtVRGYM+gRgR8GEXNli9H/PMvpnZsE44Xfh3wx9kGJaldg314v85m+bFW6WBMaVHJc/c3zA=="
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"deprecated": "Use your platform's native DOMException instead",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
|
||||
"integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
package.json
Normal file
18
package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "eden-bot",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.21.0",
|
||||
"dotenv": "^17.0.0",
|
||||
"node-fetch": "^3.3.2"
|
||||
}
|
||||
}
|
33
src/client.js
Normal file
33
src/client.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import './config.js';
|
||||
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import parseLog from './functions/log_parser.js';
|
||||
import initializeReleaseWatcher from './functions/release_watcher.js';
|
||||
import initializeDescriptionWatcher from './functions/description_watcher.js';
|
||||
|
||||
const { DISCORD_TOKEN } = process.env;
|
||||
|
||||
if (!DISCORD_TOKEN) {
|
||||
console.error('Fatal: DISCORD_TOKEN is not set in the .env file.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
],
|
||||
});
|
||||
|
||||
client.once('ready', () => {
|
||||
console.log(`Logged in as ${client.user.tag}!`);
|
||||
|
||||
initializeReleaseWatcher(client);
|
||||
initializeDescriptionWatcher(client);
|
||||
});
|
||||
|
||||
client.on('messageCreate', parseLog);
|
||||
|
||||
// Log in to Discord
|
||||
client.login(DISCORD_TOKEN);
|
2
src/config.js
Normal file
2
src/config.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
162
src/functions/description_watcher.js
Normal file
162
src/functions/description_watcher.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
import fetch from 'node-fetch';
|
||||
import { ChannelType } from 'discord.js';
|
||||
|
||||
const {
|
||||
CHANNEL_ID,
|
||||
GITHUB_REPO: REPO = 'Eden-CI/PR',
|
||||
GITHUB_TOKEN,
|
||||
EDEN_TOKEN,
|
||||
POLL_INTERVAL_MS,
|
||||
} = process.env;
|
||||
|
||||
const POLL_INTERVAL = parseInt(POLL_INTERVAL_MS) || 10 * 60 * 1000;
|
||||
let isCheckingDescriptions = false;
|
||||
|
||||
|
||||
function disabled() {
|
||||
console.log('[Description Watcher] Service is disabled due to missing environment variables.');
|
||||
console.log('[Description Watcher] CHANNEL_ID = ' + CHANNEL_ID + ' GITHUB_TOKEN = ' + GITHUB_TOKEN + ' EDEN_TOKEN = ' + EDEN_TOKEN);
|
||||
}
|
||||
|
||||
const wait = ms => new Promise(res => setTimeout(res, ms));
|
||||
|
||||
async function buildThreadContent(buildNumber) {
|
||||
let release;
|
||||
let relRes = await fetch(`https://api.github.com/repos/${REPO}/releases/tags/${buildNumber}`, {
|
||||
headers: {
|
||||
'User-Agent': 'description-watcher',
|
||||
Authorization: `token ${GITHUB_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (relRes.status === 404) {
|
||||
// Tag lookup failed – list the latest 100 releases and find one whose tag_name == buildNumber
|
||||
const listRes = await fetch(`https://api.github.com/repos/${REPO}/releases?per_page=100`, {
|
||||
headers: {
|
||||
'User-Agent': 'description-watcher',
|
||||
Authorization: `token ${GITHUB_TOKEN}`,
|
||||
},
|
||||
});
|
||||
if (listRes.ok) {
|
||||
const releases = await listRes.json();
|
||||
release = releases.find(r => r.tag_name === String(buildNumber));
|
||||
}
|
||||
} else if (relRes.ok) {
|
||||
release = await relRes.json();
|
||||
}
|
||||
|
||||
if (!release) throw new Error(`No release with tag "${buildNumber}" found`);
|
||||
|
||||
// 2️⃣ Fetch the self‑hosted Eden PR to get its body / description
|
||||
let desc = 'No description available.';
|
||||
const prRes = await fetch(
|
||||
`https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls/${buildNumber}`,
|
||||
{ headers: { Authorization: `token ${EDEN_TOKEN}` } }
|
||||
);
|
||||
if (prRes.ok) {
|
||||
const pr = await prRes.json();
|
||||
desc = pr.body || pr.description || desc;
|
||||
}
|
||||
|
||||
// 3️⃣ Assemble identical markdown to announceRelease()
|
||||
const title = `Build ${buildNumber}`;
|
||||
const prUrl = `https://git.eden-emu.dev/eden-emu/eden/pulls/${buildNumber}`;
|
||||
const dlUrl = release.html_url || '';
|
||||
let content = `**${title} - ${release.name}**
|
||||
|
||||
${desc}
|
||||
|
||||
🔗 [Go to pull request](${prUrl})
|
||||
|
||||
📝 [Go to downloads](${dlUrl})`;
|
||||
if (content.length > 2_000) content = content.slice(0, 1_997) + '...';
|
||||
return content;
|
||||
}
|
||||
|
||||
async function syncThread(channel, buildNumber) {
|
||||
let desiredContent;
|
||||
try {
|
||||
desiredContent = await buildThreadContent(buildNumber);
|
||||
} catch (err) {
|
||||
console.warn(`[Description‑Watcher] Skipping Build ${buildNumber}: ${err.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const threadName = `Build ${buildNumber}`;
|
||||
|
||||
const { threads: active } = await channel.threads.fetchActive();
|
||||
const { threads: archived } = await channel.threads.fetchArchived();
|
||||
let thread = active.find(t => t.name === threadName) ?? archived.find(t => t.name === threadName);
|
||||
if (!thread) {
|
||||
console.warn(`[Description‑Watcher] No thread named "${threadName}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Un‑archive if needed so we can edit
|
||||
const wasArchived = thread.archived;
|
||||
if (wasArchived) await thread.setArchived(false, 'syncing release description');
|
||||
|
||||
const starter = await thread.fetchStarterMessage();
|
||||
if (!starter) {
|
||||
console.warn(`[Description‑Watcher] Thread "${threadName}" missing starter message`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (starter.content.trim() === desiredContent.trim()) {
|
||||
console.log(`[Description‑Watcher] "${threadName}" already up‑to‑date`);
|
||||
} else {
|
||||
await starter.edit(desiredContent);
|
||||
console.log(`[Description‑Watcher] Updated description for "${threadName}"`);
|
||||
}
|
||||
|
||||
if (wasArchived) await thread.setArchived(true, 'restoring archived state');
|
||||
}
|
||||
|
||||
async function checkDescriptions(client) {
|
||||
if (isCheckingDescriptions) {
|
||||
console.log('[Description‑Watcher] Skipping run – previous check still active');
|
||||
return;
|
||||
}
|
||||
isCheckingDescriptions = true;
|
||||
|
||||
try {
|
||||
const channel = await client.channels.fetch(CHANNEL_ID);
|
||||
if (!channel || channel.type !== ChannelType.GuildForum) {
|
||||
console.error(`[Description‑Watcher] Channel ${CHANNEL_ID} is not a forum`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Combine active + archived threads once; we only need their names
|
||||
const threadNamePrefix = 'Build ';
|
||||
const activeThreads = [...(await channel.threads.fetchActive()).threads.values()];
|
||||
const archivedThreads = [...(await channel.threads.fetchArchived()).threads.values()];
|
||||
const allThreads = [...activeThreads, ...archivedThreads];
|
||||
|
||||
for (const th of allThreads) {
|
||||
if (!th.name.startsWith(threadNamePrefix)) continue;
|
||||
const num = Number(th.name.slice(threadNamePrefix.length).trim());
|
||||
if (Number.isNaN(num)) {
|
||||
console.warn(`[Description‑Watcher] Skipping invalid thread name: "${th.name}"`);
|
||||
continue;
|
||||
}
|
||||
await syncThread(channel, num);
|
||||
await wait(750); // tweak for rate‑limit comfort
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Description‑Watcher] Error during check:', err);
|
||||
} finally {
|
||||
isCheckingDescriptions = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initializeDescriptionWatcher(client) {
|
||||
console.log(`[Description Watcher] Service initialized. Watching ${REPO} every ${POLL_INTERVAL / 1000} seconds.`);
|
||||
checkDescriptions(client);
|
||||
setInterval(() => checkDescriptions(client), POLL_INTERVAL);
|
||||
}
|
||||
|
||||
const isEnabled = CHANNEL_ID && GITHUB_TOKEN && EDEN_TOKEN;
|
||||
const moduleToExport = isEnabled ? initializeDescriptionWatcher : disabled;
|
||||
|
||||
export default moduleToExport;
|
107
src/functions/log_parser.js
Normal file
107
src/functions/log_parser.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
import fetch from 'node-fetch';
|
||||
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
||||
|
||||
const IGNORE_ISSUE_PATTERNS = [
|
||||
/has a different file type \(unknown\) than its extension/i,
|
||||
/Filesystem object at path=.* does not exist/i,
|
||||
];
|
||||
|
||||
|
||||
export default async function parseLog(message) {
|
||||
if (message.author.bot || message.attachments.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const attachment = message.attachments.find(att =>
|
||||
att.name.startsWith('eden_log') && att.name.endsWith('.txt')
|
||||
);
|
||||
|
||||
if (!attachment) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Parsing log file: ${attachment.name} from ${message.author.tag}`);
|
||||
|
||||
try {
|
||||
const response = await fetch(attachment.url);
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch attachment: ${response.statusText}`);
|
||||
return;
|
||||
}
|
||||
const text = await response.text();
|
||||
const lines = text.split(/\r?\n/);
|
||||
|
||||
const specs = {};
|
||||
let gameName, gameVersion;
|
||||
|
||||
for (const line of lines) {
|
||||
if (/Host CPU:/.test(line)) specs.cpu = line.split('Host CPU:')[1].trim();
|
||||
if (/Host CPU Cores:/.test(line)) specs.cores = line.split('Host CPU Cores:')[1].trim();
|
||||
if (/Host CPU Threads:/.test(line)) specs.threads = line.split('Host CPU Threads:')[1].trim();
|
||||
if (/Host OS:/.test(line)) specs.os = line.split('Host OS:')[1].trim();
|
||||
if (/Host RAM:/.test(line)) specs.ram = line.split('Host RAM:')[1].trim();
|
||||
if (/Host Swap:/.test(line)) specs.swap = line.split('Host Swap:')[1].trim();
|
||||
if (/Render\.Vulkan.*Device:/.test(line)) specs.gpu = line.split('Device:')[1].trim();
|
||||
|
||||
// [ 0.479785] Frontend <Info> yuzu\main.cpp:SetFirmwareVersion:5135: Installed firmware: NintendoSDK Firmware for NX 19.0.1-1.0
|
||||
const fwMatch = line.match(/Installed firmware:[^\d]*(\d+\.\d+\.\d+(?:-[\d.]+)?)/);
|
||||
if (fwMatch) specs.firmware = fwMatch[1];
|
||||
|
||||
const gameMatch = line.match(/Booting game:.*?\|\s*(.*?)\s+\(.*\)\s+\|\s*(.*)/);
|
||||
if (gameMatch) {
|
||||
gameName = gameMatch[1].trim();
|
||||
gameVersion = gameMatch[2].trim();
|
||||
}
|
||||
}
|
||||
|
||||
const embedTitle = gameName ? `[${specs.firmware ?? "Unknown FW"}] ${gameName} (v${gameVersion})` : 'Log Parser Summary';
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(embedTitle)
|
||||
.setColor(0xbe41f5)
|
||||
.setDescription(`Summary for **${attachment.name}**.`)
|
||||
.addFields(
|
||||
{ name: 'User', value: message.author.tag, inline: false },
|
||||
{ name: 'OS', value: specs.os || 'N/A', inline: false },
|
||||
{ name: 'CPU', value: specs.cpu || 'N/A', inline: true },
|
||||
{ name: 'GPU', value: specs.gpu || 'N/A', inline: true },
|
||||
{ name: 'Cores', value: specs.cores || 'N/A', inline: true },
|
||||
{ name: 'Threads', value: specs.threads || 'N/A', inline: true },
|
||||
{ name: 'RAM', value: specs.ram || 'N/A', inline: true },
|
||||
{ name: 'Swap', value: specs.swap || 'N/A', inline: true },
|
||||
);
|
||||
|
||||
const issues = lines.filter((l) => {
|
||||
if (!/<Error>|<Critical>/.test(l)) return false;
|
||||
return !IGNORE_ISSUE_PATTERNS.some((pat) => pat.test(l));
|
||||
});
|
||||
|
||||
if (issues.length) {
|
||||
const issuesToDisplay = issues
|
||||
.slice(0, 3)
|
||||
.map((l) => l.replace(/\[\s*[\d.]+\]\s*/, '').trim());
|
||||
|
||||
embed.addFields({
|
||||
name: `${issues.length} Issues Found`,
|
||||
value: `\n\n\`\`\`\n${issuesToDisplay.join('\n')}\n\`\`\``,
|
||||
});
|
||||
if (issues.length > 3) {
|
||||
const remaining = issues.length - 3;
|
||||
embed.setFooter({ text: `... and ${remaining} more issue(s) in the log.` });
|
||||
}
|
||||
} else {
|
||||
embed.addFields({ name: 'Issues', value: '✅ No critical or error issues found.' });
|
||||
}
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel('Download Log file')
|
||||
.setStyle(ButtonStyle.Link)
|
||||
.setURL(attachment.url)
|
||||
);
|
||||
|
||||
await message.reply({ embeds: [embed], components: [row] });
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error in parseLog:', err);
|
||||
}
|
||||
}
|
116
src/functions/release_watcher.js
Normal file
116
src/functions/release_watcher.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
import fetch from 'node-fetch';
|
||||
import { ChannelType, MessageFlags } from 'discord.js';
|
||||
|
||||
const {
|
||||
CHANNEL_ID,
|
||||
GITHUB_REPO: REPO = 'Eden-CI/PR',
|
||||
GITHUB_TOKEN,
|
||||
EDEN_TOKEN,
|
||||
POLL_INTERVAL_MS,
|
||||
} = process.env;
|
||||
|
||||
const POLL_INTERVAL = parseInt(POLL_INTERVAL_MS) || 10 * 60 * 1000;
|
||||
let isCheckingReleases = false;
|
||||
|
||||
|
||||
function disabled() {
|
||||
console.log('[Release Watcher] Service is disabled due to missing environment variables.');
|
||||
console.log('[Release Watcher] CHANNEL_ID = '+CHANNEL_ID+' GITHUB_TOKEN = '+GITHUB_TOKEN+' EDEN_TOKEN = '+EDEN_TOKEN);
|
||||
}
|
||||
|
||||
async function announceRelease(release, channel, baseTag) {
|
||||
try {
|
||||
const prRes = await fetch(
|
||||
`https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls/${baseTag}`,
|
||||
{ headers: { 'Authorization': `token ${EDEN_TOKEN}` } }
|
||||
);
|
||||
let desc = 'No description available.';
|
||||
if (prRes.ok) {
|
||||
const data = await prRes.json();
|
||||
desc = data.body || data.description || desc;
|
||||
}
|
||||
const title = `Build ${baseTag}`;
|
||||
const prUrl = `https://git.eden-emu.dev/eden-emu/eden/pulls/${baseTag}`;
|
||||
let content = `**${title} - ${release.name}**\n\n${desc}\n\n🔗 [Go to pull request](${prUrl})\n\n📝 [Go to downloads](${release.html_url})`;
|
||||
if (content.length > 2000) content = content.slice(0, 1997) + '...';
|
||||
|
||||
await channel.threads.create({
|
||||
name: title,
|
||||
autoArchiveDuration: 10080,
|
||||
message: { content, flags: MessageFlags.SuppressEmbeds }
|
||||
});
|
||||
console.log(`[Release Watcher] Created thread for ${title}`);
|
||||
} catch (e) {
|
||||
console.error(`[Release Watcher] Error announcing release ${release.tag_name}:`, e);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkReleases(client) {
|
||||
if (isCheckingReleases) {
|
||||
console.log('[Release Watcher] Skipping check: a check is already in progress.');
|
||||
return;
|
||||
}
|
||||
isCheckingReleases = true;
|
||||
try {
|
||||
const res = await fetch(`https://api.github.com/repos/${REPO}/releases`, {
|
||||
headers: { 'User-Agent': 'release-watcher', 'Authorization': `token ${GITHUB_TOKEN}` }
|
||||
});
|
||||
if (res.status === 403) {
|
||||
console.error('[Release Watcher] GitHub API error: Rate limit or access denied.');
|
||||
return;
|
||||
}
|
||||
if (!res.ok) throw new Error(`GitHub API responded with status ${res.status}`);
|
||||
|
||||
const allReleases = await res.json();
|
||||
if (!Array.isArray(allReleases)) return;
|
||||
|
||||
const latestReleasesByTag = new Map();
|
||||
for (const release of allReleases) {
|
||||
const baseTag = release.tag_name.split('-')[0];
|
||||
if (!latestReleasesByTag.has(baseTag)) {
|
||||
latestReleasesByTag.set(baseTag, release);
|
||||
}
|
||||
}
|
||||
|
||||
const allUniqueReleases = Array.from(latestReleasesByTag.values());
|
||||
allUniqueReleases.sort((a, b) => parseInt(b.tag_name) - parseInt(a.tag_name));
|
||||
const toProcess = allUniqueReleases.slice(0, 10).reverse();
|
||||
|
||||
const channel = await client.channels.fetch(CHANNEL_ID);
|
||||
if (channel.type !== ChannelType.GuildForum) {
|
||||
console.error(`[Release Watcher] Channel ${CHANNEL_ID} is not a Forum Channel.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const activeThreads = await channel.threads.fetchActive();
|
||||
const archivedThreads = await channel.threads.fetchArchived();
|
||||
const existingThreadNames = new Set([
|
||||
...activeThreads.threads.map(t => t.name),
|
||||
...archivedThreads.threads.map(t => t.name)
|
||||
]);
|
||||
|
||||
for (const release of toProcess) {
|
||||
const baseTag = release.tag_name.split('-')[0];
|
||||
const title = `Build ${baseTag}`;
|
||||
if (!existingThreadNames.has(title)) {
|
||||
await announceRelease(release, channel, baseTag);
|
||||
existingThreadNames.add(title);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[Release Watcher] Error in checkReleases:', e);
|
||||
} finally {
|
||||
isCheckingReleases = false;
|
||||
}
|
||||
}
|
||||
|
||||
function initializeReleaseWatcher(client) {
|
||||
console.log(`[Release Watcher] Service initialized. Watching ${REPO} every ${POLL_INTERVAL / 1000} seconds.`);
|
||||
checkReleases(client);
|
||||
setInterval(() => checkReleases(client), POLL_INTERVAL);
|
||||
}
|
||||
|
||||
const isEnabled = true;
|
||||
const moduleToExport = isEnabled ? initializeReleaseWatcher : disabled;
|
||||
|
||||
export default moduleToExport;
|
Loading…
Add table
Add a link
Reference in a new issue