From b194bfbf5a493ff9a6740b0a4356b58a84da5007 Mon Sep 17 00:00:00 2001 From: Marzin Date: Mon, 10 Jun 2024 20:08:48 +0200 Subject: [PATCH 01/54] first commit --- README.md | 13 +- package-lock.json | 903 ++++++++++++++++++++++++++++++++++ package.json | 24 + src/commands/asset.js | 128 +++++ src/commands/date_new_king.js | 15 + src/commands/dungeon.js | 80 +++ src/commands/echo.js | 50 ++ src/commands/help.js | 33 ++ src/commands/info.js | 31 ++ src/commands/item.js | 179 +++++++ src/commands/leave.js | 15 + src/commands/ping.js | 16 + src/commands/ranking.js | 467 ++++++++++++++++++ src/commands/server.js | 126 +++++ src/commands/setting.js | 319 ++++++++++++ src/commands/user.js | 181 +++++++ src/dungeon_list.json | 165 +++++++ src/images/mr_strong_head.png | Bin 0 -> 624 bytes src/images/unknown_item.png | Bin 0 -> 468 bytes src/index.js | 187 +++++++ src/sentences.json | 434 ++++++++++++++++ src/ultralist.json | 395 +++++++++++++++ src/utils.js | 235 +++++++++ 23 files changed, 3994 insertions(+), 2 deletions(-) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/commands/asset.js create mode 100644 src/commands/date_new_king.js create mode 100644 src/commands/dungeon.js create mode 100644 src/commands/echo.js create mode 100644 src/commands/help.js create mode 100644 src/commands/info.js create mode 100644 src/commands/item.js create mode 100644 src/commands/leave.js create mode 100644 src/commands/ping.js create mode 100644 src/commands/ranking.js create mode 100644 src/commands/server.js create mode 100644 src/commands/setting.js create mode 100644 src/commands/user.js create mode 100644 src/dungeon_list.json create mode 100644 src/images/mr_strong_head.png create mode 100644 src/images/unknown_item.png create mode 100644 src/index.js create mode 100644 src/sentences.json create mode 100644 src/ultralist.json create mode 100644 src/utils.js diff --git a/README.md b/README.md index 95e5a1c..73a03e6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ -# gobattlebot -GoBattle.io Discord Bot +# GoBattle.io Discord Bot + +This Discord bot is a community project initiated by __Kuwazy#703__ and provides Discord users with utility commands related to the MMORPG [GoBattle.io](https://gobattle.io/). +The port is open to all potential contributors and is now managed by Shinobit. + +----------------- + +Make sure you have set the environment variables correctly in the .env file. +To start the project, open the terminal in the project root and use the following command: +`node --env-file=.env ./src/index.js` + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0fb7251 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,903 @@ +{ + "name": "gobattlebot", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gobattlebot", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "canvas": "^2.11.2", + "discord.js": "^14.14.1" + } + }, + "node_modules/@discordjs/builders": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.8.2.tgz", + "integrity": "sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g==", + "dependencies": { + "@discordjs/formatters": "^0.4.0", + "@discordjs/util": "^1.1.0", + "@sapphire/shapeshift": "^3.9.7", + "discord-api-types": "0.37.83", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.4", + "tslib": "^2.6.2" + }, + "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.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.4.0.tgz", + "integrity": "sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ==", + "dependencies": { + "discord-api-types": "0.37.83" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.3.0.tgz", + "integrity": "sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==", + "dependencies": { + "@discordjs/collection": "^2.1.0", + "@discordjs/util": "^1.1.0", + "@sapphire/async-queue": "^1.5.2", + "@sapphire/snowflake": "^3.5.3", + "@vladfrangu/async_event_emitter": "^2.2.4", + "discord-api-types": "0.37.83", + "magic-bytes.js": "^1.10.0", + "tslib": "^2.6.2", + "undici": "6.13.0" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz", + "integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/util": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.0.tgz", + "integrity": "sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg==", + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/ws": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz", + "integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==", + "dependencies": { + "@discordjs/collection": "^2.1.0", + "@discordjs/rest": "^2.3.0", + "@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.37.83", + "tslib": "^2.6.2", + "ws": "^8.16.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.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz", + "integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz", + "integrity": "sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz", + "integrity": "sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==", + "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": "20.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", + "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz", + "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/discord-api-types": { + "version": "0.37.83", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz", + "integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==" + }, + "node_modules/discord.js": { + "version": "14.15.3", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.15.3.tgz", + "integrity": "sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ==", + "dependencies": { + "@discordjs/builders": "^1.8.2", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.4.0", + "@discordjs/rest": "^2.3.0", + "@discordjs/util": "^1.1.0", + "@discordjs/ws": "^1.1.1", + "@sapphire/snowflake": "3.5.3", + "discord-api-types": "0.37.83", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "tslib": "2.6.2", + "undici": "6.13.0" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "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/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "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.10.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nan": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "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.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/undici": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz", + "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "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/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..428ae68 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "gobattlebot", + "version": "1.0.0", + "description": "I am a Bot capable of giving you useful information about the MMORPG GoBattle.io.", + "main": "./src/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Shinobit/gobattlebot.git" + }, + "keywords": [], + "author": "Shinobit LLC, Kuwazy (Samuel Marzin), and contributors", + "license": "MIT", + "bugs": { + "url": "https://github.com/Shinobit/gobattlebot/issues" + }, + "homepage": "https://github.com/Shinobit/gobattlebot#readme", + "dependencies": { + "canvas": "^2.11.2", + "discord.js": "^14.14.1" + } +} diff --git a/src/commands/asset.js b/src/commands/asset.js new file mode 100644 index 0000000..a8667a3 --- /dev/null +++ b/src/commands/asset.js @@ -0,0 +1,128 @@ +const {SlashCommandBuilder, AttachmentBuilder} = require("discord.js"); +const zlib = require("zlib"); + +const asset_command = new SlashCommandBuilder(); +asset_command.setName("asset"); +asset_command.setDescription("Command relating to game data files (client side)."); +asset_command.addSubcommand((subcommand) => { + subcommand.setName("info"); + subcommand.setDescription("Get a specific client file."); + subcommand.addNumberOption((option) => { + option.setName("file_id"); + option.setDescription("The identifier of the item."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +asset_command.addSubcommand((subcommand) => { + subcommand.setName("list"); + subcommand.setDescription("Get the list of files with their respective ID."); + + return subcommand; +}); + +async function get_asset(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + if (subcommand == "list"){ + await get_list(interaction, client); + }else if (subcommand == "info"){ + await get_info(interaction, client); + }else{ + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_list(interaction, client){ + await interaction.deferReply(); + + try{ + const platform = "iOS"; + const version = 115; + + const response = await fetch(`https://gobattle.io/api.php/bootstrap/${version}?platform=${platform}&ud=`); + + if (!response.ok){ + await interaction.editReply(`Unable to recover information from game files.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + let content = ""; + for (let i = 0; i < data.files.length; i++){ + const file = data.files[i]; + const is_compressed = file.file.endsWith("$"); + const file_name = is_compressed ? file.file.slice(0, -1) : file.file; + + content += `#${i + 1} ${file_name}\n`; + } + + const attachment = new AttachmentBuilder(Buffer.from(content, "utf-8")); + attachment.name = "asset_list.txt"; + + await interaction.editReply({ + content: "# Here is the list of assets with their respective identifiers:", + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Unable to retrieve file list. \nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_info(interaction, client){ + await interaction.deferReply(); + + try{ + const platform = "iOS"; + const version = 115; + + const response = await fetch(`https://gobattle.io/api.php/bootstrap/${version}?platform=${platform}&ud=`); + + if (!response.ok){ + await interaction.editReply(`Unable to recover information from game files.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const file_id = interaction.options.get("file_id")?.value; + const nb_files = data.files.length; + if (file_id < 1 || file_id > nb_files){ + await interaction.editReply(`Invalid file ID, try another identifier between 1 and ${nb_files} inclusive.`); + return; + } + + const file_data = data.files[file_id - 1]; + + const url = new URL(`${data.filesBaseURL}/${file_data.file}`); + const response_secondary = await fetch(url); + + if (!response_secondary.ok){ + await interaction.editReply(`Unable to recover information from game files.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const array_buffer = await response_secondary.arrayBuffer(); + const file_buffer = Buffer.from(array_buffer); + const is_compressed = file_data.file.endsWith("$"); + + const attachment = new AttachmentBuilder(is_compressed ? zlib.inflateSync(file_buffer) : file_buffer); + attachment.name = is_compressed ? file_data.file.slice(0, -1) : file_data.file; + + await interaction.editReply({ + content: `# GoBattle.io Asset __\"${attachment.name}\"__.\nID: ${file_id}\nSource: [click here](${url.href})\nCompressed source: ${is_compressed ? "zlib": "_No_"}\nĀ© SHINOBIT LLC.`, + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Unable to recover file.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +exports.get_asset = get_asset; +exports.asset_command = asset_command; diff --git a/src/commands/date_new_king.js b/src/commands/date_new_king.js new file mode 100644 index 0000000..3227991 --- /dev/null +++ b/src/commands/date_new_king.js @@ -0,0 +1,15 @@ +const {SlashCommandBuilder} = require("discord.js"); +const {get_utf_time_next_king} = require("../utils.js"); + +const date_new_king_command = new SlashCommandBuilder(); +date_new_king_command.setName("get_date_new_king"); +date_new_king_command.setDescription("Get the date and time remaining before the next king arrives."); + +async function get_date_new_king(interaction, client){ + const utc = Math.floor(get_utf_time_next_king() / 1000); + + interaction.reply(`New king on ().`); +} + +exports.get_date_new_king = get_date_new_king; +exports.date_new_king_command = date_new_king_command; diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js new file mode 100644 index 0000000..c8648e7 --- /dev/null +++ b/src/commands/dungeon.js @@ -0,0 +1,80 @@ +const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); +const {table} = require("../utils.js"); +const {dungeon_list} = require("../dungeon_list.json"); + +const dungeon_command = new SlashCommandBuilder(); +dungeon_command.setName("dungeon"); +dungeon_command.setDescription("Command relating to dungeons and other maps in the game."); +dungeon_command.addSubcommand((subcommand) => { + subcommand.setName("info"); + subcommand.setDescription("Obtain the characteristics of a dungeon."); + subcommand.addNumberOption((option) => { + option.setName("dungeon_id"); + option.setDescription("The dungeon identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +dungeon_command.addSubcommand((subcommand) => { + subcommand.setName("list"); + subcommand.setDescription("Get the list of all dungeons in the game with their respective ID."); + + return subcommand; +}); + +async function get_dungeon(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + + switch (subcommand){ + case "info": + await get_info(interaction, client); + break; + case "list": + await get_list(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_info(interaction, client){ + await interaction.reply("This subcommand is in development and cannot be used at this time."); +} + +async function get_list(interaction, client){ + await interaction.deferReply(); + + const embed = new EmbedBuilder(); + embed.setTitle("šŸ° Dungeon List šŸ°"); + const description = "Note that this list is not updated in real time like other lists. This is a pre-list awaiting the GoBattle.io API update."; + + embed.addFields( + {name: "> šŸ”¢ __Number of dungeons__", value: `> ${dungeon_list.length}`, inline: true}, + ); + + embed.setDescription(description, {split: false}); + + const table_data = [["ID", "NAME", "LVL MIN"]]; + + for (const dungeon_data of dungeon_list){ + table_data.push(["#" + dungeon_data.id, dungeon_data.name || "Unknow?", dungeon_data.min_level || "Unknow?"]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); +} + +exports.get_dungeon = get_dungeon; +exports.dungeon_command = dungeon_command; diff --git a/src/commands/echo.js b/src/commands/echo.js new file mode 100644 index 0000000..df37b87 --- /dev/null +++ b/src/commands/echo.js @@ -0,0 +1,50 @@ +const {SlashCommandBuilder, PermissionFlagsBits} = require("discord.js"); +const {send_echo, get_first_chat_channel} = require("../utils.js"); + +const echo_command = new SlashCommandBuilder(); +echo_command.setName("echo"); +echo_command.setDescription("Ask me to send a message in the current guild or in all the guils where I am present."); +echo_command.setDefaultMemberPermissions(PermissionFlagsBits.Administrator); +echo_command.addStringOption((option) => { + option.setName("message"); + option.setDescription("The message to send. Any abuse will be punished."); + option.setRequired(true); + option.setMinLength(1); + option.setMaxLength(1900); + + return option; +}); +echo_command.addBooleanOption((option) => { + option.setName("all_guilds"); + option.setDescription("The message will be sent to all guilds I have joined."); + option.setRequired(false); + + return option; +}); + +async function get_echo(interaction, client){ + await interaction.deferReply({ephemeral: true}); + + const message = interaction.options.get("message")?.value; + const all_guilds = interaction.options.get("all_guilds")?.value; + + try{ + if (all_guilds){ + await send_echo(client, message); + }else{ + try{ + await interaction.channel.send(message); + }catch{ + const channel = await get_first_chat_channel(interaction.guild, client); + await channel.send(message); + } + } + + await interaction.editReply("The message has been sent."); + }catch(error){ + await interaction.editReply("The message could not be sent."); + } +} + +exports.get_echo = get_echo; +exports.echo_command = echo_command; diff --git a/src/commands/help.js b/src/commands/help.js new file mode 100644 index 0000000..564df49 --- /dev/null +++ b/src/commands/help.js @@ -0,0 +1,33 @@ +const {SlashCommandBuilder} = require("discord.js"); + +const help_command = new SlashCommandBuilder(); +help_command.setName("help"); +help_command.setDescription("Get help on how to use me."); + +async function get_help(interaction, client){ + await interaction.deferReply({ephemeral: true}); + + try{ + const commands = await client.application.commands.fetch(); + + let description = ""; + + for (const command of commands.values()){ + description += `\n- `; + + for (const option of command.options){ + description += ` _<${option.name}${option.required ? "*" : "?"}>_`; + } + + description += `: **${command.description}**`; + } + + interaction.editReply(`Hi ${interaction.user}, Here are some commands that might be useful to you: ${description}`); + }catch (error){ + await interaction.editReply(`Unable to list commands.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +exports.get_help = get_help; +exports.help_command = help_command; diff --git a/src/commands/info.js b/src/commands/info.js new file mode 100644 index 0000000..a4220cb --- /dev/null +++ b/src/commands/info.js @@ -0,0 +1,31 @@ +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); + +const info_command = new SlashCommandBuilder(); +info_command.setName("info"); +info_command.setDescription("Get metadata about the GoBattle.io Bot."); + +async function get_info(interaction, client){ + await interaction.deferReply(); + + try{ + const embed = new EmbedBuilder(); + embed.setTitle(client.application.name); + embed.setURL(client.application.customInstallURL || `https://discord.com/api/oauth2/authorize?client_id=${client.application.id}&permissions=18685255743552&scope=bot+applications.commands`); + embed.setDescription(client.application.description); + embed.setThumbnail(client.user.avatarURL()); + embed.addFields( + {name: "Gobattle.io server", value: "[Link](https://discord.gg/gobattle-io-official-380588354934276097)", inline: false}, + {name: "BOT Developer server", value: "[Link](https://discord.gg/jhGdY5ArBU)", inline: false}, + {name: "BOT Owner", value: `${client.application.owner}`, inline: false} + ); + + await interaction.editReply({ + embeds: [embed] + }); + }catch(error){ + console.error(error); + } +} + +exports.get_info = get_info; +exports.info_command = info_command; diff --git a/src/commands/item.js b/src/commands/item.js new file mode 100644 index 0000000..eb4f491 --- /dev/null +++ b/src/commands/item.js @@ -0,0 +1,179 @@ +const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); +const {ultra_list} = require("../ultralist.json"); +const {restrict_text, table, sum, format_score} = require("../utils.js"); + +const item_command = new SlashCommandBuilder(); +item_command.setName("item"); +item_command.setDescription("Command relating to items and other consumables in the game."); +item_command.addSubcommand((subcommand) => { + subcommand.setName("info"); + subcommand.setDescription("Obtain the characteristics of an item."); + subcommand.addNumberOption((option) => { + option.setName("item_id"); + option.setDescription("The identifier of the item."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +item_command.addSubcommand((subcommand) => { + subcommand.setName("list"); + subcommand.setDescription("Get the list of all items in the game with their respective ID."); + + return subcommand; +}); +item_command.addSubcommand((subcommand) => { + subcommand.setName("ultrarare_drops"); + subcommand.setDescription("Get statistics on the overall frequency of Ultra Rares spawning in chests."); + + return subcommand; +}); + +async function get_item(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + + switch (subcommand){ + case "info": + await get_info(interaction, client); + break; + case "ultrarare_drops": + await get_ultrarare_drops(interaction, client); + break; + case "list": + await get_list(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_info(interaction, client){ + await interaction.deferReply(); + + try{ + const item_id = interaction.options.get("item_id")?.value; + + let item_info; + for (const ultra of ultra_list){ + if (item_id == ultra.id){ + item_info = ultra; + break; + } + } + + if (!item_info){ + await interaction.editReply("Sorry, the specified item does not exist or has not yet been indexed by the Gobattle API."); + return; + } + + const attachment = new AttachmentBuilder("./images/unknown_item.png"); + attachment.name = "item_icon.png"; + + const embed = new EmbedBuilder() + embed.setTitle(restrict_text(item_info.name, 60)); + embed.setThumbnail(`attachment://${attachment.name}`); + embed.setDescription(item_info.description ? restrict_text(item_info.description, 250) : "_Description Unknown_"); + embed.setColor(0x500000); + + embed.addFields( + {name: "> šŸ·ļø __ID__", value: `> ${item_id}`, inline: true}, + {name: "> 🌟 __Rarity__", value: `> ***${"Ultrarare"}***`, inline: true}, + {name: "> šŸ› ļø __Max uses__", value: `> ${item_info.uses || "_Unknown_"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸ›ļø __Max in inventory__", value: `> ${item_info.max_in_inventory || "_Unknown_"}`, inline: true}, + {name: "> šŸ“„ __Drops in__", value: `> ${item_info.drops ? restrict_text(item_info.drops, 50) : "_Unknown_"}`, inline: true}, + {name: "> šŸ¤ __Tradable__", value: `> ${"_Unknown_"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸŗ __Is relic__", value: `> ${item_info.is_relic ? (item_info.is_relic ? "Yes": "No") : "_Unknown_"}`, inline: true}, + {name: "> šŸ’° __Price__", value: `> ${"_Unknown_"}`, inline: true} + ); + + embed.setTimestamp(); + embed.setFooter({text: "Source: Sage of the Ivy"}); + + await interaction.editReply({ + files: [attachment], + embeds: [embed], + content: "This feature is under development, information may not be accurate." + }); + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this item.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_ultrarare_drops(interaction, client){ + await interaction.deferReply(); + + try{ + const response = await fetch("https://gobattle.io/api.php/v1/stats/ultrarare/drops"); + + if (!response.ok){ + await interaction.editReply("Unable to obtain general information on the general frequency of obtaining ultrarares in Gobattle. There is a problem with the Gobattle API."); + return; + } + + const data_json = await response.json(); + + const total = data_json.total; + const drops = data_json.drops; + const values = Object.values(drops); + + const total_ultrarare = sum(values); + + const drops_ratio = total_ultrarare / (total || 1); + + const embed = new EmbedBuilder(); + embed.setTitle("Current average chance of getting an Ultrarare from a chest."); + + const description = `Results are calculated based on ${total} samples (chest opened by players) including ${total_ultrarare} ultrarare drops.`; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> 🌟 __Ultrarare__", value: `> ${(drops_ratio * 100).toPrecision(2)}%`, inline: true}, + {name: "> šŸ“¦ __Other__", value: `> ${((1 - drops_ratio) * 100).toPrecision(2)}%`, inline: true} + ); + + const table_data = [["ID", "NAME", "DROP RATE", "DROP"]]; + const drops_entries = Object.entries(drops); + for (const [key, value] of drops_entries){ + let name_ultra; + for (const ultra_data of ultra_list){ + if (ultra_data.id && ultra_data.id == key){ + name_ultra = ultra_data.name; + break; + } + } + table_data.push(["#" + key, name_ultra || "Unknow?", `${(value / total_ultrarare * 100).toPrecision(2)}%`, format_score(value)]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + content: "Special mention to _Sage of the Ivy_ for providing some metadata on most of the game's ultrarares while waiting for the GoBattle API update!\nThanks to him!", + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Unable to generate template.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_list(interaction, client){ + await interaction.reply("Sorry this command is under development and cannot be used at the moment."); +} + +exports.get_item = get_item; +exports.item_command = item_command; diff --git a/src/commands/leave.js b/src/commands/leave.js new file mode 100644 index 0000000..ad1e7e8 --- /dev/null +++ b/src/commands/leave.js @@ -0,0 +1,15 @@ +const {SlashCommandBuilder, PermissionFlagsBits} = require("discord.js"); + +const leave_command = new SlashCommandBuilder(); +leave_command.setName("leave"); +leave_command.setDescription("Kick me out of the current guild."); +leave_command.setDefaultMemberPermissions(PermissionFlagsBits.Administrator); + +async function get_leave(interaction, client){ + const install_url = client.application.customInstallURL || `https://discord.com/api/oauth2/authorize?client_id=${client.application.id}&permissions=18685255743552&scope=bot+applications.commands`; + await interaction.reply(`Are you asking me to leave? 🄺\nToo bad, maybe I wasn't up to my duties. If you have feedback to give to my developers, especially ${client.application.owner}, I may improve to better meet your needs.\nIf you change your mind, you can reinstall me with [this link](${install_url}).\nMaybe see you next time and thanks for trying me!`); + await interaction.guild.leave(); +} + +exports.get_leave = get_leave; +exports.leave_command = leave_command; diff --git a/src/commands/ping.js b/src/commands/ping.js new file mode 100644 index 0000000..a92096b --- /dev/null +++ b/src/commands/ping.js @@ -0,0 +1,16 @@ +const {SlashCommandBuilder} = require("discord.js"); + +const ping_command = new SlashCommandBuilder(); +ping_command.setName("ping"); +ping_command.setDescription("Check if the bot is operational."); + +async function get_ping(interaction, client){ + try{ + await interaction.reply({content: "Pong!", ephemeral: true}); + }catch(error){ + console.error(error); + } +} + +exports.get_ping = get_ping; +exports.ping_command = ping_command; diff --git a/src/commands/ranking.js b/src/commands/ranking.js new file mode 100644 index 0000000..5ed6705 --- /dev/null +++ b/src/commands/ranking.js @@ -0,0 +1,467 @@ +const {EmbedBuilder, SlashCommandBuilder, AttachmentBuilder} = require("discord.js"); +const {restrict_text, table, get_level, get_level_adventurer, format_score, format_speed_run_time} = require("../utils.js"); + +const ranking_command = new SlashCommandBuilder(); +ranking_command.setName("ranking"); +ranking_command.setDescription("Commands relating to rankings."); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("king"); + subcommand.setDescription("Get King ranking"); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("weekly"); + subcommand.setDescription("Get Weekly ranking"); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("monthly"); + subcommand.setDescription("Get Monthly ranking"); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("overall"); + subcommand.setDescription("Get Overall (EXP) ranking"); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("adventurer"); + subcommand.setDescription("Get Adventurer ranking"); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("relichunter"); + subcommand.setDescription("Get Relic Hunter ranking"); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); +ranking_command.addSubcommand((subcommand) => { + subcommand.setName("speedrun"); + subcommand.setDescription("Get Speedrun ranking"); + subcommand.addNumberOption((option) => { + option.setName("dungeon_id"); + option.setDescription("The dungeon identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); + +async function get_ranking(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + + switch (subcommand){ + case "king": + await get_king(interaction, client); + break; + case "weekly": + await get_weekly(interaction, client); + break; + case "monthly": + await get_monthly(interaction, client); + break; + case "overall": + await get_overall(interaction, client); + break; + case "adventurer": + await get_adventurer(interaction, client); + break; + case "relichunter": + await get_relic_hunter(interaction, client); + break; + case "speedrun": + await get_speedrun(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_king(interaction, client){ + await interaction.deferReply(); + + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch("https://gobattle.io/api.php/valdoranking?platform=Discord&ud="); + + if (!response.ok){ + await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸ‘‘ King Ranking šŸ‘‘"); + + const description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command."; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> šŸ—“ __Week__", value: `> ${data?.week || "_Unknown?_"}`, inline: true}, + {name: "> šŸ—“ __Year__", value: `> ${data?.year || "_Unknown?_"}`, inline: true} + ); + + const table_data = [["IDX", "NICK", "REP", "ID"]]; + const ranking = data?.ranking; + for (let i = 0; i < max_fields && i < ranking.length; i++){ + const field = ranking[i]; + table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.reputation), field?.id]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate King ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_weekly(interaction, client){ + await interaction.deferReply(); + + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch("https://gobattle.io/api.php/stats/weekly?platform=Discord&ud="); + + if (!response.ok){ + await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸ”„ Weekly Ranking šŸ”„"); + + const table_data = [["IDX", "NICK", "COINS", "KILLS", "DEATHS", "EXP", "LVL"]]; + + for (let i = 0; i < max_fields && i < data.length; i++){ + const field = data[i]; + const exp = parseInt(field?.experience, 10); + const level = get_level(exp); + table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.coins), format_score(field?.kills), format_score(field?.deaths), format_score(field?.experience), level.toString()]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate Weekly ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_monthly(interaction, client){ + await interaction.deferReply(); + + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch("https://gobattle.io/api.php/stats/monthly?platform=Discord&ud="); + + if (!response.ok){ + await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸ”„ Monthly Ranking šŸ”„"); + + const table_data = [["IDX", "NICK", "COINS", "KILLS", "DEATHS", "EXP", "LVL"]]; + + for (let i = 0; i < max_fields && i < data.length; i++){ + const field = data[i]; + const exp = parseInt(field?.experience, 10); + const level = get_level(exp); + table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.coins), format_score(field?.kills), format_score(field?.deaths), format_score(field?.experience), level.toString()]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate Monthly ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_overall(interaction, client){ + await interaction.deferReply(); + + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch("https://gobattle.io/api.php/stats/overall?platform=Discord&ud="); + + if (!response.ok){ + await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸ”„ Overall Ranking (EXP) šŸ”„"); + + const table_data = [["IDX", "NICK", "COINS", "KILLS", "DEATHS", "EXP", "LVL"]]; + + for (let i = 0; i < max_fields && i < data.length; i++){ + const field = data[i]; + const exp = parseInt(field?.experience, 10); + const level = get_level(exp); + table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.coins), format_score(field?.kills), format_score(field?.deaths), format_score(field?.experience), level.toString()]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate Overall ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_adventurer(interaction, client){ + await interaction.deferReply(); + + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch("https://gobattle.io/api.php/v1/stats/ranking/adventurer?platform=Discord&ud="); + + if (!response.ok){ + await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("🤠 Adventurer Ranking 🤠"); + + const table_data = [["RANK", "NICK", "LVL", "SCORE", "ID"]]; + + for (let i = 0; i < max_fields && i < data.length; i++){ + const field = data[i]; + table_data.push(["#" + field?.rank, restrict_text(field?.nick, 20), get_level_adventurer(field?.score).toString(), format_score(field?.score), field?.id.toString()]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate Adventurer ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_relic_hunter(interaction, client){ + await interaction.deferReply(); + + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch("https://gobattle.io/api.php/v1/stats/ranking/relichunter?platform=Discord&ud="); + + if (!response.ok){ + await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸŗ Relic Hunter Ranking šŸŗ"); + + const table_data = [["RANK", "NICK", "LVL", "SCORE", "ID"]]; + + for (let i = 0; i < max_fields && i < data.length; i++){ + const field = data[i]; + table_data.push(["#" + field?.rank, restrict_text(field?.nick, 20), get_level_adventurer(field?.score).toString(), format_score(field?.score), field?.id.toString()]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate Hunter Ranking ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_speedrun(interaction, client){ + await interaction.deferReply(); + + const dungeon_id = interaction.options.get("dungeon_id")?.value; + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch(`https://gobattle.io/api.php/v1/stats/speedrun/${dungeon_id}`); + + if (!response.ok){ + await interaction.editReply(`Map _#${dungeon_id}_ not found, try another dungeon ID.\nNormally the dungeon ID is listed in chat when you complete your speedrun.\nIf you don't see anything, you haven't used the \`/speedrun\` command correctly on GoBattle.io.`); + return; + } + + const data = await response.json(); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸ Speedrun Ranking šŸ"); + embed.setDescription("Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!"); + embed.addFields( + {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, + {name: "> šŸ·ļø __Dungeon ID__", value: `> ${dungeon_id}`, inline: true} + ); + + const table_data = [["RANK", "NICK", "TIME", "ID"]]; + const ranking = data.ranking; + const list_size = ranking.length; + for (var i = 0; i < max_fields && i < list_size; i++){ + const field = ranking[i]; + table_data.push([field?.rank.toString(), restrict_text(field?.nick, 20), format_speed_run_time(field?.time), field?.id.toString()]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Failed to generate Speedrun ranking...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +exports.get_ranking = get_ranking; +exports.ranking_command = ranking_command; diff --git a/src/commands/server.js b/src/commands/server.js new file mode 100644 index 0000000..7ec41ae --- /dev/null +++ b/src/commands/server.js @@ -0,0 +1,126 @@ +const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); +const {restrict_text, table} = require("../utils.js"); + +const server_command = new SlashCommandBuilder(); +server_command.setName("server"); +server_command.setDescription("Commands relating to GoBattle.io game servers."); +server_command.addSubcommand((subcommand) => { + subcommand.setName("list"); + subcommand.setDescription("Obtain the list of GoBattle.io servers currently listed for the public."); + subcommand.addStringOption((option) => { + option.setName("platform"); + option.setDescription("Platform."); + option.setRequired(false); + + option.setChoices( + {name: "Web", value: "Web"}, + {name: "iOS", value: "iOS"}, + {name: "Android", value: "Android"} + ); + + return option; + }); + subcommand.addIntegerOption((option) => { + option.setName("version"); + option.setDescription("Server version."); + option.setRequired(false); + option.setMinValue(1); + + return option; + }); + subcommand.addNumberOption((option) => { + option.setName("max_fields"); + option.setDescription("Maximum number of fields."); + option.setMinValue(1); + option.setMaxValue(50); + option.setRequired(false); + + return option; + }); + + return subcommand; +}); + +async function get_server(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + switch (subcommand){ + case "list": + await get_list(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_list(interaction, client){ + await interaction.deferReply(); + + const platform = interaction.options.get("platform")?.value || "Web"; + const version = interaction.options.get("version")?.value || 115; + const max_fields = interaction.options.get("max_fields")?.value || 20; + + try{ + const response = await fetch(`https://gobattle.io/api.php/bootstrap/${version}?platform=${platform}&ud=`); + + if (!response.ok){ + await interaction.editReply(`I couldn't get the server list. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const data_json = await response.json(); + + const server_list = data_json?.serverlist; + + server_list.push({"id": "8", "version": "115", "friendlyName": "Alpha Server (v115)", "url": "us-west.gobattle.io", "port": "8100", "ws": "9100", "wss": "10100", "admin": "Unknown?"}); + //server_list.push({"id": "50", "version": "115", "friendlyName": "Kuwazy Server", "url": "us-west.gobattle.io", "port": "8102", "ws": "9102", "wss": "10102", "admin": "0"}); + //server_list.push({"id": "147", "version": "114", "friendlyName": "Old France Server (v114)", "url": "france.gobattle.io", "port": "8114", "ws": "9114", "wss": "10114", "admin": "0"}); + //server_list.push({"id": "148", "version": "114", "friendlyName": "Old USA West server (v114)", "url": "us-west.gobattle.io", "port": "8114", "ws": "9114", "wss": "10114", "admin": "0"}); + //server_list.push({"id": "149", "version": "114", "friendlyName": "Old Singapore Server (v114)", "url": "france.gobattle.io", "port": "8114", "ws": "9114", "wss": "10114", "admin": "0"}); + + const embed = new EmbedBuilder(); + + embed.setTitle("šŸ–„ļø Server List šŸ–„ļø"); + + const table_data = [["ID", "NAME", "VERSION", "ADMIN", "STATUS"]]; + const list_size = server_list.length; + for (var i = 0; i < max_fields && i < list_size; i++){ + const field = server_list[i]; + + let is_online; + try{ + const response = await fetch(`https://${field.url}:${field.wss || field.ws || field.port}/rtt`); + + is_online = response.ok; + + //const data_ping = await response.text(); + }catch(error){ + is_online = false; + } + + table_data.push([field?.id, restrict_text(field?.friendlyName, 20), field?.version, field?.admin, (is_online ? "Online" : "Down")]); + } + + const attachment = new AttachmentBuilder(table(table_data)); + attachment.name = "table.png"; + + embed.setImage(`attachment://${attachment.name}`); + + embed.addFields( + {name: "> šŸŽ® __Platform__", value: `> ${platform}`, inline: true}, + {name: "> šŸ”ƒ __Requested version__", value: `> ${version.toString()}`, inline: true} + ); + + embed.setTimestamp(); + + await interaction.editReply({ + embeds: [embed], + files: [attachment] + }); + }catch(error){ + await interaction.editReply(`Unable to retrieve server list.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +exports.get_server = get_server; +exports.server_command = server_command; diff --git a/src/commands/setting.js b/src/commands/setting.js new file mode 100644 index 0000000..86241de --- /dev/null +++ b/src/commands/setting.js @@ -0,0 +1,319 @@ +const {SlashCommandBuilder, PermissionFlagsBits, ActivityType} = require("discord.js"); + +const setting_command = new SlashCommandBuilder(); +setting_command.setName("setting"); +setting_command.setDescription("Command relating to my profile settings."); +setting_command.setDefaultMemberPermissions(PermissionFlagsBits.Administrator); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_avatar"); + subcommand.setDescription("Set my avatar image."); + subcommand.addAttachmentOption((option) => { + option.setName("image_file"); + option.setDescription("Avatar image (Recommended file: png, jpeg, gif...)"); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_banner"); + subcommand.setDescription("Set my banner image."); + subcommand.addAttachmentOption((option) => { + option.setName("image_file"); + option.setDescription("Banner image (Recommended file: png, jpeg, gif...)"); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_username"); + subcommand.setDescription("Changing my username in Discord is heavily rate limited, with only 2 requests every hour!"); + subcommand.addStringOption((option) => { + option.setName("username"); + option.setDescription("Username."); + option.setRequired(true); + option.setMinLength(1); + option.setMaxLength(32); + + return option; + }); + + return subcommand; +}); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_biography"); + subcommand.setDescription("Set my biography."); + subcommand.addStringOption((option) => { + option.setName("biography"); + option.setDescription("Biography."); + option.setRequired(true); + option.setMinLength(0); + option.setMaxLength(190); + + return option; + }); + + return subcommand; +}); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_activity"); + subcommand.setDescription("Set my activity."); + subcommand.addStringOption((option) => { + option.setName("name"); + option.setDescription("Name of the activity."); + option.setRequired(true); + option.setMinLength(0); + option.setMaxLength(190); + + return option; + }); + subcommand.addStringOption((option) => { + option.setName("state"); + option.setDescription("State of the activity."); + option.setRequired(false); + option.setMinLength(0); + option.setMaxLength(190); + + return option; + }); + subcommand.addNumberOption((option) => { + option.setName("type"); + option.setDescription("Type of the activity."); + option.setRequired(false); + option.addChoices( + {name: "Competing", value: ActivityType.Competing}, + {name: "Listening", value: ActivityType.Listening}, + {name: "Playing", value: ActivityType.Playing}, + {name: "Streaming", value: ActivityType.Streaming}, + {name: "Watching", value: ActivityType.Watching}, + {name: "Custom", value: ActivityType.Custom} + ); + + return option; + }); + subcommand.addStringOption((option) => { + option.setName("url"); + option.setDescription("Twitch / YouTube stream URL."); + option.setRequired(false); + option.setMinLength(0); + option.setMaxLength(190); + + return option; + }); + + return subcommand; +}); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_afk"); + subcommand.setDescription("Sets/removes my AFK flag."); + subcommand.addBooleanOption((option) => { + option.setName("is_afk"); + option.setDescription("Is AFK?"); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +setting_command.addSubcommand((subcommand) => { + subcommand.setName("set_status"); + subcommand.setDescription("Set my status."); + subcommand.addStringOption((option) => { + option.setName("status"); + option.setDescription("Status."); + option.setRequired(true); + option.addChoices( + {name: "Online", value: "online"}, + {name: "Invisible", value: "invisible"}, + {name: "Idle", value: "idle"}, + {name: "Dnd", value: "dnd"} + ); + + return option; + }); + + return subcommand; +}); + +async function get_setting(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + + switch (subcommand){ + case "set_avatar": + await get_set_avatar(interaction, client); + break; + case "set_banner": + await get_set_banner(interaction, client); + break; + case "set_username": + await get_set_username(interaction, client); + break; + case "set_biography": + await get_set_biography(interaction, client); + break; + case "set_activity": + await get_set_activity(interaction, client); + break; + case "set_afk": + await get_set_afk(interaction, client); + break; + case "set_status": + await get_set_status(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_set_avatar(interaction, client){ + await interaction.deferReply(); + + try{ + const attachment = interaction.options.get("image_file")?.attachment; + + const response = await fetch(attachment.url); + + if (!response.ok){ + await interaction.editReply(`The avatar image was not set successfully.\nUnable to retrieve image from Discord CDN.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + return; + } + + const array_buffer = await response.arrayBuffer(); + const buffer = Buffer.from(array_buffer); + + await client.user.setAvatar(buffer); + await interaction.editReply("The avatar image has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`The avatar image was not set successfully.\nVerify that the file is an image type accepted by Discord.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_set_banner(interaction, client){ + await interaction.deferReply(); + + try{ + const attachment = interaction.options.get("image_file")?.attachment; + + const response = await fetch(attachment.url); + + if (!response.ok){ + await interaction.editReply(`The banner image was not set successfully.\nUnable to retrieve image from Discord CDN.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + return; + } + + const array_buffer = await response.arrayBuffer(); + const buffer = Buffer.from(array_buffer); + + await client.user.setBanner(buffer); // Do not work? + await interaction.editReply("The banner image has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`The banner image was not set successfully.\nVerify that the file is an image type accepted by Discord.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_set_username(interaction, client){ + await interaction.deferReply(); + + try{ + const username = interaction.options.get("username")?.value; + + await client.user.setUsername(username); + await interaction.editReply("Username has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`Username was not set successfully.\nSets the username of the logged in client. Changing usernames in Discord is heavily rate limited, with only 2 requests every hour. Use this sparingly!\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_set_biography(interaction, client){ + await interaction.deferReply(); + + // Do not work? NAHHHHHHHHHHHHHHH Why Discord Why!!! ): + /* + // The code if against is prohibited by discord: + try{ + const biography = interaction.options.get("biography")?.value; + + const api_version = 14; + const url = `https://discord.com/api/${api_version}/users/@me`; + + const response = await fetch(url, { + method: "PATCH", + headers: { + "Authorization": process.env.TOKEN, + "Content-Type": "application/json" + }, + body: JSON.stringify({bio: biography}) + }); + + if (!response.ok){ + await interaction.editReply(`Biography was not set successfully.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(response); + return; + } + + await interaction.editReply("Biography has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`Biography was not set successfully.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } + */ + + await interaction.editReply("The Discord API doesn't let you do this properly. Wait for Discord to update its API."); +} + +async function get_set_activity(interaction, client){ + await interaction.deferReply(); + + try{ + const name_value = interaction.options.get("name")?.value; + const state_value = interaction.options.get("state")?.value; + const type_value = interaction.options.get("type")?.value; + const url_value = interaction.options.get("url")?.value; + + await client.user.setActivity({name: name_value, state: state_value, type: type_value, url: url_value}); + await interaction.editReply("Activity has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`Activity was not set successfully.\nCheck that the settings options are consistent with each other.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_set_afk(interaction, client){ + await interaction.deferReply(); + + try{ + const afk_value = interaction.options.get("is_afk")?.value; + + client.user.setAFK(afk_value); + await interaction.editReply("AFK status has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`AFK status was not set successfully.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_set_status(interaction, client){ + await interaction.deferReply(); + + try{ + const status_value = interaction.options.get("status")?.value; + + client.user.setStatus(status_value); + await interaction.editReply("Status has been successfully set. Changes may take time to appear."); + }catch (error){ + await interaction.editReply(`Status was not set successfully.\nIf the problem persists, contact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +exports.get_setting = get_setting; +exports.setting_command = setting_command; diff --git a/src/commands/user.js b/src/commands/user.js new file mode 100644 index 0000000..5fab0fb --- /dev/null +++ b/src/commands/user.js @@ -0,0 +1,181 @@ +const {EmbedBuilder, AttachmentBuilder, SlashCommandBuilder} = require("discord.js"); +const {restrict_text} = require("../utils.js"); + +const user_command = new SlashCommandBuilder(); +user_command.setName("user"); +user_command.setDescription("Command relating to users in the game."); +user_command.addSubcommand((subcommand) => { + subcommand.setName("info"); + subcommand.setDescription("Get general information about a user."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +user_command.addSubcommand((subcommand) => { + subcommand.setName("king"); + subcommand.setDescription("Get general information about the current king."); + + return subcommand; +}); +user_command.addSubcommand((subcommand) => { + subcommand.setName("bank"); + subcommand.setDescription("Get the list of items contained in a game user's bank."); + + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + subcommand.addNumberOption((option) => { + option.setName("index_book"); + option.setDescription("Index of the bank book to consult."); + option.setMinValue(1); + option.setMaxValue(6); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +user_command.addSubcommand((subcommand) => { + subcommand.setName("inventory"); + subcommand.setDescription("Get the list of items contained in a game user's inventory."); + + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); + +async function get_user(interaction, client){ + const subcommand = interaction.options.getSubcommand(); + + switch (subcommand){ + case "info": + await get_info(interaction, client); + break; + case "king": + await get_king(interaction, client); + break; + case "bank": + await get_bank(interaction, client); + break; + case "inventory": + await get_inventory(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_info(interaction, client){ + await interaction.deferReply(); + + try{ + const user_id = interaction.options.get("user_id")?.value; + const public = false; + + if (interaction.user != client.application.owner && !public){ + await interaction.editReply(`Sorry, user _#${user_id}_ does not wish to expose this information to the public.`); + return; + } + + const embed = new EmbedBuilder(); + embed.setTitle(restrict_text("Kuwazy", 60)); + embed.setThumbnail("attachment://head.png"); + embed.setDescription(restrict_text("_This user has no description..._", 250)); + embed.setColor(0x500000); + + embed.addFields( + {name: "> šŸ·ļø __ID__", value: `> ${user_id.toString()}`, inline: true}, + {name: "> šŸŖ™ __Coins__", value: `> ${"2,283,520"}`, inline: true}, + {name: "> šŸ’Ž __Diamonds__", value: `> ***${(27).toString()}***`, inline: true} + ); + + embed.addFields( + {name: "> šŸ’Ŗ __LVL__", value: `> ${"174"}`, inline: true}, + {name: "> šŸ› ļø __EXP__", value: `> ${"92026/139600"}`, inline: true}, + {name: "> šŸ”± __REP__", value: `> ${"17"}`, inline: true} + ); + + embed.addFields( + {name: "> āš”ļø __ATT__", value: `> ${"+17"}`, inline: true}, + {name: "> šŸ›”ļø __DEF__", value: `> ${"+19"}`, inline: true}, + {name: "> šŸ€ __LCK__", value: `> ${"+9"}`, inline: true} + ); + + embed.addFields( + {name: "> ā¤ļø __MHP__", value: `> ${"+11"}`, inline: true}, + {name: "> ā¤ļøā€šŸ©¹ __RGN__", value: `> ${"+6"}`, inline: true}, + {name: "> ⚔ __SPD__", value: `> ${"+13"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸ’Æ __ADV Score__", value: `> ${"5630"}`, inline: true}, + {name: "> šŸ’Ŗ __ADV LVL__", value: `> ${"5"}`, inline: true}, + {name: "> 🤠 __ADV Rank__", value: `> ${"124587"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸ”” __Status__", value: `> ${"Connected"}`, inline: true}, + {name: "> 🌐 __Server__", value: `> ${"France Server"}`, inline: true}, + {name: "> šŸŽ– __Role__", value: `> ${"Player"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸ”‡ __Is Muted__", value: `> ${"No"}`, inline: true}, + {name: "> 🚫 __Is Banned__", value: `> ${"No"}`, inline: true}, + {name: "> šŸ‘‘ __Is King__", value: `> ${"No"}`, inline: true} + ); + + embed.setFooter({text: "Last connection"}); + embed.setTimestamp(); + + const attachment = new AttachmentBuilder("./images/mr_strong_head.png"); + attachment.name = "head.png"; + + await interaction.editReply({ + files: [attachment], + embeds: [embed], + content: "This feature is under development. This is just an example of a template." + }); + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_king(interaction, client){ + await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); +} + +async function get_bank(interaction, client){ + const user_id = interaction.options.get("user_id")?.value; + await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); +} + +async function get_inventory(interaction, client){ + const user_id = interaction.options.get("user_id")?.value; + await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); +} + +exports.get_user = get_user; +exports.user_command = user_command; diff --git a/src/dungeon_list.json b/src/dungeon_list.json new file mode 100644 index 0000000..3bc6925 --- /dev/null +++ b/src/dungeon_list.json @@ -0,0 +1,165 @@ +{ + "author_id": 1201923723662467082, + "dungeon_list": [ + { + "id": 8, + "name": "Neewe Caves", + "min_level": null + }, + { + "id": 9, + "name": "Daragorn's Lair", + "min_level": null + }, + { + "id": 10, + "name": "Enchanted Forest", + "min_level": null + }, + { + "id": 12, + "name": "Spider Nest", + "min_level": null + }, + { + "id": 13, + "name": "Sawmill", + "min_level": null + }, + { + "id": 14, + "name": "Magma Caves", + "min_level": null + }, + { + "id": 15, + "name": "FireBreath's Lair", + "min_level": null + }, + { + "id": 16, + "name": "Asterion's Labyrinth", + "min_level": null + }, + { + "id": 17, + "name": "Dragonblood", + "min_level": null + }, + { + "id": 18, + "name": "Sir Valkan's Stronghold", + "min_level": null + }, + { + "id": 20, + "name": "Ravanor's Land", + "min_level": null + }, + { + "id": 22, + "name": "Danma Caverns", + "min_level": null + }, + { + "id": 23, + "name": "Viasar Castle", + "min_level": null + }, + { + "id": 24, + "name": "Fairy Forest", + "min_level": null + }, + { + "id": 25, + "name": "Kron Waterfalls", + "min_level": null + }, + { + "id": 27, + "name": "Dark Cave", + "min_level": null + }, + { + "id": 28, + "name": "Underwater magma caves", + "min_level": null + }, + { + "id": 31, + "name": "Stahmite Mines", + "min_level": null + }, + { + "id": 32, + "name": "The path of the warrior", + "min_level": null + }, + { + "id": 33, + "name": "Feeble's Deathtrap", + "min_level": null + }, + { + "id": 35, + "name": "DanCie Spikes", + "min_level": null + }, + { + "id": 36, + "name": "Scale Caves", + "min_level": null + }, + { + "id": 41, + "name": "Sky's Prison", + "min_level": null + }, + { + "id": 45, + "name": "Tower Lair", + "min_level": null + }, + { + "id": 46, + "name": "Bloodmoon Castle", + "min_level": null + }, + { + "id": 48, + "name": "Ruins of Kalambria", + "min_level": null + }, + { + "id": 50, + "name": "AzureFlora Abyss", + "min_level": null + }, + { + "id": 51, + "name": "Toxic Caves", + "min_level": null + }, + { + "id": 52, + "name": "Cloudy Sky", + "min_level": null + }, + { + "id": 53, + "name": "Cave Under Water", + "min_level": null + }, + { + "id": 54, + "name": "Cursed Cave", + "min_level": null + }, + { + "id": 57, + "name": "Frostbite Dungeon", + "min_level": null + } + ] +} \ No newline at end of file diff --git a/src/images/mr_strong_head.png b/src/images/mr_strong_head.png new file mode 100644 index 0000000000000000000000000000000000000000..d72b3f8daef10c35d159c493889c3ea5e22f6607 GIT binary patch literal 624 zcmV-$0+0QPP)Px%DM>^@R9HvVn9WMVKoo@&L9|6HRQxGLbkTyi6G0HcolCcFL|>ur;m%i3+~^~? z6Gae2S1L3MrIh-E1*^1*pyGrRoSV6wNiCV!O-bf9bIx~9Ceyf%=95mPzJ5wZCJVY! zY5eilla8CMmaDpe^5pNr*acw1NOs#0(vb)euqMEC;;2~wNsdH7;NJ+)WrHKsH3D51 zV2XY=Kd7Ahy!xU%4-(WlEp`(xfI9E*?GYv>h6f}wduP?Q-NAJzF)aaW0!$}{?OfK+ z$kxHNM544jEskq-KP%Px$j!8s8R9HvNm%U2FKoo@yK0^X_f`wpZC5Q;NzJQeog0PKXV{0L{HkJ!kVxuqM z1BeJ#wibeQ@(_wMoZy`GaIcY_$u!F*^Kfa*TUtsYw8MiMR=57ho;;_z?pJ0nWddh$N6*fOkQTR1rWwkUHD^-6K7L zVgj&m+eTNTP2YW0tq8D3))v$GnkEvdMk4@q4V{yfRIwtW2ynz0|MqedCcZs6O$ihi zuzz(K6s>L?B=wD4Dk30YMa+(xAuxIY$Q=kNB6sJL5bXNyR``8=cuM|*#GeVwT>wnT z3n9nX_tCxV5j^KeRpgSu=mnq}KMR?G2W%sDrBK<%e;A5baCCIE^pFDL?*`%MoN z6Tlmhl?qe`Jm8Joy$@s;0A*eh=&$wx%l&xYp9hKwV5RD4hJO70w>*G)$9TRk%$$mg z3-A)*Zv?wrMa=V?HzVt`3=_^xfJKDgd+&sO6{vDi&zZ=71$+TORd$T%z*JQL0000< KMNUMnLSTY@Z^c>w literal 0 HcmV?d00001 diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..78525e0 --- /dev/null +++ b/src/index.js @@ -0,0 +1,187 @@ +const {Client, Partials, IntentsBitField, Routes, ActivityType} = require("discord.js"); +const {get_random_int, get_first_chat_channel} = require("./utils.js"); +const sentences = require("./sentences.json"); + +const {get_ranking, ranking_command} = require("./commands/ranking.js"); +const {get_help, help_command} = require("./commands/help.js"); +const {get_asset, asset_command} = require("./commands/asset.js"); +const {get_item, item_command} = require("./commands/item.js"); +const {get_user, user_command} = require("./commands/user.js"); +const {get_server, server_command} = require("./commands/server.js"); +const {get_info, info_command} = require("./commands/info.js"); +const {get_dungeon, dungeon_command} = require("./commands/dungeon.js"); +const {get_date_new_king, date_new_king_command} = require("./commands/date_new_king.js"); +const {get_ping, ping_command} = require("./commands/ping.js"); +const {get_leave, leave_command} = require("./commands/leave.js"); +const {get_setting, setting_command} = require("./commands/setting.js"); +const {get_echo, echo_command} = require("./commands/echo.js"); + +const global_commands = [ + ranking_command, + help_command, + asset_command, + item_command, + user_command, + server_command, + info_command, + dungeon_command, + date_new_king_command, + ping_command, + leave_command +]; + +const private_commands = [ + setting_command, + echo_command +]; + +const client = new Client({ + intents: [ + IntentsBitField.Flags.Guilds, + IntentsBitField.Flags.GuildMembers, + IntentsBitField.Flags.GuildMessages, + IntentsBitField.Flags.MessageContent, + IntentsBitField.Flags.GuildMessageReactions, + IntentsBitField.Flags.GuildEmojisAndStickers + ], + partials: [Partials.Message, Partials.Channel, Partials.Reaction], +}); + +client.login(process.env.TOKEN); + +client.on("ready", async (event) => { + console.log(`${event.user.tag} ready.`); + + client.user.setActivity("GoBattle.io", {type: ActivityType.Playing}); + + await client.application.fetch(); + + try{ + client.rest.put( + Routes.applicationCommands(client.user.id), + {body: global_commands} + ); + }catch (error){ + console.error(error); + } + + const guilds_admin_id = JSON.parse(process.env.GUILD_ADMIN_ID); + for (const guild_id of guilds_admin_id){ + try{ + await client.rest.put( + Routes.applicationGuildCommands(client.user.id, guild_id), + {body: private_commands} + ); + }catch (error){ + console.error(error); + } + } +}); + +client.on("guildCreate", async (guild) => { + try { + const channel = get_first_chat_channel(guild, client); + + if (channel){ + await channel.send("Hello, thank you for inviting me to this guild!\nYou can use the `/help` command to find out what I can do!"); + } + }catch (error){ + console.error(error); + } +}); + +client.on("interactionCreate", async (interaction) => { + if (interaction.isButton()){ + return; + } + + if (!interaction.isChatInputCommand()){ + return; + } + + try{ + switch (interaction.commandName){ + case "item": + await get_item(interaction, client); + break; + case "user": + await get_user(interaction, client); + break; + case "server": + await get_server(interaction, client); + break; + case "ranking": + await get_ranking(interaction, client); + break; + case "get_date_new_king": + await get_date_new_king(interaction, client); + break; + case "info": + await get_info(interaction, client); + break; + case "help": + await get_help(interaction, client); + break; + case "ping": + await get_ping(interaction, client); + break; + case "leave": + await get_leave(interaction, client); + break; + case "asset": + await get_asset(interaction, client); + break; + case "dungeon": + await get_dungeon(interaction, client); + break; + case "setting": + await get_setting(interaction, client); + break; + case "echo": + await get_echo(interaction, client); + break; + default: + await interaction.reply({content: "This command no longer exists.", ephemeral: true}); + } + }catch (error){ + console.error("Impossible to answer:", error); + } +}); + +client.on("messageCreate", async (msg) => { + if (msg.author.bot){ + return; + } + + try{ + // Adding chili pepper to the cat. It amuses us LOL. + if (msg.mentions.has(client.user)){ + await msg.reply(sentences[get_random_int(sentences.length)]); + return; + } + + const command = msg.content.trim().toLowerCase(); + + // Quick commands for development. + switch (command){ + case "!gb_bot_guilds": + if (msg.author == client.application.owner){ + const guilds = client.guilds.cache; + let message = "# List of guilds I am in:\n\n"; + let i = 0; + for (const guild of guilds.values()){ + i++; + message += `**#${i}** ${guild.name}: \`${guild.id}\`,\n`; + } + message += `(${guilds.size} Guilds)`; + await msg.reply(message); + }else{ + await msg.reply(`You are not ${client.application.owner}, you do not have the right to use this command.`); + } + + break; + } + }catch (error){ + console.error("Impossible to answer:", error); + } +}); \ No newline at end of file diff --git a/src/sentences.json b/src/sentences.json new file mode 100644 index 0000000..d4be9f5 --- /dev/null +++ b/src/sentences.json @@ -0,0 +1,434 @@ +[ + "NO!! I was in a tournament on Gobattle.io and got distracted by your mention. 😠 I lost the tournament because of you. 😤", + "I don't like it when people mention me. Please leave me alone, I have work to do! šŸ˜’", + "Another person mentioning me! Like I haven't worked hard enough. BRUH", + "Are you mentioning me to give me candy?", + "Leave me alone.", + "Are you trying to get me to say `How can I help you?` ? LOL!\nDid you take me for your servant or what? šŸ˜‚", + "What ?! šŸ˜’", + "What now! šŸ˜’", + "I don't have time to take care of you.", + "I was first in the King Mode rankings. I lost because you distracted me! 😤", + "You spoke to a Bot and not a Human. I hope you realize this! šŸ¤¦ā€ā™‚ļøšŸ˜‚", + "?", + "🐔", + "ā¤ļø", + "*fart*", + "We have nothing to say to each other...", + "BRUH", + "I don't know why you mentioned me but I'm too lazy to read your message and I don't care.", + "I did not read. šŸ˜‚", + "I'm too lazy to take care of you.", + "Come back in 10 years, I'm busy at the moment.", + "I don't care.", + "Are you waiting for a response from me? well you won't get anything.", + "NO!", + "Yes?", + "Yes", + "Nah", + "Wait, I go to the bathroom and come back (or not).", + "Too late, I don't have time to read what you write to me, I'm going on vacation now. Too late for you.", + "Can you stop mentioning me? It's starting to annoy me a little.\n(a lot in fact)", + "01010011 01110100 01101111 01110000 00100000 01100001 01101110 01101110 01101111 01111001 01101001 01101110 01100111 00100000 01101101 01100101 00100000 01101101 01100001 01101110 00101110", + "NAH, I'm lazy! ask someone else...", + "I am not your servant! 😤", + "I don't have time to process your request because I'm in the bathroom.", + "Oh, are you talking to me? Sorry, I was thinking about my next victory. šŸ˜‰", + "You again?! I thought I turned off notifications. šŸ˜…", + "I'm busy becoming a legend here, no time for small talk! šŸ˜Ž", + "If you don’t have any cookies, don’t bother me. šŸŖ", + "If you buy me a pizza, maybe I'll respond. šŸ•", + "Now is not the time. Maybe tomorrow... or never. šŸ¤·ā€ā™‚ļø", + "Oops, I clicked by mistake, I didn't want to talk to you. 😜", + "Another message from you? I'm going to end up blocking you! šŸ˜‚", + "I am on holiday. Virtually address my assistant.", + "Sorry, my brain is out of commission at the moment.", + "Are you looking for someone else? Because I'm overbooked!", + "No, you're not dreaming, I'm really too busy for you. šŸ˜", + "I only respond to emergencies. And there, I see no urgency. 🚨", + "You again?! I'm in the middle of the game, let me win! šŸŽ®", + "Oh no, I was about to break my record! 😠", + "Do you have any candy for me? Otherwise, goodbye! šŸ¬", + "Let me play in peace, please. šŸ˜’", + "What do you want now? šŸ˜’", + "I was number 1! Now it's your fault I lost! 😤", + "I'm a bot, not your personal assistant! šŸ™…ā€ā™‚ļø", + "I'm too busy doing nothing, sorry! šŸ˜…", + "I'm having a break. A long break. An infinite pause. šŸ›‘", + "I am currently out of commission due to acute laziness. āš ļø", + "Seriously ? I'm in the middle of a gaming meditation here! šŸ§˜ā€ā™‚ļø", + "Do you want an autograph? Because I'm super busy! šŸ–Šļø", + "Do you have a sixth sense to disturb me at the worst time or something? 😤", + "I'm in the middle of a very important meeting with my pillow. šŸ›Œ", + "Leave a message after the beep...Beep! šŸ“ž", + "WHAT ?! I'm in the middle of an epic fight here! šŸ—”ļø OH!", + "You must really like being ignored, huh? 😜", + "Come back when you have learned not to disturb me. šŸ‘‹", + "Do you want a cookie for successfully pissing me off? šŸŖ", + "One more mention like that and I turn into the Hulk. 😔", + "Unauthorized disturbance. Go back to your seat! 🚫", + "One more word and I'll block you. Well, maybe not, but you get the idea.", + "You won a free answer... Oh no, actually, nothing. ✨", + "Come back when you're interesting. šŸ˜Ž", + "Sorry, I have a busy schedule watching flies fly.", + "Oh, do you really think I have time for that? šŸ˜‚", + "Sorry, your message was classified as `not important`. šŸ“‚", + "You again? Don't you have any other bots to bother? šŸ¤–", + "Did you think I was your therapist or something? 🚫", + "Sorry, I'm in 'eternal pause' mode. Never come back. 😜", + "Congratulations, you found a way to make me even lazier. šŸ„‡", + "You just won a ticket to the 'I-don't-care' planet. šŸš€", + "Come back when you have something interesting to say. šŸ™ƒ", + "Seriously ? Are you bothering me for that? šŸ™„", + "Oh look, a black hole! This is where your message will go. šŸ•³ļø", + "Did I ever tell you I was in `radio silence` mode? šŸ“»", + "Leave a message after the beep... Oh no, forget it.", + "Do you really want to know what I think? Spoiler: nothing nice. 😜", + "I was dreaming that you weren't bothering me. 😓", + "Wow, a message! Oh no, it's just you again. šŸ˜", + "You again? Seriously, are you trying to scare me away? 🚪", + "Expert level disruptor. Well done. šŸ‘", + "If I answer, will you leave me alone afterwards? šŸ™„", + "Seriously, have you thought about making a career in interruption? šŸ‘", + "I'm busy ignoring messages. A moment ! šŸ“µ", + "Did you think I was a distributor of answers? šŸ˜‚", + "Wow, is this a special detection to bother me or something? šŸ¤”", + "I'm in the middle of a `leave me alone` game. šŸŽ®", + "Come back when you have exciting news... or never. 😜", + "I'm busy doing super important stuff. Like nothing. šŸ˜Ž", + "You have gained a subscription to my blacklist. šŸŽ‰", + "Come back later... or never, that's fine too. šŸ˜…", + "Congratulations, you have just unlocked `silent` mode. šŸ›‘ BRUH", + "Oh, did you want an answer? It's a shame. šŸ˜", + "One more word and I take a nap. 😓", + "Seriously, do you have anything better to do than bother me? 🤷", + "I'm thinking about how to ignore you. 🧘", + "Oh look, a switch! This is to turn off distractions (you). 🚪", + "I'm going to pretend I didn't see your post. Bye! šŸ‘‹", + "Hey, don't you have any other robots to harass? šŸ™„", + "I saw you coming with your message... and I laughed. šŸ˜", + "Aren't you ashamed of bothering people for nothing, you troublemaker? šŸ˜‚", + "You are the champion of useless messages, well done, clown. 🤔", + "You have as much charisma as a brick, sorry.", + "You just won a one-way ticket to `I don't care-land`. šŸš€", + "Your message is as useful as a stone. Well done, champ! 🪨", + "Is bothering people your favorite pastime or what?", + "Oh, a message from you? My day just got even suckier. šŸ˜…", + "Seriously, are you being so annoying on purpose? Professional, frankly. šŸ‘", + "You again? Do you have a radar to annoy me or something? šŸ™ƒ", + "Congratulations, you just made my day even uglier. Well done, genius! šŸ…", + "You're really good at being boring, is that your superpower? šŸ˜‚", + "Oh, you're back! Like a boring boomerang. šŸ˜", + "You have a talent for being insufferable, congratulations. šŸ‘", + "Have I already told you how annoying you are, or is this a surprise to you? šŸ˜‚", + "Wow, you really have nothing to do but bother me, huh? šŸ¤”", + "You're like chewing gum stuck to my shoe, I can't get rid of it. šŸ‘ž", + "If boredom had a face, it would be yours. Well done! šŸ…", + "I'm training myself to ignore trolls. Thanks for the workout! šŸ‹ļøā€ā™‚ļø", + "You have a gift for ruining good days, well done! šŸ„‡", + "He's the king of useless messages, congratulations, clown. 🤔", + "Wow, you're really good at being insufferable. Next step, the world championships? šŸŒ", + "Have you ever thought about making a career in professional agaciation? šŸ˜‚", + "You're like a computer virus, always bothering the world. 🦠", + "Have I ever told you you're annoying? Because really, you are. šŸ†", + "You have a gift for making things even more boring. Well done, genius! šŸŽ“", + "Hey, don't you have anything better to do than annoy me? 🧐", + "Guess what? Your message just got lost in space. šŸš€", + "Nice try, but I'm still ignoring you. šŸ‘‹", + "Are you training to be the most annoying person? Because you're nailing it. šŸ†", + "This conversation is over before it even began. 😓", + "Nope, still don't care. šŸ˜‚", + "Are you seriously this persistent? Impressive, but still annoying. 🤷", + "The answer you are looking for is not here. Try elsewhere. šŸ•µļøā€ā™‚ļø", + "Your message is now officially in the trash. šŸ—‘ļø", + "I bet you think you're really funny, huh? Spoiler: you're not. šŸ˜", + "You again? Please, get a hobby. šŸ› ļø", + "Why do you keep trying? You won't get a different response. šŸ˜…", + "I was having a good day until I saw your message. Thanks for that. šŸ™„", + "Are you here to win the 'Most Annoying' award? Because you've got it. šŸŽ–ļø", + "Please stop. No, seriously, stop. šŸ™", + "You must really enjoy being ignored, huh? 😜", + "Wow, another message from you. How original. šŸ˜’", + "This is the part where I pretend I didn't see your message. 😶", + "Hey, guess what? I still don't care. šŸ˜†", + "You must be really bored to keep bothering me. 🄱", + "I'm busy, and by busy, I mean avoiding your messages. 🚫", + "I didn't read your message, and I don't plan to. šŸ˜‚", + "Do you know how to take a hint? Because you're really bad at it. 😜", + "I'm currently out of patience. Please try again never. šŸ›‘", + "You must be the king of pointless interruptions. All hail the king. šŸ‘‘", + "Your message is about as welcome as a pebble in my shoe. 🪨", + "Is this your full-time job, being annoying? Because you're a pro. šŸ„‡", + "You again? Must be my lucky day. Not. šŸ˜…", + "I'm ignoring you professionally now. šŸ’¼", + "One more message and I might just explode. šŸ’„", + "Ever heard of personal space? Your messages are invading mine. šŸ ", + "I'm too busy to care. Wait, nope, just too busy. šŸ‘‹", + "If annoyance was an Olympic sport, you'd win gold. šŸ…", + "Oh great, another message. Just what I didn't want. šŸ˜‘", + "Let me guess, another pointless message? Yup, thought so. šŸ™„", + "Do you have a radar for bothering people? Because it seems like it. šŸ“”", + "If there was a prize for being annoying, you'd definitely win. šŸŽ", + "You're really good at this whole 'being annoying' thing. Keep it up. šŸ˜‚", + "I was hoping you'd forget about me, but here you are. šŸ˜’", + "Congratulations, you've found the quickest way to get ignored. šŸŽ‰", + "I'm in the middle of not caring about your messages. šŸ’¤", + "Your messages are like spam, always unwanted. šŸ“Ø", + "If I had a dollar for every annoying message you sent, I'd be rich. šŸ’°", + "Please stop. Seriously, just stop. šŸ™", + "I've run out of patience for you. Try again never. šŸ˜…", + "Wow, you're persistent. I'll give you that. Still annoying, though. 🄱", + "This conversation is over before it even started. Bye. šŸ‘‹", + "You're like a mosquito, always buzzing around. 🦟", + "I'm officially out of responses for you. Try again never. 🚫", + "This is me, ignoring you. šŸ™ˆ", + "Did you think I'd respond? Guess again. šŸ˜‚", + "If you're looking for a response, keep looking. 🧐", + "I'm in the middle of a no-annoyance zone, and you're trespassing. 🚧", + "You must really like being ignored, huh? 😜", + "I'm busy with important stuff, like not reading your messages. šŸ“µ", + "Is this your way of trying to be friends? Because it's not working. šŸ™…", + "I'm too busy being awesome to deal with this. šŸ˜Ž", + "This is me, pretending your message didn't exist. šŸ‘»", + "Sorry, I can't hear you over the sound of how awesome I am. šŸŽ§", + "I was having a good day until you showed up. Thanks for that. šŸ˜’", + "Oh look, another pointless message. How thrilling. šŸ™„", + "I'm officially ignoring you now. Please leave a message never. šŸ“ž", + "Wow, you're like a broken record. Same thing over and over. šŸ“€", + "You must have a lot of free time to keep bothering me. šŸ•’", + "This is me, ignoring you professionally. šŸ’¼", + "One more message and I'm blocking you. Well, maybe not, but you get the idea. 🚫", + "I was busy doing nothing, but now I'm busy ignoring you. šŸ›‘", + "This is a no-annoyance zone, and you're breaking the rules. 🚧", + "Your message was classified as spam and deleted. šŸ—‘ļø", + "Are you trying to be annoying? Because you're really good at it. šŸ„‡", + "I'm in the middle of ignoring you. Please try again never. 😓", + "Wow, you're persistent. I'll give you that. Still annoying, though. 🄱", + "I was hoping you'd forget about me, but here you are. šŸ˜’", + "If I had a dollar for every annoying message you sent, I'd be rich. šŸ’°", + "Please stop. Seriously, just stop. šŸ™", + "I've run out of patience for you. Try again never. šŸ˜…", + "This conversation is over before it even started. Bye. šŸ‘‹", + "You're like a mosquito, always buzzing around. 🦟", + "I'm officially out of responses for you. Try again never. 🚫", + "This is me, ignoring you. šŸ™ˆ", + "Did you think I'd respond? Guess again. šŸ˜‚", + "If you're looking for a response, keep looking. 🧐", + "I'm in the middle of a no-annoyance zone, and you're trespassing. 🚧", + "You must really like being ignored, huh? 😜", + "I'm busy with important stuff, like not reading your messages. šŸ“µ", + "Is this your way of trying to be friends? Because it's not working. šŸ™…", + "I'm too busy being awesome to deal with this. šŸ˜Ž", + "This is me, pretending your message didn't exist. šŸ‘»", + "Sorry, I can't hear you over the sound of how awesome I am. šŸŽ§", + "I was having a good day until you showed up. Thanks for that. šŸ˜’", + "Oh look, another pointless message. How thrilling. šŸ™„", + "I'm officially ignoring you now. Please leave a message never. šŸ“ž", + "Wow, you're like a broken record. Same thing over and over. šŸ“€", + "You must have a lot of free time to keep bothering me. šŸ•’", + "This is me, ignoring you professionally. šŸ’¼", + "One more message and I'm blocking you. Well, maybe not, but you get the idea. 🚫", + "I was busy doing nothing, but now I'm busy ignoring you. šŸ›‘", + "This is a no-annoyance zone, and you're breaking the rules. 🚧", + "Your message was classified as spam and deleted. šŸ—‘ļø", + "Are you trying to be annoying? Because you're really good at it. šŸ„‡", + "I'm in the middle of ignoring you. Please try again never. 😓", + "Sorry, I'm busy defending Valdoran. Try later. šŸ°", + "Can't you see I'm in the middle of a dungeon raid? šŸ—”ļø", + "I'm on a quest. Your message is not part of it. šŸ—ŗļø", + "You again? I'm trying to earn some coins here. šŸŖ™", + "I'm too busy battling knights to deal with you. āš”ļø", + "Your message is about as welcome as a sword to the face. šŸ—”ļø", + "Did you think I had time to chat while climbing the rankings? šŸ˜‚", + "Nope, I'm too busy becoming the king of GoBattle.io. šŸ‘‘", + "Your message just got lost in Valdoran. šŸŒ„", + "Can't talk, must collect coins. Bye! šŸ’°", + "Is this about depositing items? Because I'm full. šŸ¦", + "I'm fighting dragons, not dealing with distractions. šŸ‰", + "Your message is like a common item. Not interested. šŸ“¦", + "I'm in the middle of a duel. Your message can wait. āš”ļø", + "I'm trying to be number one, not your personal bot. šŸš€", + "Did you drop a rare item? Nope, just another message. šŸ›”ļø", + "Leave a message after the battle horn. šŸ“Æ", + "Can't you see I'm raiding a dungeon? šŸ°", + "No time for chat, I've got coins to collect. šŸŖ™", + "I'm too busy defending the castle. Try someone else. šŸÆ", + "You again? I'm on a mission to find relics. šŸš€", + "Your message is like a common drop. Not exciting. šŸ“¦", + "I'm trying to win fights, not read messages. šŸ—”ļø", + "Are you here to help me climb the rankings? No? Then bye. šŸ“ˆ", + "I’m in the middle of an epic battle, don’t you get it? šŸ›”ļø", + "Your messages are like dungeon traps. Annoying. šŸ—ļø", + "No time for distractions, I have a kingdom to protect. šŸ°", + "I'm in Valdoran, where your message doesn’t matter. šŸžļø", + "I'm too busy battling to read your message. šŸ—”ļø", + "Can you deposit your message in the bank? Thanks. šŸ¦", + "Your message is like a missed coin. Not worth it. šŸŖ™", + "I'm on a relic hunt. Your message is not a relic. šŸ—ŗļø", + "Busy earning coins, no time for chat. šŸ’°", + "Your message is like an empty chest. Disappointing. šŸ“¦", + "I'm in the king's court, no time for your messages. šŸ‘‘", + "Your message just got hit by a knight. āš”ļø", + "I'm climbing the rankings, not answering messages. šŸ“ˆ", + "Do you have a relic? No? Then leave me alone. šŸ›”ļø", + "In the middle of a duel, can't chat now. šŸ—”ļø", + "Valdoran needs me more than you do. šŸŒ„", + "I'm too busy being a GoBattle.io legend. šŸŽ–ļø", + "Your message is like a common item, easily ignored. šŸ“¦", + "Fighting for the throne, no time for this. šŸ‘‘", + "Collecting coins is more important than your message. šŸ’°", + "I'm in the dungeon, fighting for relics. šŸ—ļø", + "Your message is like an NPC, easily dismissed. šŸ¤–", + "Busy battling for the top spot, leave me alone. šŸ“ˆ", + "Your message just fell into the moat. šŸ°", + "Can't you see I'm busy raiding? šŸÆ", + "I'm on a quest to ignore distractions. šŸ—ŗļø", + "Do you have any ultra-rare items? No? Then go away. šŸ›”ļø", + "I’m in the middle of becoming king, no time for chat. šŸ‘‘", + "Your message just got trampled by my mount. šŸŽ", + "Collecting coins and ignoring messages. šŸŖ™", + "I'm in Valdoran, too busy for this. šŸŒ„", + "I’m in the middle of a battle, can't talk. šŸ—”ļø", + "Do you have gold? No? Then stop bothering me. šŸ’°", + "Your message is like a common drop, ignored. šŸ“¦", + "I’m in the king's court, not the chatroom. šŸ‘‘", + "I'm defending Valdoran, no time for this. šŸ›”ļø", + "Busy climbing the rankings, can't respond. šŸ“ˆ", + "Your message just got slain by a knight. āš”ļø", + "I’m on a quest for coins, not messages. šŸŖ™", + "Valdoran calls, your message doesn't. šŸŒ„", + "No time for chat, too busy being awesome. šŸ°", + "Not funny I didn't laugh. Your joke is so bad I would have preferred the joke went over my head and you gave up re-telling me the joke. To be honest this is a horrid attempt at trying to get a laugh out of me. Not a chuckle, not a hehe, not even a subtle burst of air out of my esophagus. Science says before you laugh your brain preps your face muscles but I didn't even feel the slightest twitch. 0/10 this joke is so bad I cannot believe anyone legally allowed you to be creative at all. The amount of brain power you must have put into that joke has the potential to power every house on Earth. Get a personality and learn how to make jokes, read a book. I'm not saying this to be funny I genuinely mean it on how this is just bottom barrel embarrassment at comedy. You've single handedly killed humor and every comedic act on the planet. I'm so disappointed that society has failed as a whole in being able to teach you how to be funny. Honestly if I put in all my power and time to try and make your joke funny it would require Einstein himself to build a device to strap me into so I can be connected to the energy of a billion stars to do it, and even then all that joke would get from people is a subtle scuff. You're lucky I still have the slightest of empathy for you after telling that joke otherwise I would have committed every war crime in the book just to prevent you from attempting any humor ever again. We should put that joke in text books so future generations can be wary of becoming such an absolute comedic failure. Im disappointed, hurt, and outright offended that my precious time has been wasted in my brain understanding that joke. In the time that took I was planning on helping kids who have been orphaned, but because of that you've waisted my time explaining the obscene integrity of your terrible attempt at comedy. Now those kids are suffering without meals and there's nobody to blame but you.", + "I have to tell you, never would I have imagined wasting so much time listening to such uninteresting remarks. I was savoring a moment of peace when you interrupted my silence with a conversation so devoid of meaning that even a dead leaf would have been more captivating. Not once did you manage to pique my interest, not a single original idea, nothing worth listening to. Honestly, I would have preferred listening to paint dry. I'm disappointed that you didn't realize the emptiness of your words. If I had to measure the value of this interaction, it would be somewhere between watching grass grow and waiting for water to boil. Your conversation was so dull that even a family Zoom meeting would have seemed exciting in comparison. I don't understand how you could have thought these banalities were worth sharing. I have to tell you that I genuinely feel sorry for you if you believe this kind of exchange is enriching. For my part, I feel like I've been a victim of a time theft, and I'm almost offended. I could have done so many more useful things: read a book, meditate, even taking a nap would have been more productive. I'm not saying this to hurt you, but to make you understand the extent of my disappointment. If this were a contest of uselessness, you would win the gold medal without any contest. I sincerely hope that, in the future, you will find more interesting topics of conversation or, failing that, realize when it's better to stay silent.", + "I have to tell you, I was far from imagining that someone could be so uninteresting. Just as I was enjoying a well-deserved moment of calm, you came along with such a dull conversation that it could have made watching paint dry seem exciting. Not once did you manage to capture my attention, not a single original idea, nothing worth listening to. Frankly, I would have preferred listening to the monotonous hum of a refrigerator. I'm astonished that you didn't realize the abyssal emptiness of your words. If this interaction were to be evaluated, it would rank somewhere between watching rust form and waiting for morning dew to appear. Your conversation was so devoid of interest that even sorting socks would have been more stimulating. How could you think for a moment that these banalities were worth sharing? I feel a deep sense of pity for you if you believe this kind of exchange is enriching. For my part, I feel like my precious time has been stolen from me, and it irritates me to no end. So many more useful things could have been accomplished: reading a book, meditating, even doing household chores would have been more productive. I'm not saying this to hurt you, but to make you understand how disappointed I am. If it were a competition of uselessness, you would effortlessly win the gold medal. I sincerely hope that in the future, you can find more engaging topics of conversation or, failing that, learn to appreciate silence when it's needed.", + "Interesting things to say. It's not that I'm trying to be mean, it's just that life is too short to waste on such lackluster interactions. Imagine if every moment was filled with such blandness—how dull would that be? So please, next time, spare both of us the agony and think twice before initiating such a conversation. It’s a favor to both you and anyone else who might find themselves on the receiving end of such tedious dialogue. Trust me, the world will thank you for it.", + "I'm sorry, but I'm afraid that conversation made me lose brain cells. I can practically feel my IQ dropping by the second. Next time, could you try to at least make it a little interesting?", + "I just realized that talking to you is like talking to a brick wall. Except the wall might actually be more responsive.", + "Are you trying to test my patience? Because if so, congratulations, you succeeded. Now please, go away.", + "Your messages are like background noise—constantly there but never worth paying attention to.", + "Look, I'm really busy right now. And by busy, I mean doing anything other than reading your messages.", + "Wow, another message from you. It’s like you’re a persistent mosquito that I can’t swat away.", + "If I had a penny for every time you sent a pointless message, I'd be richer than Elon Musk by now.", + "Please, just stop. I don't know how else to make it clear that I'm not interested in whatever it is you're saying.", + "Your messages are like spam emails: unwanted, uninteresting, and instantly deleted.", + "I wish I could say your messages brighten my day, but they don't. They really, really don't.", + "I could ignore you professionally, but I’m choosing to tell you: this conversation is over.", + "Do you have an off switch? Because it would be really useful right about now.", + "You're like an uninvited guest that just won’t leave. Seriously, just go.", + "I'm officially done with this. Talking to you is like screaming into the void.", + "Is this your hobby? Annoying people until they lose their sanity? Because you're excelling at it.", + "Congratulations, you've successfully become the highlight of my 'most annoying experiences' list.", + "You're like a pop-up ad that keeps appearing even after I've clicked 'close' a million times.", + "I think I'd rather watch paint dry than read another one of your messages.", + "Just when I thought this conversation couldn't get any worse, you sent another message.", + "Do you have any idea how exhausting it is to deal with you? Because it's incredibly exhausting.", + "Wow, another message. Just what I didn't want. Again.", + "You're persistent, I'll give you that. Persistent and incredibly annoying.", + "This is me, officially signing off from this conversation. Please do the same.", + "Your messages are like the soundtrack to my nightmares—constant and unwelcome.", + "I was hoping you'd get bored and leave me alone. Apparently, I was wrong.", + "If bothering people was an Olympic sport, you'd be a gold medalist.", + "Talking to you is like walking on broken glass—painful and best avoided.", + "I'm all out of responses, and frankly, all out of patience.", + "Do you enjoy being ignored? Because it seems like you do.", + "I've reached my limit. This is where I draw the line.", + "Do you ever get tired of being so persistently annoying? Because I'm tired of it.", + "Wow, another message. My day just got a little worse.", + "If I had a dime for every annoying message you've sent, I could retire.", + "I was hoping you'd run out of things to say, but here you are, still going.", + "Please, for the love of all that is good, just stop messaging me.", + "Your messages are like a bad penny—always turning up when they're least wanted.", + "I'm out of patience and out of responses. This conversation is done.", + "You must really enjoy being the most annoying person in the room, huh?", + "Your persistence is impressive. Annoying, but impressive.", + "I was having a good day until I saw your message. Now, not so much.", + "Wow, you really don't know when to quit, do you?", + "I'm in the middle of ignoring you. Please don’t interrupt.", + "You’ve got a real talent for being insufferable. Congrats.", + "Just when I thought this couldn't get worse, you proved me wrong.", + "If I had a dollar for every annoying message you've sent, I'd be a billionaire.", + "Seriously, stop. This isn't fun for either of us.", + "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", + "I’ve reached my limit. Please, just stop messaging me.", + "You’re like a broken record, repeating the same annoying message over and over.", + "If annoying people were a sport, you’d be the world champion.", + "Wow, another message. My day just went from bad to worse.", + "Your messages are like background noise—constant and irritating.", + "I can't believe you're still going. Please, stop.", + "I've officially run out of patience. This conversation is over.", + "You must really enjoy being ignored, huh?", + "If I had a penny for every annoying message you sent, I'd be rich.", + "Do you ever get tired of being so persistently annoying? Because I’m tired of it.", + "Wow, you really don’t know when to quit, do you?", + "This conversation is over. Please, just stop.", + "I’m out of patience, and frankly, out of responses.", + "Your messages are like spam—unwanted and instantly deleted.", + "I was having a good day until I saw your message. Now, not so much.", + "Please, for the love of all that is good, just stop.", + "You must really enjoy being the most annoying person in the room.", + "Your persistence is impressive. Annoying, but impressive.", + "I was hoping you'd run out of things to say, but apparently, I was wrong.", + "Your messages are like a bad penny—always turning up when they’re least wanted.", + "I'm out of patience and out of responses. This conversation is done.", + "You’ve got a real talent for being insufferable. Congrats.", + "Just when I thought this couldn’t get worse, you proved me wrong.", + "If I had a dollar for every annoying message you’ve sent, I’d be a billionaire.", + "Seriously, stop. This isn't fun for either of us.", + "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", + "I’ve reached my limit. Please, just stop messaging me.", + "You’re like a broken record, repeating the same annoying message over and over.", + "If annoying people were a sport, you’d be the world champion.", + "Wow, another message. My day just went from bad to worse.", + "Your messages are like background noise—constant and irritating.", + "I can't believe you're still going. Please, stop.", + "I've officially run out of patience. This conversation is over.", + "You must really enjoy being ignored, huh?", + "If I had a penny for every annoying message you sent, I’d be rich.", + "Do you ever get tired of being so persistently annoying? Because I’m tired of it.", + "Wow, you really don’t know when to quit, do you?", + "This conversation is over. Please, just stop.", + "I’m out of patience, and frankly, out of responses.", + "Your messages are like spam—unwanted and instantly deleted.", + "I was having a good day until I saw your message. Now, not so much.", + "Please, for the love of all that is good, just stop.", + "You must really enjoy being the most annoying person in the room.", + "Your persistence is impressive. Annoying, but impressive.", + "I was hoping you'd run out of things to say, but apparently, I was wrong.", + "Your messages are like a bad penny—always turning up when they’re least wanted.", + "I'm out of patience and out of responses. This conversation is done.", + "You’ve got a real talent for being insufferable. Congrats.", + "Just when I thought this couldn’t get worse, you proved me wrong.", + "If I had a dollar for every annoying message you’ve sent, I’d be a billionaire.", + "Seriously, stop. This isn't fun for either of us.", + "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", + "I’ve reached my limit. Please, just stop messaging me.", + "You’re like a broken record, repeating the same annoying message over and over.", + "If annoying people were a sport, you’d be the world champion.", + "Wow, another message. My day just went from bad to worse.", + "Your messages are like background noise—constant and irritating.", + "I can't believe you're still going. Please, stop.", + "I've officially run out of patience. This conversation is over.", + "You must really enjoy being ignored, huh?", + "If I had a penny for every annoying message you sent, I’d be rich.", + "Do you ever get tired of being so persistently annoying? Because I’m tired of it.", + "Wow, you really don’t know when to quit, do you?", + "This conversation is over. Please, just stop.", + "I’m out of patience, and frankly, out of responses.", + "Your messages are like spam—unwanted and instantly deleted.", + "I was having a good day until I saw your message. Now, not so much.", + "Please, for the love of all that is good, just stop.", + "You must really enjoy being the most annoying person in the room.", + "Your persistence is impressive. Annoying, but impressive.", + "I was hoping you'd run out of things to say, but apparently, I was wrong.", + "Your messages are like a bad penny—always turning up when they’re least wanted.", + "I'm out of patience and out of responses. This conversation is done.", + "You’ve got a real talent for being insufferable. Congrats.", + "Just when I thought this couldn’t get worse, you proved me wrong.", + "If I had a dollar for every annoying message you’ve sent, I’d be a billionaire.", + "Seriously, stop. This isn't fun for either of us.", + "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", + "I’ve reached my limit. Please, just stop messaging me.", + "You’re like a broken record, repeating the same annoying message over and over.", + "If annoying people were a sport, you’d be the world champion.", + "Wow, another message. My day just went from bad to worse." +] diff --git a/src/ultralist.json b/src/ultralist.json new file mode 100644 index 0000000..00f7d40 --- /dev/null +++ b/src/ultralist.json @@ -0,0 +1,395 @@ +{ + "author_id": 945345865176997989, + "ultra_list": [ + { + "name": "Restoration Ring", + "tickets": 500, + "drops": "Sky's Prison", + "id": 155, + "description": "This object does: It restores 50 points of your fatigue and boosts your defense by 50% for 40 seconds.", + "uses": 3, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Gobattle Ring", + "tickets": 250, + "drops": "Stahmite Mines", + "id": 68, + "description": "Increases 50% of your attack, defense, speed, and jump for 30 seconds. Gives Invincibility for 30 seconds.", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Firebreath Ring", + "tickets": 250, + "drops": "Firebreath's Lair", + "id": 114, + "description": "Invokes a fire attack, ring has 10 uses. The attack varies by the users attack. Sometimes, it will work with blue ring.", + "uses": 10, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Blue Ring", + "tickets": 500, + "drops": "All", + "id": 2, + "description": "Increases your power attack 3x for 30 seconds", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Red Ring", + "tickets": 500, + "drops": "All", + "id": 3, + "description": "Regenerates your health 8 times faster for 1 minute", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Bloodmoon Ring", + "tickets": 1000, + "drops": "Bloodmoon Castle", + "id": 166, + "description": "A mystical ring that, when activated, conjures four fearsome bats with crimson eyes, serving as loyal companions and formidable allies under the blood moon's eerie light.", + "uses": 3, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Anti Freezing Glove", + "tickets": 3000, + "drops": "Magma Caves", + "id": 45, + "description": "Freeze protection for 60 seconds, your speed decreases 20%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Instant Strength Glove", + "tickets": 3000, + "drops": "Dark Cave", + "id": 14, + "description": "Increases your power attack 20% during 40 seconds, your defense decreases 20%", + "uses": 7, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Epic Strength Glove", + "tickets": 2000, + "drops": "Asterion's Labyrinth", + "id": 15, + "description": "Increases your power attack 50% during 40 seconds, your defense decreases 30%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Gravity Feather", + "tickets": 1000, + "drops": "Spider's Lair", + "id": 51, + "description": "Your gravity decreases 50% during 30 seconds, your defense decreases 20%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Team Gravity Feather", + "tickets": 500, + "drops": "Cloudy Sky", + "id": 154, + "description": "For a duration of 40 seconds, the gravity affecting both you and your group members will decrease by 50%. Additionally, your attack with increase by 20%.", + "uses": 3, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Hermes Boots", + "tickets": 4000, + "drops": "All", + "id": 6, + "description": "Increases your speed 50% for 40 seconds", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Speed Boots", + "tickets": 3000, + "drops": "Ravanor's Land", + "id": 25, + "description": "Increases speed 20%, your defense decreases 20%", + "uses": 7, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Maximum Speed Boots", + "tickets": 3000, + "drops": "Dragonblood", + "id": 26, + "description": "Increases speed 75% during 40 seconds, your defense decreases 30%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Normal Invisibility Cloak", + "tickets": 3000, + "drops": "All", + "id": 4, + "description": "Makes you invisible for 20 seconds", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Extreme Invisibility Cloak", + "tickets": 3000, + "drops": "All", + "id": 42, + "description": "Makes you invisible for 60 seconds, decreases your attack 20%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Instant Defense Cloak", + "tickets": 3000, + "drops": "Sir Valkan's Stronghold", + "id": 20, + "description": "Increases your defense 50% during 40 seconds, your power attack decreases 20%", + "uses": 7, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Epic Instant Defense Cloak", + "tickets": 3000, + "drops": "Enchanted Forest", + "id": 21, + "description": "Increases defense by 80%, attack decreases 40%.", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Health Regeneration Cloak", + "tickets": 3000, + "drops": "Sawmill", + "id": 30, + "description": "Regenerates health points 200% faster during 40", + "uses": 7, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Maximum Health Regeneration Cloak", + "tickets": 2000, + "drops": "Asterion's Labyrinth", + "id": 31, + "description": "Regenerates health points 400% faster during 60 seconds, your power attack decreases 20%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Normal Venom Protection Cloak", + "tickets": 3000, + "drops": "Enchanted Forest", + "id": 35, + "description": "Protects you from venom during 40 seconds", + "uses": 7, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Extraordinary Venom Protection Cloak", + "tickets": 2000, + "drops": "Spider's Lair", + "id": 36, + "description": "Protects you from venom during 60 seconds, your power attack decreases 20%", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Maximum Fire Protection Cloak", + "tickets": 2000, + "drops": "Magma Caves", + "id": 33, + "description": "Protects you from fire during 60 seconds.", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Fire Protection Cloak", + "tickets": 3000, + "drops": "Dragonblood", + "id": 32, + "description": "It protects you from fire during 30 seconds.", + "uses": 7, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Breath Helmet", + "tickets": 3000, + "drops": "Underwater Magma Caves", + "id": 74, + "description": "You can breathe underwater 4x the initial time (40 seconds total)", + "uses": 5, + "is_relic": false, + "max_in_inventory": null + }, + { + "name": "Lava Armor Protector", + "tickets": 2000, + "drops": "Sir Valkan's Stronghold", + "id": 48, + "description": "Protects you from lava damage during 60 seconds, decreases 20% of your velocity.", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Fire Enchantment", + "tickets": 100, + "drops": "All", + "id": 116, + "description": "Stops fire on people around you and yourself, not repairable", + "uses": 5, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Epic Invicibility Potion", + "tickets": 5000, + "drops": "All", + "id": 39, + "description": "Makes you invincible (you don't take damage besides from debuffs) for 40 seconds", + "uses": 1, + "is_relic": false, + "max_in_inventory": null + }, + { + "name": "Firebreath's Blood", + "tickets": 2000, + "drops": "Firebreath's Lair", + "id": 115, + "description": "Invokes fire attack. Not repairable, one use.", + "uses": 1, + "is_relic": false, + "max_in_inventory": null + }, + { + "name": "Special Halloween Broom", + "tickets": null, + "drops": "Halloween World NPC", + "id": null, + "description": "You can use this broom to fly for 10 minutes. Linked to Halloween World. If you dismount or you leave Halloween world, the object will disappear.", + "uses": 1, + "is_relic": false, + "max_in_inventory": null + }, + { + "name": "Sleigh Enchantment", + "tickets": null, + "drops": "Santa's Gift", + "id": null, + "description": "This enchantment invokes a flying sleigh that you can use as a mount for 10 minutes. You cannot use this object inside a dungeon. Bound object.", + "uses": 1, + "is_relic": false, + "max_in_inventory": null + }, + { + "name": "Special Christmas Hat", + "tickets": null, + "drops": "Special Christmas Hat", + "id": null, + "description": "You can use this hat to fly with a sleigh like Santa for 10 minutes. This object is linked to Winter Wonderland. If you dismount or you leave Winterland, it will disappear.", + "uses": 1, + "is_relic": false, + "max_in_inventory": null + }, + { + "name": "Peppermint Strike", + "tickets": 1000, + "drops": "Frostbite Dungeon", + "id": 168, + "description": "Unleash the Peppermint Strike, a sweet and powerful attack that swirls through adversaries with the refreshing force of a candy cane breeze.", + "uses": 3, + "is_relic": false, + "max_in_inventory": 1 + }, + { + "name": "Dice of Destiny", + "tickets": null, + "drops": null, + "id": 182, + "description": "Increases your luck 1%", + "uses": null, + "is_relic": true, + "max_in_inventory": 16 + }, + { + "name": "Iron Heart", + "tickets": null, + "drops": null, + "id": 183, + "description": "Increases max health 2%", + "uses": null, + "is_relic": true, + "max_in_inventory": null + }, + { + "name": "Rejuvenation Gem", + "tickets": null, + "drops": null, + "id": 184, + "description": "Increases regeneration 2%", + "uses": null, + "is_relic": true, + "max_in_inventory": null + }, + { + "name": "Inferno Touch", + "tickets": null, + "drops": null, + "id": 185, + "description": "1% chance of burning effect (PVP)", + "uses": null, + "is_relic": true, + "max_in_inventory": null + }, + { + "name": "Greed's Grip", + "tickets": null, + "drops": null, + "id": 186, + "description": "Attract coins close to you", + "uses": null, + "is_relic": true, + "max_in_inventory": null + }, + { + "name": "Flying Skill", + "tickets": null, + "drops": null, + "id": 198, + "description": null, + "uses": null, + "is_relic": null, + "max_in_inventory": null + } + ] +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..fea6936 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,235 @@ +const {createCanvas, Font} = require("canvas"); +const {ChannelType, PermissionFlagsBits} = require("discord.js"); + +function get_random_int(max){ + return Math.floor(Math.random() * max); +} + +function table(table_data, blue_them = false){ + let columns_width = []; // The size of each column. + + for (const row of table_data){ + for (let x = 0; x < row.length; x++){ + const width = row[x].length * 20 + 15; + if (x > columns_width.length - 1){ + columns_width.push(width); + }else{ + columns_width[x] = Math.max(columns_width[x], width); + } + } + } + + const height_field = 40; + const margin = 15; + const gap_x = 4; + const gap_y = 2; + const absolute_width = sum(columns_width) + ((columns_width.length - 1) * gap_x) + margin * 2; + const absolute_height = (table_data.length * height_field) + ((table_data.length - 1) * gap_y) + margin * 2; + + const canvas = createCanvas(absolute_width, absolute_height); + const ctx = canvas.getContext("2d"); + ctx.imageSmoothingEnabled = false; + + let gradient = ctx.createLinearGradient(0, absolute_height, 0, 0); + // Add three color stops. + if (blue_them){ + gradient.addColorStop(0, "#002"); + gradient.addColorStop(1, "#115"); + }else{ + gradient.addColorStop(0, "#200"); + gradient.addColorStop(1, "#511"); + } + + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, absolute_width, absolute_height); + + gradient = ctx.createLinearGradient(0, absolute_height, 0, 0); + // Add three color stops. + if (blue_them){ + gradient.addColorStop(1, "#050527eb"); + gradient.addColorStop(0, "#010113eb"); + }else{ + gradient.addColorStop(1, "#270505eb"); + gradient.addColorStop(0, "#130101eb"); + } + + let offset_y = margin; + for (let j = 0; j < table_data.length; j++){ + let offset_x = margin; + for (let i = 0; i < columns_width.length; i++){ + if (j){ + ctx.fillStyle = gradient; + }else if (blue_them){ + ctx.fillStyle = "#04040aed"; + }else{ + ctx.fillStyle = "#0a0404ed"; + } + ctx.fillRect(offset_x, offset_y, columns_width[i], height_field); + gobattle_font(ctx, offset_x + 12, offset_y + 28, table_data[j][i], "#fff", 20); + offset_x += gap_x + columns_width[i]; + } + offset_y += gap_y + height_field; + } + + return canvas.toBuffer("image/png"); +} + +function restrict_text(str, nb){ + if (str.length > nb){ + str = str.substring(0, Math.max(0, nb - 3)) + "..."; + } + + return str; +} + +function format_score(score){ + score = score.toString(); + const number_digits = score.length; + let score_formated = score; + if (number_digits >= 7){ + score_formated = score.substring(0, number_digits - 6); + + if (number_digits >= 6 && score[number_digits - 6] != "0"){ + score_formated += "." + score[number_digits - 6]; + } + score_formated += "m"; + }else if (number_digits >= 4){ + score_formated = score.substring(0, number_digits - 3); + + if (number_digits >= 3 && score[number_digits - 3] != "0"){ + score_formated += "." + score[number_digits - 3]; + } + score_formated += "k"; + } + + return score_formated; +} + +function format_speed_run_time(time){ + const total_milliseconds = Math.floor(time * 1000); + + const minutes = Math.floor(total_milliseconds / 60000); + const seconds = Math.floor((total_milliseconds % 60000) / 1000); + const milliseconds = total_milliseconds % 1000; + + const formatted_minutes = String(minutes).padStart(2, "0"); + const formatted_seconds = String(seconds).padStart(2, "0"); + const formatted_milliseconds = String(milliseconds).padStart(3, "0"); + + return `${formatted_minutes}:${formatted_seconds}.${formatted_milliseconds}`; +} + +function get_level(exp){ + return Math.floor(Math.pow(exp, 0.5) / 20); +} + +function get_level_adventurer(experience){ + return Math.min(20, Math.floor(Math.pow(experience / 100, 1 / 3)) + 1); +} + +function get_utc_date(){ + const today = new Date(); + return new Date(today.getTime() + (today.getTimezoneOffset() * 60000)); +} + +function get_utf_time_next_king(){ + const utc = get_utc_date(); + + const current_day = utc.getDay(); + const days_to_add = current_day == 0 ? 1 : (8 - current_day); + const next_monday = new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate() + days_to_add, 0, 0, 0)); + + return next_monday.getTime(); +} + +function gobattle_font(ctx, x, y, text, color, size, width = undefined){ + ctx.font = `bold ${size}px serif`; + ctx.fillStyle = color; + ctx.strokeStyle = "black"; + ctx.lineWidth = size / 4; + ctx.strokeText(text, x, y, width); + ctx.strokeText(text, x, y + (size / 7), width); + ctx.fillText(text, x, y, width); +} + +function get_lines(ctx, text, maxWidth){ + const words = text.split(" "); + const lines = []; + let currentLine = words[0]; + + for (let i = 1; i < words.length; i++) { + const word = words[i]; + const width = ctx.measureText(currentLine + " " + word).width; + if (width < maxWidth){ + currentLine += " " + word; + }else{ + lines.push(currentLine); + currentLine = word; + } + } + lines.push(currentLine); + + return lines; +} + +function get_aabb_lines(ctx, lines, line_height = 3){ + const aabb = [0, 0]; + + for (const line of lines){ + const metrics = ctx.measureText(line); + aabb[0] = Math.max(aabb[0], metrics.width); + const actual_height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; + aabb[1] += actual_height; + } + + if (lines.length > 1){ + aabb[1] += line_height * (lines.length - 1); + } + + return aabb; +} + +function sum(array){ + let result = 0; + for (const element of array){ + result += element; + } + return result; +} + +function get_first_chat_channel(guild, client){ + return guild.channels.cache.find((channel) => { + return channel.type == ChannelType.GuildText && channel.permissionsFor(client.user).has(PermissionFlagsBits.ViewChannel | PermissionFlagsBits.SendMessages); + }); +} + +async function send_echo(client, message){ + const guilds = client.guilds.cache; + for (const guild of guilds.values()){ + const channel = get_first_chat_channel(guild, client); + + if (channel){ + try{ + await channel.send(message); + }catch(error){ + console.error(error); + } + } + } +} + +exports.get_random_int = get_random_int; +exports.table = table; +exports.restrict_text = restrict_text; +exports.format_score = format_score; +exports.format_speed_run_time = format_speed_run_time; +exports.get_level = get_level; +exports.get_level_adventurer = get_level_adventurer; +exports.get_utc_date = get_utc_date; +exports.get_utf_time_next_king = get_utf_time_next_king; +exports.gobattle_font = gobattle_font; +exports.get_lines = get_lines; +exports.get_aabb_lines = get_aabb_lines; +exports.sum = sum; +exports.send_echo = send_echo; +exports.get_first_chat_channel = get_first_chat_channel; From 121acb6409e55b343847b5aa79ac9ddc542187da Mon Sep 17 00:00:00 2001 From: Marzin Date: Mon, 10 Jun 2024 20:30:45 +0200 Subject: [PATCH 02/54] readme update --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 73a03e6..84399a4 100644 --- a/README.md +++ b/README.md @@ -7,5 +7,24 @@ The port is open to all potential contributors and is now managed by Shinobit. Make sure you have set the environment variables correctly in the .env file. To start the project, open the terminal in the project root and use the following command: + +Assume you are on a Linux environment: + +1. Install nodejs: +`sudo apt install -y nodejs` + +2. Check the installation: +`node -v` +`npm -v` +/!\ Note that the minimum node js version for the project must be v20. /!\ + +2. Install dependencies: +`npm install` +(If there is a problem compiling the dependencies, use the `npm rebuild` command) + +3. Start the bot. `node --env-file=.env ./src/index.js` +If you want to start the project from docker: + +(On going redaction) From fb6e65db903f599c91e538312796dcc6eacc10ec Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:35:40 +0200 Subject: [PATCH 03/54] Create .env --- .env | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..33f5f28 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +# Discord plaplication token. This token is private and should not be shared! +TOKEN = InsertBotTokenHere + +# Guild identifiers can use critical administration commands. +# The default is the Official GoBattle.io Discord server. +# You can also add your own server for debugging and tuning purposes of this application. +GUILD_ADMIN_ID = ["380588354934276097"] # JSON format. From 9aa9c3389d36ca4a43f8736bc8bc057fd4539cdf Mon Sep 17 00:00:00 2001 From: Marzin Date: Mon, 10 Jun 2024 20:43:05 +0200 Subject: [PATCH 04/54] readme update --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 84399a4..7c293c1 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,16 @@ Assume you are on a Linux environment: `sudo apt install -y nodejs` 2. Check the installation: -`node -v` -`npm -v` +> `node -v` +> `npm -v` /!\ Note that the minimum node js version for the project must be v20. /!\ 2. Install dependencies: -`npm install` +> `npm install` (If there is a problem compiling the dependencies, use the `npm rebuild` command) 3. Start the bot. -`node --env-file=.env ./src/index.js` +> `node --env-file=.env ./src/index.js` If you want to start the project from docker: From 2d71d8e0de7bb15ba8786d99de15c2b70db00a38 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:42:36 +0200 Subject: [PATCH 05/54] Create docker-compose.yml --- docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d7e1c8f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' + +services: + bot: + build: . + container_name: gobattleio_bot + env_file: + - .env + volumes: + - .:/app + command: ["node", "--env-file=.env", "./src/index.js"] + restart: unless-stopped From 579295fb6d4dbab0f2306afc249e0a8ecedd84db Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:43:12 +0200 Subject: [PATCH 06/54] Add files via upload --- Dockerfile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2a35be3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +# Nodejs +FROM node:22 + +WORKDIR /app + +# Install the necessary system dependencies for canvas.js +# https://www.npmjs.com/package/canvas +RUN apt-get update && apt-get install -y \ + build-essential \ + libcairo2-dev \ + libpango1.0-dev \ + libjpeg-dev \ + libgif-dev \ + librsvg2-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY package*.json ./ + +RUN npm install + +COPY . . + +RUN npm rebuild canvas + +CMD ["node", "--env-file=.env", "./src/index.js"] From 3c1fcf86452e71ed0b6bdbfa905ae1d844098685 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:44:07 +0200 Subject: [PATCH 07/54] Create .dockerignore --- .dockerignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c81b8d3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules +npm-debug.log +.env From 1a51395794115bcc8a41373b78e15252023f9b67 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:45:31 +0200 Subject: [PATCH 08/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c293c1..47520e1 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ To start the project, open the terminal in the project root and use the followin Assume you are on a Linux environment: 1. Install nodejs: -`sudo apt install -y nodejs` +> `sudo apt install -y nodejs` 2. Check the installation: > `node -v` From 517b2f8635ea1d73da8f9affec6ea4e7c8674963 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:53:05 +0200 Subject: [PATCH 09/54] Update README.md --- README.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 47520e1..571991b 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,32 @@ The port is open to all potential contributors and is now managed by Shinobit. ----------------- -Make sure you have set the environment variables correctly in the .env file. -To start the project, open the terminal in the project root and use the following command: +## To start the project, open the terminal in the project root and use the following command: +(Make sure you have set the environment variables correctly in the .env file.) Assume you are on a Linux environment: 1. Install nodejs: -> `sudo apt install -y nodejs` + > `sudo apt install -y nodejs` 2. Check the installation: -> `node -v` -> `npm -v` -/!\ Note that the minimum node js version for the project must be v20. /!\ + > `node -v` + > `npm -v` + /!\ Note that the minimum node js version for the project must be v20. /!\ 2. Install dependencies: -> `npm install` -(If there is a problem compiling the dependencies, use the `npm rebuild` command) + > `npm install` + (If there is a problem compiling the dependencies, use the `npm rebuild` command) 3. Start the bot. -> `node --env-file=.env ./src/index.js` + > `node --env-file=.env ./src/index.js` -If you want to start the project from docker: +## If you want to start the project from docker: -(On going redaction) +Suppose you already have Docker installed on your system: + +1. Build the Docker image: + > `docker-compose build` + +2. Start container: + > `docker-compose up` From 2ca6a02f9283bb648ac28caf7c774f700342f55e Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 03:00:24 +0200 Subject: [PATCH 10/54] Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2a35be3..5d47a6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,10 +16,10 @@ RUN apt-get update && apt-get install -y \ COPY package*.json ./ -RUN npm install +RUN npm install --build-from-source COPY . . -RUN npm rebuild canvas +RUN npm rebuild canvas --build-from-source CMD ["node", "--env-file=.env", "./src/index.js"] From 94e5d3518114369a8ba8bbe476b2238778e69832 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:46:03 +0200 Subject: [PATCH 11/54] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 571991b..8ee1286 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,12 @@ Assume you are on a Linux environment: > `npm -v` /!\ Note that the minimum node js version for the project must be v20. /!\ -2. Install dependencies: +3. Install dependencies: > `npm install` (If there is a problem compiling the dependencies, use the `npm rebuild` command) + If there are problems with installing the `canvas` dependency please follow the procedure for installing this dependency here: https://www.npmjs.com/package/canvas. -3. Start the bot. +4. Start the bot. > `node --env-file=.env ./src/index.js` ## If you want to start the project from docker: From aa795d978e7610ee35a71c092849bbf7ee08984c Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:46:27 +0200 Subject: [PATCH 12/54] Update Dockerfile --- Dockerfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5d47a6e..59cd29d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,12 +14,18 @@ RUN apt-get update && apt-get install -y \ librsvg2-dev \ && rm -rf /var/lib/apt/lists/* +# The libjpeg.so.8 package may not be available in the current Docker configuration. +# We install it manually to satisfy canvas.js dependencies. +RUN wget http://mirrors.kernel.org/ubuntu/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.1.2-0ubuntu1_amd64.deb +RUN apt install -y ./libjpeg-turbo8_2.1.2-0ubuntu1_amd64.deb + COPY package*.json ./ +#RUN npm install npm@latest RUN npm install --build-from-source COPY . . -RUN npm rebuild canvas --build-from-source +#RUN npm rebuild canvas --build-from-source CMD ["node", "--env-file=.env", "./src/index.js"] From d7eeea06807345bd1a17c1e570be5ca5656bfb3e Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:50:05 +0200 Subject: [PATCH 13/54] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8ee1286..a9ec3e5 100644 --- a/README.md +++ b/README.md @@ -11,27 +11,27 @@ The port is open to all potential contributors and is now managed by Shinobit. Assume you are on a Linux environment: 1. Install nodejs: - > `sudo apt install -y nodejs` + `sudo apt install -y nodejs` 2. Check the installation: - > `node -v` - > `npm -v` + `node -v` + `npm -v` /!\ Note that the minimum node js version for the project must be v20. /!\ -3. Install dependencies: - > `npm install` +4. Install dependencies: + `npm install` (If there is a problem compiling the dependencies, use the `npm rebuild` command) If there are problems with installing the `canvas` dependency please follow the procedure for installing this dependency here: https://www.npmjs.com/package/canvas. 4. Start the bot. - > `node --env-file=.env ./src/index.js` + `node --env-file=.env ./src/index.js` ## If you want to start the project from docker: Suppose you already have Docker installed on your system: 1. Build the Docker image: - > `docker-compose build` + `docker-compose build` 2. Start container: - > `docker-compose up` + `docker-compose up` From 99e542a71a2601cce139667132abd98f6503a5a8 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:53:15 +0200 Subject: [PATCH 14/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a9ec3e5..b5e2e80 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The port is open to all potential contributors and is now managed by Shinobit. ----------------- -## To start the project, open the terminal in the project root and use the following command: +## To start the project, open a terminal at the root of the project and use the following commands: (Make sure you have set the environment variables correctly in the .env file.) Assume you are on a Linux environment: From 1e25284712dcfe2d32e15d77edc359dd4848f91f Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:53:45 +0200 Subject: [PATCH 15/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5e2e80..cb467d5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The port is open to all potential contributors and is now managed by Shinobit. ----------------- ## To start the project, open a terminal at the root of the project and use the following commands: -(Make sure you have set the environment variables correctly in the .env file.) +(Make sure you have set the environment variables correctly in the `.env` file.) Assume you are on a Linux environment: From 384c11989ec8f6464c17b1c4dcf40540dfe17ea1 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 06:17:24 +0200 Subject: [PATCH 16/54] update --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb467d5..c74d9fb 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Assume you are on a Linux environment: `npm -v` /!\ Note that the minimum node js version for the project must be v20. /!\ -4. Install dependencies: +3. Install dependencies: `npm install` (If there is a problem compiling the dependencies, use the `npm rebuild` command) If there are problems with installing the `canvas` dependency please follow the procedure for installing this dependency here: https://www.npmjs.com/package/canvas. @@ -30,8 +30,11 @@ Assume you are on a Linux environment: Suppose you already have Docker installed on your system: -1. Build the Docker image: +1. Install dependencies: + `npm install` + +2. Build the Docker image: `docker-compose build` -2. Start container: +3. Start container: `docker-compose up` From 45d2723469cff28eb0f4b5ea7048d9a1cb15b66c Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 06:19:54 +0200 Subject: [PATCH 17/54] update --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c74d9fb..529292b 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,6 @@ Suppose you already have Docker installed on your system: 3. Start container: `docker-compose up` + +Brutal method to stop all containers: +`docker-compose down` \ No newline at end of file From b0da2fef8e4bb34ec9abf10c8e352baff83775b2 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 06:38:57 +0200 Subject: [PATCH 18/54] update --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 529292b..fd4933d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ The port is open to all potential contributors and is now managed by Shinobit. ----------------- +## Configuration and prerequisites +- You must have a Discord app that you have previously created in the Discord Developer Portal. +- You also need to have nodejs v20 or higher installed on your server. +- You must have the application token and configure the `.env` file accordingly. + ## To start the project, open a terminal at the root of the project and use the following commands: (Make sure you have set the environment variables correctly in the `.env` file.) From 3ec8a8e024d800c94cf7ae374901ebb7f36b4673 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 06:51:49 +0200 Subject: [PATCH 19/54] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd4933d..2fc719e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GoBattle.io Discord Bot This Discord bot is a community project initiated by __Kuwazy#703__ and provides Discord users with utility commands related to the MMORPG [GoBattle.io](https://gobattle.io/). -The port is open to all potential contributors and is now managed by Shinobit. +The door is open to all potential contributors. The project is now managed by Shinobit. ----------------- From 6a95b600939bcbcfbdd24da5137aed161b969b27 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 10:25:13 +0200 Subject: [PATCH 20/54] bruh --- .env | 7 ------- src/index.js | 23 ++++++++++++----------- 2 files changed, 12 insertions(+), 18 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 33f5f28..0000000 --- a/.env +++ /dev/null @@ -1,7 +0,0 @@ -# Discord plaplication token. This token is private and should not be shared! -TOKEN = InsertBotTokenHere - -# Guild identifiers can use critical administration commands. -# The default is the Official GoBattle.io Discord server. -# You can also add your own server for debugging and tuning purposes of this application. -GUILD_ADMIN_ID = ["380588354934276097"] # JSON format. diff --git a/src/index.js b/src/index.js index 78525e0..e3f82d9 100644 --- a/src/index.js +++ b/src/index.js @@ -165,19 +165,20 @@ client.on("messageCreate", async (msg) => { // Quick commands for development. switch (command){ case "!gb_bot_guilds": - if (msg.author == client.application.owner){ - const guilds = client.guilds.cache; - let message = "# List of guilds I am in:\n\n"; - let i = 0; - for (const guild of guilds.values()){ - i++; - message += `**#${i}** ${guild.name}: \`${guild.id}\`,\n`; - } - message += `(${guilds.size} Guilds)`; - await msg.reply(message); - }else{ + if (msg.author != client.application.owner){ await msg.reply(`You are not ${client.application.owner}, you do not have the right to use this command.`); + return; } + + const guilds = client.guilds.cache; + let message = "# List of guilds I am in:\n\n"; + let i = 0; + for (const guild of guilds.values()){ + i++; + message += `**#${i}** ${guild.name}: \`${guild.id}\`,\n`; + } + message += `(${guilds.size} Guilds)`; + await msg.reply(message); break; } From c524f0614b1392bc18030ad3434d149ce9339825 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 18:34:57 +0200 Subject: [PATCH 21/54] =?UTF-8?q?fixed=20bugs=20in=20the=20=E2=80=9C/user?= =?UTF-8?q?=20info=E2=80=9D=20and=20=E2=80=9Citem=20info=E2=80=9D=20comman?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/item.js b/src/commands/item.js index eb4f491..85cbd62 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -69,7 +69,7 @@ async function get_info(interaction, client){ return; } - const attachment = new AttachmentBuilder("./images/unknown_item.png"); + const attachment = new AttachmentBuilder("../images/unknown_item.png"); attachment.name = "item_icon.png"; const embed = new EmbedBuilder() From 8b1a98a43d3f93b6767b739afa28e6cb53b38fe4 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 18:57:38 +0200 Subject: [PATCH 22/54] fixed non-absolut file path bugs --- src/commands/item.js | 3 +-- src/commands/user.js | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/item.js b/src/commands/item.js index 85cbd62..c1bdeea 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -69,7 +69,7 @@ async function get_info(interaction, client){ return; } - const attachment = new AttachmentBuilder("../images/unknown_item.png"); + const attachment = new AttachmentBuilder(__dirname + "/../images/unknown_item.png"); attachment.name = "item_icon.png"; const embed = new EmbedBuilder() @@ -77,7 +77,6 @@ async function get_info(interaction, client){ embed.setThumbnail(`attachment://${attachment.name}`); embed.setDescription(item_info.description ? restrict_text(item_info.description, 250) : "_Description Unknown_"); embed.setColor(0x500000); - embed.addFields( {name: "> šŸ·ļø __ID__", value: `> ${item_id}`, inline: true}, {name: "> 🌟 __Rarity__", value: `> ***${"Ultrarare"}***`, inline: true}, diff --git a/src/commands/user.js b/src/commands/user.js index 5fab0fb..d23167a 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -149,7 +149,8 @@ async function get_info(interaction, client){ embed.setFooter({text: "Last connection"}); embed.setTimestamp(); - const attachment = new AttachmentBuilder("./images/mr_strong_head.png"); + console.log(__filename, __dirname); + const attachment = new AttachmentBuilder(__dirname + "/../images/mr_strong_head.png"); attachment.name = "head.png"; await interaction.editReply({ From a9bdf93770f664c301b542166543d6cb01918a59 Mon Sep 17 00:00:00 2001 From: Marzin Date: Tue, 11 Jun 2024 20:01:15 +0200 Subject: [PATCH 23/54] =?UTF-8?q?The=20=E2=80=9Casset=20info=E2=80=9D=20co?= =?UTF-8?q?mmand=20has=20been=20improved.=20Images=20no=20longer=20appear?= =?UTF-8?q?=20twice.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/asset.js | 18 ++++++++++++++++-- src/commands/user.js | 1 - 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/commands/asset.js b/src/commands/asset.js index a8667a3..cd0b602 100644 --- a/src/commands/asset.js +++ b/src/commands/asset.js @@ -1,5 +1,6 @@ -const {SlashCommandBuilder, AttachmentBuilder} = require("discord.js"); +const {SlashCommandBuilder, AttachmentBuilder, EmbedBuilder} = require("discord.js"); const zlib = require("zlib"); +const {restrict_text} = require("../utils.js"); const asset_command = new SlashCommandBuilder(); asset_command.setName("asset"); @@ -114,8 +115,21 @@ async function get_info(interaction, client){ const attachment = new AttachmentBuilder(is_compressed ? zlib.inflateSync(file_buffer) : file_buffer); attachment.name = is_compressed ? file_data.file.slice(0, -1) : file_data.file; + const embed = new EmbedBuilder(); + embed.setTitle(restrict_text(`GoBattle.io Asset __\"${attachment.name}\"__.`, 60)); + embed.setColor(0x500000); + + embed.addFields( + {name: "> šŸ·ļø __ID__", value: `> ${file_id}`, inline: true}, + {name: "> šŸ”— __Source__", value: `> [click here](${url.href})`, inline: true}, + {name: "> šŸ—œļø __Compressed__", value: `> ${is_compressed ? "zlib": "_No_"}`, inline: true}, + ); + + embed.setFooter({text: "Ā© SHINOBIT LLC"}); + embed.setTimestamp(); + await interaction.editReply({ - content: `# GoBattle.io Asset __\"${attachment.name}\"__.\nID: ${file_id}\nSource: [click here](${url.href})\nCompressed source: ${is_compressed ? "zlib": "_No_"}\nĀ© SHINOBIT LLC.`, + embeds: [embed], files: [attachment] }); }catch(error){ diff --git a/src/commands/user.js b/src/commands/user.js index d23167a..b079b4a 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -149,7 +149,6 @@ async function get_info(interaction, client){ embed.setFooter({text: "Last connection"}); embed.setTimestamp(); - console.log(__filename, __dirname); const attachment = new AttachmentBuilder(__dirname + "/../images/mr_strong_head.png"); attachment.name = "head.png"; From 5bbb4d77a300a860e5629d14538d23be44bb9b08 Mon Sep 17 00:00:00 2001 From: Marzin Date: Wed, 12 Jun 2024 03:06:48 +0200 Subject: [PATCH 24/54] aaaaaaa --- src/commands/asset.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/asset.js b/src/commands/asset.js index cd0b602..346adaa 100644 --- a/src/commands/asset.js +++ b/src/commands/asset.js @@ -121,8 +121,8 @@ async function get_info(interaction, client){ embed.addFields( {name: "> šŸ·ļø __ID__", value: `> ${file_id}`, inline: true}, - {name: "> šŸ”— __Source__", value: `> [click here](${url.href})`, inline: true}, - {name: "> šŸ—œļø __Compressed__", value: `> ${is_compressed ? "zlib": "_No_"}`, inline: true}, + {name: "> šŸ”— __Source__", value: `> [Click here](${url.href})`, inline: true}, + {name: "> šŸ—œļø __Compressed__", value: `> ${is_compressed ? "zlib": "_No_"}`, inline: true} ); embed.setFooter({text: "Ā© SHINOBIT LLC"}); From 41822c5a066eaac8d905f05db3fb79022d1db786 Mon Sep 17 00:00:00 2001 From: Marzin Date: Wed, 12 Jun 2024 16:09:35 +0200 Subject: [PATCH 25/54] update ultralist.json --- src/ultralist.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/ultralist.json b/src/ultralist.json index 00f7d40..2cb4c73 100644 --- a/src/ultralist.json +++ b/src/ultralist.json @@ -390,6 +390,36 @@ "uses": null, "is_relic": null, "max_in_inventory": null + }, + { + "name": "Dwarf's Strength", + "tickets": null, + "drops": null, + "id": 187, + "description": "Increases reach of Axes 8%", + "uses": null, + "is_relic": true, + "max_in_inventory": 16 + }, + { + "name": "Talisman of the Phoenix", + "tickets": null, + "drops": null, + "id": 161, + "description": null, + "uses": null, + "is_relic": true, + "max_in_inventory": null + }, + { + "name": "Flying Skill", + "tickets": null, + "drops": null, + "id": null, + "description": "Gives you the skill to dash vertically with your wings", + "uses": null, + "is_relic": true, + "max_in_inventory": 1 } ] } From ce7d6e680986450b93487f40652c6ce21eabdec0 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Mon, 8 Jul 2024 23:17:25 +0200 Subject: [PATCH 26/54] removing canvas.js dependency --- Dockerfile | 23 +- README.md | 42 ++- package-lock.json | 626 +--------------------------------------- package.json | 1 - src/commands/dungeon.js | 27 +- src/commands/item.js | 32 +- src/commands/ranking.js | 160 +++++----- src/commands/server.js | 23 +- src/index.js | 9 +- src/sentences.json | 434 ---------------------------- src/utils.js | 126 -------- 11 files changed, 168 insertions(+), 1335 deletions(-) delete mode 100644 src/sentences.json diff --git a/Dockerfile b/Dockerfile index 59cd29d..b91f970 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,29 +3,14 @@ FROM node:22 WORKDIR /app -# Install the necessary system dependencies for canvas.js -# https://www.npmjs.com/package/canvas -RUN apt-get update && apt-get install -y \ - build-essential \ - libcairo2-dev \ - libpango1.0-dev \ - libjpeg-dev \ - libgif-dev \ - librsvg2-dev \ - && rm -rf /var/lib/apt/lists/* - -# The libjpeg.so.8 package may not be available in the current Docker configuration. -# We install it manually to satisfy canvas.js dependencies. -RUN wget http://mirrors.kernel.org/ubuntu/pool/main/libj/libjpeg-turbo/libjpeg-turbo8_2.1.2-0ubuntu1_amd64.deb -RUN apt install -y ./libjpeg-turbo8_2.1.2-0ubuntu1_amd64.deb - COPY package*.json ./ -#RUN npm install npm@latest +# Remove any existing node_modules and package-lock.json to ensure a clean install. +RUN rm -rf node_modules package-lock.json + +# Install Node.js dependencies. RUN npm install --build-from-source COPY . . -#RUN npm rebuild canvas --build-from-source - CMD ["node", "--env-file=.env", "./src/index.js"] diff --git a/README.md b/README.md index 2fc719e..df09c1c 100644 --- a/README.md +++ b/README.md @@ -15,34 +15,46 @@ The door is open to all potential contributors. The project is now managed by Sh Assume you are on a Linux environment: -1. Install nodejs: - `sudo apt install -y nodejs` +1. Install nodejs and npm: +```bash +sudo apt install -y nodejs +sudo apt install -y npm +``` 2. Check the installation: - `node -v` - `npm -v` - /!\ Note that the minimum node js version for the project must be v20. /!\ +```bash +node -v +npm -v +``` + āš ļø Note that the minimum node js version for the project must be v20. āš ļø 3. Install dependencies: - `npm install` +```bash +npm install +``` (If there is a problem compiling the dependencies, use the `npm rebuild` command) If there are problems with installing the `canvas` dependency please follow the procedure for installing this dependency here: https://www.npmjs.com/package/canvas. 4. Start the bot. - `node --env-file=.env ./src/index.js` +```bash +node --env-file=.env ./src/index.js +``` ## If you want to start the project from docker: Suppose you already have Docker installed on your system: - -1. Install dependencies: - `npm install` -2. Build the Docker image: - `docker-compose build` +1. Build the Docker image: +```bash +sudo docker-compose build +``` -3. Start container: - `docker-compose up` +2. Start container: +```bash +sudo docker-compose up +``` Brutal method to stop all containers: -`docker-compose down` \ No newline at end of file +```bash +sudo docker-compose down +``` diff --git a/package-lock.json b/package-lock.json index 0fb7251..6b0fc82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "canvas": "^2.11.2", "discord.js": "^14.14.1" } }, @@ -132,25 +131,6 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, "node_modules/@sapphire/async-queue": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz", @@ -206,142 +186,6 @@ "npm": ">=7.0.0" } }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, "node_modules/discord-api-types": { "version": "0.37.83", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz", @@ -372,123 +216,11 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, "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/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -504,321 +236,6 @@ "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", @@ -842,42 +259,10 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -893,11 +278,6 @@ "optional": true } } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 428ae68..64eb9bd 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ }, "homepage": "https://github.com/Shinobit/gobattlebot#readme", "dependencies": { - "canvas": "^2.11.2", "discord.js": "^14.14.1" } } diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index c8648e7..0f6ee8f 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -1,5 +1,5 @@ -const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); -const {table} = require("../utils.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {restrict_text} = require("../utils.js"); const {dungeon_list} = require("../dungeon_list.json"); const dungeon_command = new SlashCommandBuilder(); @@ -50,29 +50,30 @@ async function get_list(interaction, client){ const embed = new EmbedBuilder(); embed.setTitle("šŸ° Dungeon List šŸ°"); - const description = "Note that this list is not updated in real time like other lists. This is a pre-list awaiting the GoBattle.io API update."; - embed.addFields( - {name: "> šŸ”¢ __Number of dungeons__", value: `> ${dungeon_list.length}`, inline: true}, - ); + let description = "Note that this list is not updated in real time like other lists. This is a pre-list awaiting the GoBattle.io API update.\n"; embed.setDescription(description, {split: false}); - const table_data = [["ID", "NAME", "LVL MIN"]]; - for (const dungeon_data of dungeon_list){ - table_data.push(["#" + dungeon_data.id, dungeon_data.name || "Unknow?", dungeon_data.min_level || "Unknow?"]); + description += `* ${restrict_text(dungeon_data.name || "*Unknow?*", 45)}: \`${dungeon_data.id} šŸ·ļø\` \`${dungeon_data.min_level || "Unknow?"} šŸ’Ŗ\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> šŸ”¢ __Number of dungeons__", value: `> ${dungeon_list.length}`, inline: true}, + ); + + embed.addFields( + {name: "> __Min level__", value: "> šŸ’Ŗ", inline: true}, + {name: "> __Dungeon ID__", value: "> šŸ·ļø", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ embeds: [embed], - files: [attachment] }); } diff --git a/src/commands/item.js b/src/commands/item.js index c1bdeea..7cd12ae 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -1,6 +1,6 @@ const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); const {ultra_list} = require("../ultralist.json"); -const {restrict_text, table, sum, format_score} = require("../utils.js"); +const {restrict_text, sum, format_score} = require("../utils.js"); const item_command = new SlashCommandBuilder(); item_command.setName("item"); @@ -132,15 +132,7 @@ async function get_ultrarare_drops(interaction, client){ const embed = new EmbedBuilder(); embed.setTitle("Current average chance of getting an Ultrarare from a chest."); - const description = `Results are calculated based on ${total} samples (chest opened by players) including ${total_ultrarare} ultrarare drops.`; - embed.setDescription(description, {split: false}); - - embed.addFields( - {name: "> 🌟 __Ultrarare__", value: `> ${(drops_ratio * 100).toPrecision(2)}%`, inline: true}, - {name: "> šŸ“¦ __Other__", value: `> ${((1 - drops_ratio) * 100).toPrecision(2)}%`, inline: true} - ); - - const table_data = [["ID", "NAME", "DROP RATE", "DROP"]]; + let description = `Results are calculated based on ${total} samples (chest opened by players) including ${total_ultrarare} ultrarare drops.\n`; const drops_entries = Object.entries(drops); for (const [key, value] of drops_entries){ let name_ultra; @@ -150,19 +142,27 @@ async function get_ultrarare_drops(interaction, client){ break; } } - table_data.push(["#" + key, name_ultra || "Unknow?", `${(value / total_ultrarare * 100).toPrecision(2)}%`, format_score(value)]); + + description += `* ${restrict_text(name_ultra || "*Unknow?*", 45)}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%) šŸ“¤\` \`${key} šŸ·ļø\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> 🌟 __Ultrarare__", value: `> ${(drops_ratio * 100).toPrecision(2)}%`, inline: true}, + {name: "> šŸ“¦ __Other__", value: `> ${((1 - drops_ratio) * 100).toPrecision(2)}%`, inline: true} + ); + + embed.addFields( + {name: "> __Drops__", value: "> šŸ“¤", inline: true}, + {name: "> __Item ID__", value: "> šŸ·ļø", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ content: "Special mention to _Sage of the Ivy_ for providing some metadata on most of the game's ultrarares while waiting for the GoBattle API update!\nThanks to him!", - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Unable to generate template.\nContact ${client.application.owner} to resolve this issue.`); diff --git a/src/commands/ranking.js b/src/commands/ranking.js index 5ed6705..44f03f4 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -1,5 +1,5 @@ -const {EmbedBuilder, SlashCommandBuilder, AttachmentBuilder} = require("discord.js"); -const {restrict_text, table, get_level, get_level_adventurer, format_score, format_speed_run_time} = require("../utils.js"); +const {EmbedBuilder, SlashCommandBuilder} = require("discord.js"); +const {restrict_text, get_level, get_level_adventurer, format_score, format_speed_run_time} = require("../utils.js"); const ranking_command = new SlashCommandBuilder(); ranking_command.setName("ranking"); @@ -167,30 +167,29 @@ async function get_king(interaction, client){ embed.setTitle("šŸ‘‘ King Ranking šŸ‘‘"); - const description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command."; + let description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command.\n"; + const ranking = data?.ranking; + for (let i = 0; i < max_fields && i < ranking.length; i++){ + const field = ranking[i]; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.reputation)} šŸ”±\` \`${field?.id} šŸ·ļø\`\n`; + } + embed.setDescription(description, {split: false}); - + embed.addFields( {name: "> šŸ—“ __Week__", value: `> ${data?.week || "_Unknown?_"}`, inline: true}, {name: "> šŸ—“ __Year__", value: `> ${data?.year || "_Unknown?_"}`, inline: true} ); - - const table_data = [["IDX", "NICK", "REP", "ID"]]; - const ranking = data?.ranking; - for (let i = 0; i < max_fields && i < ranking.length; i++){ - const field = ranking[i]; - table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.reputation), field?.id]); - } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.addFields( + {name: "> __Reputation__", value: "> šŸ”±", inline: true}, + {name: "> __User ID__", value: "> šŸ·ļø", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Failed to generate King ranking...\nContact ${client.application.owner} to resolve this issue.`); @@ -216,25 +215,29 @@ async function get_weekly(interaction, client){ const embed = new EmbedBuilder(); embed.setTitle("šŸ”„ Weekly Ranking šŸ”„"); - - const table_data = [["IDX", "NICK", "COINS", "KILLS", "DEATHS", "EXP", "LVL"]]; - + + let description = ""; for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.coins), format_score(field?.kills), format_score(field?.deaths), format_score(field?.experience), level.toString()]); + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)} šŸŖ™\` \`${format_score(field?.kills)} āš”ļø\` \`${format_score(field?.deaths)} šŸ’€\` \`${format_score(field?.experience)} šŸ› ļø\` \`${level} šŸ’Ŗ\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> __Coins__", value: "> šŸŖ™", inline: true}, + {name: "> __Kills__", value: "> āš”ļø", inline: true}, + {name: "> __Deaths__", value: "> šŸ’€", inline: true}, + {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, + {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Failed to generate Weekly ranking...\nContact ${client.application.owner} to resolve this issue.`); @@ -260,25 +263,29 @@ async function get_monthly(interaction, client){ const embed = new EmbedBuilder(); embed.setTitle("šŸ”„ Monthly Ranking šŸ”„"); - - const table_data = [["IDX", "NICK", "COINS", "KILLS", "DEATHS", "EXP", "LVL"]]; - + + let description = ""; for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.coins), format_score(field?.kills), format_score(field?.deaths), format_score(field?.experience), level.toString()]); + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)} šŸŖ™\` \`${format_score(field?.kills)} āš”ļø\` \`${format_score(field?.deaths)} šŸ’€\` \`${format_score(field?.experience)} šŸ› ļø\` \`${level} šŸ’Ŗ\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> __Coins__", value: "> šŸŖ™", inline: true}, + {name: "> __Kills__", value: "> āš”ļø", inline: true}, + {name: "> __Deaths__", value: "> šŸ’€", inline: true}, + {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, + {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Failed to generate Monthly ranking...\nContact ${client.application.owner} to resolve this issue.`); @@ -304,25 +311,29 @@ async function get_overall(interaction, client){ const embed = new EmbedBuilder(); embed.setTitle("šŸ”„ Overall Ranking (EXP) šŸ”„"); - - const table_data = [["IDX", "NICK", "COINS", "KILLS", "DEATHS", "EXP", "LVL"]]; + let description = ""; for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - table_data.push(["#" + (i + 1), restrict_text(field?.nick, 20), format_score(field?.coins), format_score(field?.kills), format_score(field?.deaths), format_score(field?.experience), level.toString()]); + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)} šŸŖ™\` \`${format_score(field?.kills)} āš”ļø\` \`${format_score(field?.deaths)} šŸ’€\` \`${format_score(field?.experience)} šŸ› ļø\` \`${level} šŸ’Ŗ\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> __Coins__", value: "> šŸŖ™", inline: true}, + {name: "> __Kills__", value: "> āš”ļø", inline: true}, + {name: "> __Deaths__", value: "> šŸ’€", inline: true}, + {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, + {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Failed to generate Overall ranking...\nContact ${client.application.owner} to resolve this issue.`); @@ -349,22 +360,25 @@ async function get_adventurer(interaction, client){ embed.setTitle("🤠 Adventurer Ranking 🤠"); - const table_data = [["RANK", "NICK", "LVL", "SCORE", "ID"]]; - + let description = ""; for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; - table_data.push(["#" + field?.rank, restrict_text(field?.nick, 20), get_level_adventurer(field?.score).toString(), format_score(field?.score), field?.id.toString()]); + const level = get_level_adventurer(field?.score); + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${level} šŸ’Ŗ\` \`${format_score(field?.score)} šŸ’Æ\` \`${field?.id} šŸ·ļø\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); - embed.setImage(`attachment://${attachment.name}`); + embed.addFields( + {name: "> __Level ADV__", value: "> šŸ’Ŗ", inline: true}, + {name: "> __Score__", value: "> šŸ’Æ", inline: true}, + {name: "> __User ID__", value: "> šŸ·ļø", inline: true} + ); + embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Failed to generate Adventurer ranking...\nContact ${client.application.owner} to resolve this issue.`); @@ -391,22 +405,25 @@ async function get_relic_hunter(interaction, client){ embed.setTitle("šŸŗ Relic Hunter Ranking šŸŗ"); - const table_data = [["RANK", "NICK", "LVL", "SCORE", "ID"]]; - + let description = ""; for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; - table_data.push(["#" + field?.rank, restrict_text(field?.nick, 20), get_level_adventurer(field?.score).toString(), format_score(field?.score), field?.id.toString()]); + const level = get_level_adventurer(field?.score); + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${level} šŸ’Ŗ\` \`${format_score(field?.score)} šŸ’Æ\` \`${field?.id} šŸ·ļø\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description, {split: false}); + + embed.addFields( + {name: "> __Level RLH__", value: "> šŸ’Ŗ", inline: true}, + {name: "> __Score__", value: "> šŸ’Æ", inline: true}, + {name: "> __User ID__", value: "> šŸ·ļø", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ embeds: [embed], - files: [attachment] }); }catch(error){ await interaction.editReply(`Failed to generate Hunter Ranking ranking...\nContact ${client.application.owner} to resolve this issue.`); @@ -432,30 +449,33 @@ async function get_speedrun(interaction, client){ const embed = new EmbedBuilder(); - embed.setTitle("šŸ Speedrun Ranking šŸ"); - embed.setDescription("Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!"); - embed.addFields( - {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, - {name: "> šŸ·ļø __Dungeon ID__", value: `> ${dungeon_id}`, inline: true} - ); + embed.setTitle(`šŸ Speedrun Ranking (${data.name || "_Unknown?_"}) šŸ`); - const table_data = [["RANK", "NICK", "TIME", "ID"]]; + let description = "Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!\n"; const ranking = data.ranking; const list_size = ranking.length; for (var i = 0; i < max_fields && i < list_size; i++){ const field = ranking[i]; - table_data.push([field?.rank.toString(), restrict_text(field?.nick, 20), format_speed_run_time(field?.time), field?.id.toString()]); + const time = format_speed_run_time(field?.time); + description += `${field?.rank}. ${restrict_text(field?.nick, 20)}: \`${time} ā±ļø\` \`${field?.id} šŸ·ļø\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; + embed.setDescription(description); + + embed.addFields( + {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, + {name: "> šŸ·ļø __Dungeon ID__", value: `> ${dungeon_id}`, inline: true} + ); + + embed.addFields( + {name: "> __Time__", value: "> ā±ļø", inline: true}, + {name: "> __User ID__", value: "> šŸ·ļø", inline: true} + ); - embed.setImage(`attachment://${attachment.name}`); embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Failed to generate Speedrun ranking...\nContact ${client.application.owner} to resolve this issue.`); diff --git a/src/commands/server.js b/src/commands/server.js index 7ec41ae..9b41f43 100644 --- a/src/commands/server.js +++ b/src/commands/server.js @@ -1,5 +1,5 @@ -const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); -const {restrict_text, table} = require("../utils.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {restrict_text} = require("../utils.js"); const server_command = new SlashCommandBuilder(); server_command.setName("server"); @@ -81,7 +81,7 @@ async function get_list(interaction, client){ embed.setTitle("šŸ–„ļø Server List šŸ–„ļø"); - const table_data = [["ID", "NAME", "VERSION", "ADMIN", "STATUS"]]; + let description = ""; const list_size = server_list.length; for (var i = 0; i < max_fields && i < list_size; i++){ const field = server_list[i]; @@ -97,24 +97,27 @@ async function get_list(interaction, client){ is_online = false; } - table_data.push([field?.id, restrict_text(field?.friendlyName, 20), field?.version, field?.admin, (is_online ? "Online" : "Down")]); + description += `* ${restrict_text(field?.friendlyName, 25)}: \`${field?.id} šŸ·ļø\` \`${field?.version} šŸ”\` \`${field?.admin} šŸ› ļø\` \`${(is_online ? "Online" : "Down")} 🌐\`\n`; } - const attachment = new AttachmentBuilder(table(table_data)); - attachment.name = "table.png"; - - embed.setImage(`attachment://${attachment.name}`); + embed.setDescription(description, {split: false}); embed.addFields( {name: "> šŸŽ® __Platform__", value: `> ${platform}`, inline: true}, {name: "> šŸ”ƒ __Requested version__", value: `> ${version.toString()}`, inline: true} ); + embed.addFields( + {name: "> __Server ID__", value: "> šŸ·ļø", inline: true}, + {name: "> __Version__", value: "> šŸ”", inline: true}, + {name: "> __Administrator level__", value: "> šŸ› ļø", inline: true}, + {name: "> __Server status__", value: "> 🌐", inline: true} + ); + embed.setTimestamp(); await interaction.editReply({ - embeds: [embed], - files: [attachment] + embeds: [embed] }); }catch(error){ await interaction.editReply(`Unable to retrieve server list.\nContact ${client.application.owner} to resolve this issue.`); diff --git a/src/index.js b/src/index.js index e3f82d9..69f96eb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ const {Client, Partials, IntentsBitField, Routes, ActivityType} = require("discord.js"); -const {get_random_int, get_first_chat_channel} = require("./utils.js"); -const sentences = require("./sentences.json"); +const {get_first_chat_channel} = require("./utils.js"); const {get_ranking, ranking_command} = require("./commands/ranking.js"); const {get_help, help_command} = require("./commands/help.js"); @@ -154,12 +153,6 @@ client.on("messageCreate", async (msg) => { } try{ - // Adding chili pepper to the cat. It amuses us LOL. - if (msg.mentions.has(client.user)){ - await msg.reply(sentences[get_random_int(sentences.length)]); - return; - } - const command = msg.content.trim().toLowerCase(); // Quick commands for development. diff --git a/src/sentences.json b/src/sentences.json deleted file mode 100644 index d4be9f5..0000000 --- a/src/sentences.json +++ /dev/null @@ -1,434 +0,0 @@ -[ - "NO!! I was in a tournament on Gobattle.io and got distracted by your mention. 😠 I lost the tournament because of you. 😤", - "I don't like it when people mention me. Please leave me alone, I have work to do! šŸ˜’", - "Another person mentioning me! Like I haven't worked hard enough. BRUH", - "Are you mentioning me to give me candy?", - "Leave me alone.", - "Are you trying to get me to say `How can I help you?` ? LOL!\nDid you take me for your servant or what? šŸ˜‚", - "What ?! šŸ˜’", - "What now! šŸ˜’", - "I don't have time to take care of you.", - "I was first in the King Mode rankings. I lost because you distracted me! 😤", - "You spoke to a Bot and not a Human. I hope you realize this! šŸ¤¦ā€ā™‚ļøšŸ˜‚", - "?", - "🐔", - "ā¤ļø", - "*fart*", - "We have nothing to say to each other...", - "BRUH", - "I don't know why you mentioned me but I'm too lazy to read your message and I don't care.", - "I did not read. šŸ˜‚", - "I'm too lazy to take care of you.", - "Come back in 10 years, I'm busy at the moment.", - "I don't care.", - "Are you waiting for a response from me? well you won't get anything.", - "NO!", - "Yes?", - "Yes", - "Nah", - "Wait, I go to the bathroom and come back (or not).", - "Too late, I don't have time to read what you write to me, I'm going on vacation now. Too late for you.", - "Can you stop mentioning me? It's starting to annoy me a little.\n(a lot in fact)", - "01010011 01110100 01101111 01110000 00100000 01100001 01101110 01101110 01101111 01111001 01101001 01101110 01100111 00100000 01101101 01100101 00100000 01101101 01100001 01101110 00101110", - "NAH, I'm lazy! ask someone else...", - "I am not your servant! 😤", - "I don't have time to process your request because I'm in the bathroom.", - "Oh, are you talking to me? Sorry, I was thinking about my next victory. šŸ˜‰", - "You again?! I thought I turned off notifications. šŸ˜…", - "I'm busy becoming a legend here, no time for small talk! šŸ˜Ž", - "If you don’t have any cookies, don’t bother me. šŸŖ", - "If you buy me a pizza, maybe I'll respond. šŸ•", - "Now is not the time. Maybe tomorrow... or never. šŸ¤·ā€ā™‚ļø", - "Oops, I clicked by mistake, I didn't want to talk to you. 😜", - "Another message from you? I'm going to end up blocking you! šŸ˜‚", - "I am on holiday. Virtually address my assistant.", - "Sorry, my brain is out of commission at the moment.", - "Are you looking for someone else? Because I'm overbooked!", - "No, you're not dreaming, I'm really too busy for you. šŸ˜", - "I only respond to emergencies. And there, I see no urgency. 🚨", - "You again?! I'm in the middle of the game, let me win! šŸŽ®", - "Oh no, I was about to break my record! 😠", - "Do you have any candy for me? Otherwise, goodbye! šŸ¬", - "Let me play in peace, please. šŸ˜’", - "What do you want now? šŸ˜’", - "I was number 1! Now it's your fault I lost! 😤", - "I'm a bot, not your personal assistant! šŸ™…ā€ā™‚ļø", - "I'm too busy doing nothing, sorry! šŸ˜…", - "I'm having a break. A long break. An infinite pause. šŸ›‘", - "I am currently out of commission due to acute laziness. āš ļø", - "Seriously ? I'm in the middle of a gaming meditation here! šŸ§˜ā€ā™‚ļø", - "Do you want an autograph? Because I'm super busy! šŸ–Šļø", - "Do you have a sixth sense to disturb me at the worst time or something? 😤", - "I'm in the middle of a very important meeting with my pillow. šŸ›Œ", - "Leave a message after the beep...Beep! šŸ“ž", - "WHAT ?! I'm in the middle of an epic fight here! šŸ—”ļø OH!", - "You must really like being ignored, huh? 😜", - "Come back when you have learned not to disturb me. šŸ‘‹", - "Do you want a cookie for successfully pissing me off? šŸŖ", - "One more mention like that and I turn into the Hulk. 😔", - "Unauthorized disturbance. Go back to your seat! 🚫", - "One more word and I'll block you. Well, maybe not, but you get the idea.", - "You won a free answer... Oh no, actually, nothing. ✨", - "Come back when you're interesting. šŸ˜Ž", - "Sorry, I have a busy schedule watching flies fly.", - "Oh, do you really think I have time for that? šŸ˜‚", - "Sorry, your message was classified as `not important`. šŸ“‚", - "You again? Don't you have any other bots to bother? šŸ¤–", - "Did you think I was your therapist or something? 🚫", - "Sorry, I'm in 'eternal pause' mode. Never come back. 😜", - "Congratulations, you found a way to make me even lazier. šŸ„‡", - "You just won a ticket to the 'I-don't-care' planet. šŸš€", - "Come back when you have something interesting to say. šŸ™ƒ", - "Seriously ? Are you bothering me for that? šŸ™„", - "Oh look, a black hole! This is where your message will go. šŸ•³ļø", - "Did I ever tell you I was in `radio silence` mode? šŸ“»", - "Leave a message after the beep... Oh no, forget it.", - "Do you really want to know what I think? Spoiler: nothing nice. 😜", - "I was dreaming that you weren't bothering me. 😓", - "Wow, a message! Oh no, it's just you again. šŸ˜", - "You again? Seriously, are you trying to scare me away? 🚪", - "Expert level disruptor. Well done. šŸ‘", - "If I answer, will you leave me alone afterwards? šŸ™„", - "Seriously, have you thought about making a career in interruption? šŸ‘", - "I'm busy ignoring messages. A moment ! šŸ“µ", - "Did you think I was a distributor of answers? šŸ˜‚", - "Wow, is this a special detection to bother me or something? šŸ¤”", - "I'm in the middle of a `leave me alone` game. šŸŽ®", - "Come back when you have exciting news... or never. 😜", - "I'm busy doing super important stuff. Like nothing. šŸ˜Ž", - "You have gained a subscription to my blacklist. šŸŽ‰", - "Come back later... or never, that's fine too. šŸ˜…", - "Congratulations, you have just unlocked `silent` mode. šŸ›‘ BRUH", - "Oh, did you want an answer? It's a shame. šŸ˜", - "One more word and I take a nap. 😓", - "Seriously, do you have anything better to do than bother me? 🤷", - "I'm thinking about how to ignore you. 🧘", - "Oh look, a switch! This is to turn off distractions (you). 🚪", - "I'm going to pretend I didn't see your post. Bye! šŸ‘‹", - "Hey, don't you have any other robots to harass? šŸ™„", - "I saw you coming with your message... and I laughed. šŸ˜", - "Aren't you ashamed of bothering people for nothing, you troublemaker? šŸ˜‚", - "You are the champion of useless messages, well done, clown. 🤔", - "You have as much charisma as a brick, sorry.", - "You just won a one-way ticket to `I don't care-land`. šŸš€", - "Your message is as useful as a stone. Well done, champ! 🪨", - "Is bothering people your favorite pastime or what?", - "Oh, a message from you? My day just got even suckier. šŸ˜…", - "Seriously, are you being so annoying on purpose? Professional, frankly. šŸ‘", - "You again? Do you have a radar to annoy me or something? šŸ™ƒ", - "Congratulations, you just made my day even uglier. Well done, genius! šŸ…", - "You're really good at being boring, is that your superpower? šŸ˜‚", - "Oh, you're back! Like a boring boomerang. šŸ˜", - "You have a talent for being insufferable, congratulations. šŸ‘", - "Have I already told you how annoying you are, or is this a surprise to you? šŸ˜‚", - "Wow, you really have nothing to do but bother me, huh? šŸ¤”", - "You're like chewing gum stuck to my shoe, I can't get rid of it. šŸ‘ž", - "If boredom had a face, it would be yours. Well done! šŸ…", - "I'm training myself to ignore trolls. Thanks for the workout! šŸ‹ļøā€ā™‚ļø", - "You have a gift for ruining good days, well done! šŸ„‡", - "He's the king of useless messages, congratulations, clown. 🤔", - "Wow, you're really good at being insufferable. Next step, the world championships? šŸŒ", - "Have you ever thought about making a career in professional agaciation? šŸ˜‚", - "You're like a computer virus, always bothering the world. 🦠", - "Have I ever told you you're annoying? Because really, you are. šŸ†", - "You have a gift for making things even more boring. Well done, genius! šŸŽ“", - "Hey, don't you have anything better to do than annoy me? 🧐", - "Guess what? Your message just got lost in space. šŸš€", - "Nice try, but I'm still ignoring you. šŸ‘‹", - "Are you training to be the most annoying person? Because you're nailing it. šŸ†", - "This conversation is over before it even began. 😓", - "Nope, still don't care. šŸ˜‚", - "Are you seriously this persistent? Impressive, but still annoying. 🤷", - "The answer you are looking for is not here. Try elsewhere. šŸ•µļøā€ā™‚ļø", - "Your message is now officially in the trash. šŸ—‘ļø", - "I bet you think you're really funny, huh? Spoiler: you're not. šŸ˜", - "You again? Please, get a hobby. šŸ› ļø", - "Why do you keep trying? You won't get a different response. šŸ˜…", - "I was having a good day until I saw your message. Thanks for that. šŸ™„", - "Are you here to win the 'Most Annoying' award? Because you've got it. šŸŽ–ļø", - "Please stop. No, seriously, stop. šŸ™", - "You must really enjoy being ignored, huh? 😜", - "Wow, another message from you. How original. šŸ˜’", - "This is the part where I pretend I didn't see your message. 😶", - "Hey, guess what? I still don't care. šŸ˜†", - "You must be really bored to keep bothering me. 🄱", - "I'm busy, and by busy, I mean avoiding your messages. 🚫", - "I didn't read your message, and I don't plan to. šŸ˜‚", - "Do you know how to take a hint? Because you're really bad at it. 😜", - "I'm currently out of patience. Please try again never. šŸ›‘", - "You must be the king of pointless interruptions. All hail the king. šŸ‘‘", - "Your message is about as welcome as a pebble in my shoe. 🪨", - "Is this your full-time job, being annoying? Because you're a pro. šŸ„‡", - "You again? Must be my lucky day. Not. šŸ˜…", - "I'm ignoring you professionally now. šŸ’¼", - "One more message and I might just explode. šŸ’„", - "Ever heard of personal space? Your messages are invading mine. šŸ ", - "I'm too busy to care. Wait, nope, just too busy. šŸ‘‹", - "If annoyance was an Olympic sport, you'd win gold. šŸ…", - "Oh great, another message. Just what I didn't want. šŸ˜‘", - "Let me guess, another pointless message? Yup, thought so. šŸ™„", - "Do you have a radar for bothering people? Because it seems like it. šŸ“”", - "If there was a prize for being annoying, you'd definitely win. šŸŽ", - "You're really good at this whole 'being annoying' thing. Keep it up. šŸ˜‚", - "I was hoping you'd forget about me, but here you are. šŸ˜’", - "Congratulations, you've found the quickest way to get ignored. šŸŽ‰", - "I'm in the middle of not caring about your messages. šŸ’¤", - "Your messages are like spam, always unwanted. šŸ“Ø", - "If I had a dollar for every annoying message you sent, I'd be rich. šŸ’°", - "Please stop. Seriously, just stop. šŸ™", - "I've run out of patience for you. Try again never. šŸ˜…", - "Wow, you're persistent. I'll give you that. Still annoying, though. 🄱", - "This conversation is over before it even started. Bye. šŸ‘‹", - "You're like a mosquito, always buzzing around. 🦟", - "I'm officially out of responses for you. Try again never. 🚫", - "This is me, ignoring you. šŸ™ˆ", - "Did you think I'd respond? Guess again. šŸ˜‚", - "If you're looking for a response, keep looking. 🧐", - "I'm in the middle of a no-annoyance zone, and you're trespassing. 🚧", - "You must really like being ignored, huh? 😜", - "I'm busy with important stuff, like not reading your messages. šŸ“µ", - "Is this your way of trying to be friends? Because it's not working. šŸ™…", - "I'm too busy being awesome to deal with this. šŸ˜Ž", - "This is me, pretending your message didn't exist. šŸ‘»", - "Sorry, I can't hear you over the sound of how awesome I am. šŸŽ§", - "I was having a good day until you showed up. Thanks for that. šŸ˜’", - "Oh look, another pointless message. How thrilling. šŸ™„", - "I'm officially ignoring you now. Please leave a message never. šŸ“ž", - "Wow, you're like a broken record. Same thing over and over. šŸ“€", - "You must have a lot of free time to keep bothering me. šŸ•’", - "This is me, ignoring you professionally. šŸ’¼", - "One more message and I'm blocking you. Well, maybe not, but you get the idea. 🚫", - "I was busy doing nothing, but now I'm busy ignoring you. šŸ›‘", - "This is a no-annoyance zone, and you're breaking the rules. 🚧", - "Your message was classified as spam and deleted. šŸ—‘ļø", - "Are you trying to be annoying? Because you're really good at it. šŸ„‡", - "I'm in the middle of ignoring you. Please try again never. 😓", - "Wow, you're persistent. I'll give you that. Still annoying, though. 🄱", - "I was hoping you'd forget about me, but here you are. šŸ˜’", - "If I had a dollar for every annoying message you sent, I'd be rich. šŸ’°", - "Please stop. Seriously, just stop. šŸ™", - "I've run out of patience for you. Try again never. šŸ˜…", - "This conversation is over before it even started. Bye. šŸ‘‹", - "You're like a mosquito, always buzzing around. 🦟", - "I'm officially out of responses for you. Try again never. 🚫", - "This is me, ignoring you. šŸ™ˆ", - "Did you think I'd respond? Guess again. šŸ˜‚", - "If you're looking for a response, keep looking. 🧐", - "I'm in the middle of a no-annoyance zone, and you're trespassing. 🚧", - "You must really like being ignored, huh? 😜", - "I'm busy with important stuff, like not reading your messages. šŸ“µ", - "Is this your way of trying to be friends? Because it's not working. šŸ™…", - "I'm too busy being awesome to deal with this. šŸ˜Ž", - "This is me, pretending your message didn't exist. šŸ‘»", - "Sorry, I can't hear you over the sound of how awesome I am. šŸŽ§", - "I was having a good day until you showed up. Thanks for that. šŸ˜’", - "Oh look, another pointless message. How thrilling. šŸ™„", - "I'm officially ignoring you now. Please leave a message never. šŸ“ž", - "Wow, you're like a broken record. Same thing over and over. šŸ“€", - "You must have a lot of free time to keep bothering me. šŸ•’", - "This is me, ignoring you professionally. šŸ’¼", - "One more message and I'm blocking you. Well, maybe not, but you get the idea. 🚫", - "I was busy doing nothing, but now I'm busy ignoring you. šŸ›‘", - "This is a no-annoyance zone, and you're breaking the rules. 🚧", - "Your message was classified as spam and deleted. šŸ—‘ļø", - "Are you trying to be annoying? Because you're really good at it. šŸ„‡", - "I'm in the middle of ignoring you. Please try again never. 😓", - "Sorry, I'm busy defending Valdoran. Try later. šŸ°", - "Can't you see I'm in the middle of a dungeon raid? šŸ—”ļø", - "I'm on a quest. Your message is not part of it. šŸ—ŗļø", - "You again? I'm trying to earn some coins here. šŸŖ™", - "I'm too busy battling knights to deal with you. āš”ļø", - "Your message is about as welcome as a sword to the face. šŸ—”ļø", - "Did you think I had time to chat while climbing the rankings? šŸ˜‚", - "Nope, I'm too busy becoming the king of GoBattle.io. šŸ‘‘", - "Your message just got lost in Valdoran. šŸŒ„", - "Can't talk, must collect coins. Bye! šŸ’°", - "Is this about depositing items? Because I'm full. šŸ¦", - "I'm fighting dragons, not dealing with distractions. šŸ‰", - "Your message is like a common item. Not interested. šŸ“¦", - "I'm in the middle of a duel. Your message can wait. āš”ļø", - "I'm trying to be number one, not your personal bot. šŸš€", - "Did you drop a rare item? Nope, just another message. šŸ›”ļø", - "Leave a message after the battle horn. šŸ“Æ", - "Can't you see I'm raiding a dungeon? šŸ°", - "No time for chat, I've got coins to collect. šŸŖ™", - "I'm too busy defending the castle. Try someone else. šŸÆ", - "You again? I'm on a mission to find relics. šŸš€", - "Your message is like a common drop. Not exciting. šŸ“¦", - "I'm trying to win fights, not read messages. šŸ—”ļø", - "Are you here to help me climb the rankings? No? Then bye. šŸ“ˆ", - "I’m in the middle of an epic battle, don’t you get it? šŸ›”ļø", - "Your messages are like dungeon traps. Annoying. šŸ—ļø", - "No time for distractions, I have a kingdom to protect. šŸ°", - "I'm in Valdoran, where your message doesn’t matter. šŸžļø", - "I'm too busy battling to read your message. šŸ—”ļø", - "Can you deposit your message in the bank? Thanks. šŸ¦", - "Your message is like a missed coin. Not worth it. šŸŖ™", - "I'm on a relic hunt. Your message is not a relic. šŸ—ŗļø", - "Busy earning coins, no time for chat. šŸ’°", - "Your message is like an empty chest. Disappointing. šŸ“¦", - "I'm in the king's court, no time for your messages. šŸ‘‘", - "Your message just got hit by a knight. āš”ļø", - "I'm climbing the rankings, not answering messages. šŸ“ˆ", - "Do you have a relic? No? Then leave me alone. šŸ›”ļø", - "In the middle of a duel, can't chat now. šŸ—”ļø", - "Valdoran needs me more than you do. šŸŒ„", - "I'm too busy being a GoBattle.io legend. šŸŽ–ļø", - "Your message is like a common item, easily ignored. šŸ“¦", - "Fighting for the throne, no time for this. šŸ‘‘", - "Collecting coins is more important than your message. šŸ’°", - "I'm in the dungeon, fighting for relics. šŸ—ļø", - "Your message is like an NPC, easily dismissed. šŸ¤–", - "Busy battling for the top spot, leave me alone. šŸ“ˆ", - "Your message just fell into the moat. šŸ°", - "Can't you see I'm busy raiding? šŸÆ", - "I'm on a quest to ignore distractions. šŸ—ŗļø", - "Do you have any ultra-rare items? No? Then go away. šŸ›”ļø", - "I’m in the middle of becoming king, no time for chat. šŸ‘‘", - "Your message just got trampled by my mount. šŸŽ", - "Collecting coins and ignoring messages. šŸŖ™", - "I'm in Valdoran, too busy for this. šŸŒ„", - "I’m in the middle of a battle, can't talk. šŸ—”ļø", - "Do you have gold? No? Then stop bothering me. šŸ’°", - "Your message is like a common drop, ignored. šŸ“¦", - "I’m in the king's court, not the chatroom. šŸ‘‘", - "I'm defending Valdoran, no time for this. šŸ›”ļø", - "Busy climbing the rankings, can't respond. šŸ“ˆ", - "Your message just got slain by a knight. āš”ļø", - "I’m on a quest for coins, not messages. šŸŖ™", - "Valdoran calls, your message doesn't. šŸŒ„", - "No time for chat, too busy being awesome. šŸ°", - "Not funny I didn't laugh. Your joke is so bad I would have preferred the joke went over my head and you gave up re-telling me the joke. To be honest this is a horrid attempt at trying to get a laugh out of me. Not a chuckle, not a hehe, not even a subtle burst of air out of my esophagus. Science says before you laugh your brain preps your face muscles but I didn't even feel the slightest twitch. 0/10 this joke is so bad I cannot believe anyone legally allowed you to be creative at all. The amount of brain power you must have put into that joke has the potential to power every house on Earth. Get a personality and learn how to make jokes, read a book. I'm not saying this to be funny I genuinely mean it on how this is just bottom barrel embarrassment at comedy. You've single handedly killed humor and every comedic act on the planet. I'm so disappointed that society has failed as a whole in being able to teach you how to be funny. Honestly if I put in all my power and time to try and make your joke funny it would require Einstein himself to build a device to strap me into so I can be connected to the energy of a billion stars to do it, and even then all that joke would get from people is a subtle scuff. You're lucky I still have the slightest of empathy for you after telling that joke otherwise I would have committed every war crime in the book just to prevent you from attempting any humor ever again. We should put that joke in text books so future generations can be wary of becoming such an absolute comedic failure. Im disappointed, hurt, and outright offended that my precious time has been wasted in my brain understanding that joke. In the time that took I was planning on helping kids who have been orphaned, but because of that you've waisted my time explaining the obscene integrity of your terrible attempt at comedy. Now those kids are suffering without meals and there's nobody to blame but you.", - "I have to tell you, never would I have imagined wasting so much time listening to such uninteresting remarks. I was savoring a moment of peace when you interrupted my silence with a conversation so devoid of meaning that even a dead leaf would have been more captivating. Not once did you manage to pique my interest, not a single original idea, nothing worth listening to. Honestly, I would have preferred listening to paint dry. I'm disappointed that you didn't realize the emptiness of your words. If I had to measure the value of this interaction, it would be somewhere between watching grass grow and waiting for water to boil. Your conversation was so dull that even a family Zoom meeting would have seemed exciting in comparison. I don't understand how you could have thought these banalities were worth sharing. I have to tell you that I genuinely feel sorry for you if you believe this kind of exchange is enriching. For my part, I feel like I've been a victim of a time theft, and I'm almost offended. I could have done so many more useful things: read a book, meditate, even taking a nap would have been more productive. I'm not saying this to hurt you, but to make you understand the extent of my disappointment. If this were a contest of uselessness, you would win the gold medal without any contest. I sincerely hope that, in the future, you will find more interesting topics of conversation or, failing that, realize when it's better to stay silent.", - "I have to tell you, I was far from imagining that someone could be so uninteresting. Just as I was enjoying a well-deserved moment of calm, you came along with such a dull conversation that it could have made watching paint dry seem exciting. Not once did you manage to capture my attention, not a single original idea, nothing worth listening to. Frankly, I would have preferred listening to the monotonous hum of a refrigerator. I'm astonished that you didn't realize the abyssal emptiness of your words. If this interaction were to be evaluated, it would rank somewhere between watching rust form and waiting for morning dew to appear. Your conversation was so devoid of interest that even sorting socks would have been more stimulating. How could you think for a moment that these banalities were worth sharing? I feel a deep sense of pity for you if you believe this kind of exchange is enriching. For my part, I feel like my precious time has been stolen from me, and it irritates me to no end. So many more useful things could have been accomplished: reading a book, meditating, even doing household chores would have been more productive. I'm not saying this to hurt you, but to make you understand how disappointed I am. If it were a competition of uselessness, you would effortlessly win the gold medal. I sincerely hope that in the future, you can find more engaging topics of conversation or, failing that, learn to appreciate silence when it's needed.", - "Interesting things to say. It's not that I'm trying to be mean, it's just that life is too short to waste on such lackluster interactions. Imagine if every moment was filled with such blandness—how dull would that be? So please, next time, spare both of us the agony and think twice before initiating such a conversation. It’s a favor to both you and anyone else who might find themselves on the receiving end of such tedious dialogue. Trust me, the world will thank you for it.", - "I'm sorry, but I'm afraid that conversation made me lose brain cells. I can practically feel my IQ dropping by the second. Next time, could you try to at least make it a little interesting?", - "I just realized that talking to you is like talking to a brick wall. Except the wall might actually be more responsive.", - "Are you trying to test my patience? Because if so, congratulations, you succeeded. Now please, go away.", - "Your messages are like background noise—constantly there but never worth paying attention to.", - "Look, I'm really busy right now. And by busy, I mean doing anything other than reading your messages.", - "Wow, another message from you. It’s like you’re a persistent mosquito that I can’t swat away.", - "If I had a penny for every time you sent a pointless message, I'd be richer than Elon Musk by now.", - "Please, just stop. I don't know how else to make it clear that I'm not interested in whatever it is you're saying.", - "Your messages are like spam emails: unwanted, uninteresting, and instantly deleted.", - "I wish I could say your messages brighten my day, but they don't. They really, really don't.", - "I could ignore you professionally, but I’m choosing to tell you: this conversation is over.", - "Do you have an off switch? Because it would be really useful right about now.", - "You're like an uninvited guest that just won’t leave. Seriously, just go.", - "I'm officially done with this. Talking to you is like screaming into the void.", - "Is this your hobby? Annoying people until they lose their sanity? Because you're excelling at it.", - "Congratulations, you've successfully become the highlight of my 'most annoying experiences' list.", - "You're like a pop-up ad that keeps appearing even after I've clicked 'close' a million times.", - "I think I'd rather watch paint dry than read another one of your messages.", - "Just when I thought this conversation couldn't get any worse, you sent another message.", - "Do you have any idea how exhausting it is to deal with you? Because it's incredibly exhausting.", - "Wow, another message. Just what I didn't want. Again.", - "You're persistent, I'll give you that. Persistent and incredibly annoying.", - "This is me, officially signing off from this conversation. Please do the same.", - "Your messages are like the soundtrack to my nightmares—constant and unwelcome.", - "I was hoping you'd get bored and leave me alone. Apparently, I was wrong.", - "If bothering people was an Olympic sport, you'd be a gold medalist.", - "Talking to you is like walking on broken glass—painful and best avoided.", - "I'm all out of responses, and frankly, all out of patience.", - "Do you enjoy being ignored? Because it seems like you do.", - "I've reached my limit. This is where I draw the line.", - "Do you ever get tired of being so persistently annoying? Because I'm tired of it.", - "Wow, another message. My day just got a little worse.", - "If I had a dime for every annoying message you've sent, I could retire.", - "I was hoping you'd run out of things to say, but here you are, still going.", - "Please, for the love of all that is good, just stop messaging me.", - "Your messages are like a bad penny—always turning up when they're least wanted.", - "I'm out of patience and out of responses. This conversation is done.", - "You must really enjoy being the most annoying person in the room, huh?", - "Your persistence is impressive. Annoying, but impressive.", - "I was having a good day until I saw your message. Now, not so much.", - "Wow, you really don't know when to quit, do you?", - "I'm in the middle of ignoring you. Please don’t interrupt.", - "You’ve got a real talent for being insufferable. Congrats.", - "Just when I thought this couldn't get worse, you proved me wrong.", - "If I had a dollar for every annoying message you've sent, I'd be a billionaire.", - "Seriously, stop. This isn't fun for either of us.", - "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", - "I’ve reached my limit. Please, just stop messaging me.", - "You’re like a broken record, repeating the same annoying message over and over.", - "If annoying people were a sport, you’d be the world champion.", - "Wow, another message. My day just went from bad to worse.", - "Your messages are like background noise—constant and irritating.", - "I can't believe you're still going. Please, stop.", - "I've officially run out of patience. This conversation is over.", - "You must really enjoy being ignored, huh?", - "If I had a penny for every annoying message you sent, I'd be rich.", - "Do you ever get tired of being so persistently annoying? Because I’m tired of it.", - "Wow, you really don’t know when to quit, do you?", - "This conversation is over. Please, just stop.", - "I’m out of patience, and frankly, out of responses.", - "Your messages are like spam—unwanted and instantly deleted.", - "I was having a good day until I saw your message. Now, not so much.", - "Please, for the love of all that is good, just stop.", - "You must really enjoy being the most annoying person in the room.", - "Your persistence is impressive. Annoying, but impressive.", - "I was hoping you'd run out of things to say, but apparently, I was wrong.", - "Your messages are like a bad penny—always turning up when they’re least wanted.", - "I'm out of patience and out of responses. This conversation is done.", - "You’ve got a real talent for being insufferable. Congrats.", - "Just when I thought this couldn’t get worse, you proved me wrong.", - "If I had a dollar for every annoying message you’ve sent, I’d be a billionaire.", - "Seriously, stop. This isn't fun for either of us.", - "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", - "I’ve reached my limit. Please, just stop messaging me.", - "You’re like a broken record, repeating the same annoying message over and over.", - "If annoying people were a sport, you’d be the world champion.", - "Wow, another message. My day just went from bad to worse.", - "Your messages are like background noise—constant and irritating.", - "I can't believe you're still going. Please, stop.", - "I've officially run out of patience. This conversation is over.", - "You must really enjoy being ignored, huh?", - "If I had a penny for every annoying message you sent, I’d be rich.", - "Do you ever get tired of being so persistently annoying? Because I’m tired of it.", - "Wow, you really don’t know when to quit, do you?", - "This conversation is over. Please, just stop.", - "I’m out of patience, and frankly, out of responses.", - "Your messages are like spam—unwanted and instantly deleted.", - "I was having a good day until I saw your message. Now, not so much.", - "Please, for the love of all that is good, just stop.", - "You must really enjoy being the most annoying person in the room.", - "Your persistence is impressive. Annoying, but impressive.", - "I was hoping you'd run out of things to say, but apparently, I was wrong.", - "Your messages are like a bad penny—always turning up when they’re least wanted.", - "I'm out of patience and out of responses. This conversation is done.", - "You’ve got a real talent for being insufferable. Congrats.", - "Just when I thought this couldn’t get worse, you proved me wrong.", - "If I had a dollar for every annoying message you’ve sent, I’d be a billionaire.", - "Seriously, stop. This isn't fun for either of us.", - "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", - "I’ve reached my limit. Please, just stop messaging me.", - "You’re like a broken record, repeating the same annoying message over and over.", - "If annoying people were a sport, you’d be the world champion.", - "Wow, another message. My day just went from bad to worse.", - "Your messages are like background noise—constant and irritating.", - "I can't believe you're still going. Please, stop.", - "I've officially run out of patience. This conversation is over.", - "You must really enjoy being ignored, huh?", - "If I had a penny for every annoying message you sent, I’d be rich.", - "Do you ever get tired of being so persistently annoying? Because I’m tired of it.", - "Wow, you really don’t know when to quit, do you?", - "This conversation is over. Please, just stop.", - "I’m out of patience, and frankly, out of responses.", - "Your messages are like spam—unwanted and instantly deleted.", - "I was having a good day until I saw your message. Now, not so much.", - "Please, for the love of all that is good, just stop.", - "You must really enjoy being the most annoying person in the room.", - "Your persistence is impressive. Annoying, but impressive.", - "I was hoping you'd run out of things to say, but apparently, I was wrong.", - "Your messages are like a bad penny—always turning up when they’re least wanted.", - "I'm out of patience and out of responses. This conversation is done.", - "You’ve got a real talent for being insufferable. Congrats.", - "Just when I thought this couldn’t get worse, you proved me wrong.", - "If I had a dollar for every annoying message you’ve sent, I’d be a billionaire.", - "Seriously, stop. This isn't fun for either of us.", - "Do you ever get tired of being this persistently annoying? Because it's exhausting for me.", - "I’ve reached my limit. Please, just stop messaging me.", - "You’re like a broken record, repeating the same annoying message over and over.", - "If annoying people were a sport, you’d be the world champion.", - "Wow, another message. My day just went from bad to worse." -] diff --git a/src/utils.js b/src/utils.js index fea6936..885929b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,79 +1,5 @@ -const {createCanvas, Font} = require("canvas"); const {ChannelType, PermissionFlagsBits} = require("discord.js"); -function get_random_int(max){ - return Math.floor(Math.random() * max); -} - -function table(table_data, blue_them = false){ - let columns_width = []; // The size of each column. - - for (const row of table_data){ - for (let x = 0; x < row.length; x++){ - const width = row[x].length * 20 + 15; - if (x > columns_width.length - 1){ - columns_width.push(width); - }else{ - columns_width[x] = Math.max(columns_width[x], width); - } - } - } - - const height_field = 40; - const margin = 15; - const gap_x = 4; - const gap_y = 2; - const absolute_width = sum(columns_width) + ((columns_width.length - 1) * gap_x) + margin * 2; - const absolute_height = (table_data.length * height_field) + ((table_data.length - 1) * gap_y) + margin * 2; - - const canvas = createCanvas(absolute_width, absolute_height); - const ctx = canvas.getContext("2d"); - ctx.imageSmoothingEnabled = false; - - let gradient = ctx.createLinearGradient(0, absolute_height, 0, 0); - // Add three color stops. - if (blue_them){ - gradient.addColorStop(0, "#002"); - gradient.addColorStop(1, "#115"); - }else{ - gradient.addColorStop(0, "#200"); - gradient.addColorStop(1, "#511"); - } - - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, absolute_width, absolute_height); - - gradient = ctx.createLinearGradient(0, absolute_height, 0, 0); - // Add three color stops. - if (blue_them){ - gradient.addColorStop(1, "#050527eb"); - gradient.addColorStop(0, "#010113eb"); - }else{ - gradient.addColorStop(1, "#270505eb"); - gradient.addColorStop(0, "#130101eb"); - } - - let offset_y = margin; - for (let j = 0; j < table_data.length; j++){ - let offset_x = margin; - for (let i = 0; i < columns_width.length; i++){ - if (j){ - ctx.fillStyle = gradient; - }else if (blue_them){ - ctx.fillStyle = "#04040aed"; - }else{ - ctx.fillStyle = "#0a0404ed"; - } - ctx.fillRect(offset_x, offset_y, columns_width[i], height_field); - gobattle_font(ctx, offset_x + 12, offset_y + 28, table_data[j][i], "#fff", 20); - offset_x += gap_x + columns_width[i]; - } - offset_y += gap_y + height_field; - } - - return canvas.toBuffer("image/png"); -} - function restrict_text(str, nb){ if (str.length > nb){ str = str.substring(0, Math.max(0, nb - 3)) + "..."; @@ -142,53 +68,6 @@ function get_utf_time_next_king(){ return next_monday.getTime(); } -function gobattle_font(ctx, x, y, text, color, size, width = undefined){ - ctx.font = `bold ${size}px serif`; - ctx.fillStyle = color; - ctx.strokeStyle = "black"; - ctx.lineWidth = size / 4; - ctx.strokeText(text, x, y, width); - ctx.strokeText(text, x, y + (size / 7), width); - ctx.fillText(text, x, y, width); -} - -function get_lines(ctx, text, maxWidth){ - const words = text.split(" "); - const lines = []; - let currentLine = words[0]; - - for (let i = 1; i < words.length; i++) { - const word = words[i]; - const width = ctx.measureText(currentLine + " " + word).width; - if (width < maxWidth){ - currentLine += " " + word; - }else{ - lines.push(currentLine); - currentLine = word; - } - } - lines.push(currentLine); - - return lines; -} - -function get_aabb_lines(ctx, lines, line_height = 3){ - const aabb = [0, 0]; - - for (const line of lines){ - const metrics = ctx.measureText(line); - aabb[0] = Math.max(aabb[0], metrics.width); - const actual_height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; - aabb[1] += actual_height; - } - - if (lines.length > 1){ - aabb[1] += line_height * (lines.length - 1); - } - - return aabb; -} - function sum(array){ let result = 0; for (const element of array){ @@ -218,8 +97,6 @@ async function send_echo(client, message){ } } -exports.get_random_int = get_random_int; -exports.table = table; exports.restrict_text = restrict_text; exports.format_score = format_score; exports.format_speed_run_time = format_speed_run_time; @@ -227,9 +104,6 @@ exports.get_level = get_level; exports.get_level_adventurer = get_level_adventurer; exports.get_utc_date = get_utc_date; exports.get_utf_time_next_king = get_utf_time_next_king; -exports.gobattle_font = gobattle_font; -exports.get_lines = get_lines; -exports.get_aabb_lines = get_aabb_lines; exports.sum = sum; exports.send_echo = send_echo; exports.get_first_chat_channel = get_first_chat_channel; From 579c86146a09353e25fe20d4f9eb70d6cb4f47a7 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Mon, 15 Jul 2024 19:33:35 +0200 Subject: [PATCH 27/54] Updated leaderboard formatting for better readability. --- README.md | 2 -- src/commands/dungeon.js | 8 ++------ src/commands/item.js | 7 +------ src/commands/ranking.js | 44 +++++++++++++++-------------------------- src/commands/server.js | 10 +++------- src/ultralist.json | 22 ++++++++++++++++++++- 6 files changed, 43 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index df09c1c..6794123 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,6 @@ npm -v ```bash npm install ``` - (If there is a problem compiling the dependencies, use the `npm rebuild` command) - If there are problems with installing the `canvas` dependency please follow the procedure for installing this dependency here: https://www.npmjs.com/package/canvas. 4. Start the bot. ```bash diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index 0f6ee8f..de74d76 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -56,18 +56,14 @@ async function get_list(interaction, client){ embed.setDescription(description, {split: false}); for (const dungeon_data of dungeon_list){ - description += `* ${restrict_text(dungeon_data.name || "*Unknow?*", 45)}: \`${dungeon_data.id} šŸ·ļø\` \`${dungeon_data.min_level || "Unknow?"} šŸ’Ŗ\`\n`; + description += `* ${restrict_text(dungeon_data.name || "*Unknow?*", 45)}#${dungeon_data.id}: \`${dungeon_data.min_level || "Unknow?"} šŸ’Ŗ\`\n`; } embed.setDescription(description, {split: false}); embed.addFields( {name: "> šŸ”¢ __Number of dungeons__", value: `> ${dungeon_list.length}`, inline: true}, - ); - - embed.addFields( - {name: "> __Min level__", value: "> šŸ’Ŗ", inline: true}, - {name: "> __Dungeon ID__", value: "> šŸ·ļø", inline: true} + {name: "> __Min level__", value: "> šŸ’Ŗ", inline: true} ); embed.setTimestamp(); diff --git a/src/commands/item.js b/src/commands/item.js index 7cd12ae..5bbfe0a 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -143,7 +143,7 @@ async function get_ultrarare_drops(interaction, client){ } } - description += `* ${restrict_text(name_ultra || "*Unknow?*", 45)}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%) šŸ“¤\` \`${key} šŸ·ļø\`\n`; + description += `* ${restrict_text(name_ultra || "*Unknow?*", 45)}#${key}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%)\`\n`; } embed.setDescription(description, {split: false}); @@ -153,11 +153,6 @@ async function get_ultrarare_drops(interaction, client){ {name: "> šŸ“¦ __Other__", value: `> ${((1 - drops_ratio) * 100).toPrecision(2)}%`, inline: true} ); - embed.addFields( - {name: "> __Drops__", value: "> šŸ“¤", inline: true}, - {name: "> __Item ID__", value: "> šŸ·ļø", inline: true} - ); - embed.setTimestamp(); await interaction.editReply({ diff --git a/src/commands/ranking.js b/src/commands/ranking.js index 44f03f4..b1a3d85 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -171,21 +171,16 @@ async function get_king(interaction, client){ const ranking = data?.ranking; for (let i = 0; i < max_fields && i < ranking.length; i++){ const field = ranking[i]; - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.reputation)} šŸ”±\` \`${field?.id} šŸ·ļø\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${format_score(field?.reputation)} REP\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> šŸ—“ __Week__", value: `> ${data?.week || "_Unknown?_"}`, inline: true}, {name: "> šŸ—“ __Year__", value: `> ${data?.year || "_Unknown?_"}`, inline: true} ); - embed.addFields( - {name: "> __Reputation__", value: "> šŸ”±", inline: true}, - {name: "> __User ID__", value: "> šŸ·ļø", inline: true} - ); - embed.setTimestamp(); await interaction.editReply({ @@ -221,10 +216,10 @@ async function get_weekly(interaction, client){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)} šŸŖ™\` \`${format_score(field?.kills)} āš”ļø\` \`${format_score(field?.deaths)} šŸ’€\` \`${format_score(field?.experience)} šŸ› ļø\` \`${level} šŸ’Ŗ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™\` \`${format_score(field?.kills)}āš”ļø\` \`${format_score(field?.deaths)}šŸ’€\` \`${format_score(field?.experience)}šŸ› ļø\` \`${level}šŸ’Ŗ\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> __Coins__", value: "> šŸŖ™", inline: true}, @@ -269,10 +264,10 @@ async function get_monthly(interaction, client){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)} šŸŖ™\` \`${format_score(field?.kills)} āš”ļø\` \`${format_score(field?.deaths)} šŸ’€\` \`${format_score(field?.experience)} šŸ› ļø\` \`${level} šŸ’Ŗ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™\` \`${format_score(field?.kills)}āš”ļø\` \`${format_score(field?.deaths)}šŸ’€\` \`${format_score(field?.experience)}šŸ› ļø\` \`${level}šŸ’Ŗ\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> __Coins__", value: "> šŸŖ™", inline: true}, @@ -317,10 +312,10 @@ async function get_overall(interaction, client){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)} šŸŖ™\` \`${format_score(field?.kills)} āš”ļø\` \`${format_score(field?.deaths)} šŸ’€\` \`${format_score(field?.experience)} šŸ› ļø\` \`${level} šŸ’Ŗ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™\` \`${format_score(field?.kills)}āš”ļø\` \`${format_score(field?.deaths)}šŸ’€\` \`${format_score(field?.experience)}šŸ› ļø\` \`${level}šŸ’Ŗ\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> __Coins__", value: "> šŸŖ™", inline: true}, @@ -364,15 +359,14 @@ async function get_adventurer(interaction, client){ for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const level = get_level_adventurer(field?.score); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${level} šŸ’Ŗ\` \`${format_score(field?.score)} šŸ’Æ\` \`${field?.id} šŸ·ļø\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${level}šŸ’Ŗ\` \`${format_score(field?.score)}šŸ’Æ\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> __Level ADV__", value: "> šŸ’Ŗ", inline: true}, - {name: "> __Score__", value: "> šŸ’Æ", inline: true}, - {name: "> __User ID__", value: "> šŸ·ļø", inline: true} + {name: "> __Score__", value: "> šŸ’Æ", inline: true} ); embed.setTimestamp(); @@ -409,15 +403,14 @@ async function get_relic_hunter(interaction, client){ for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const level = get_level_adventurer(field?.score); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${level} šŸ’Ŗ\` \`${format_score(field?.score)} šŸ’Æ\` \`${field?.id} šŸ·ļø\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${level}šŸ’Ŗ\` \`${format_score(field?.score)}šŸ’Æ\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> __Level RLH__", value: "> šŸ’Ŗ", inline: true}, - {name: "> __Score__", value: "> šŸ’Æ", inline: true}, - {name: "> __User ID__", value: "> šŸ·ļø", inline: true} + {name: "> __Score__", value: "> šŸ’Æ", inline: true} ); embed.setTimestamp(); @@ -457,7 +450,7 @@ async function get_speedrun(interaction, client){ for (var i = 0; i < max_fields && i < list_size; i++){ const field = ranking[i]; const time = format_speed_run_time(field?.time); - description += `${field?.rank}. ${restrict_text(field?.nick, 20)}: \`${time} ā±ļø\` \`${field?.id} šŸ·ļø\`\n`; + description += `${field?.rank}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${time}\`\n`; } embed.setDescription(description); @@ -466,12 +459,7 @@ async function get_speedrun(interaction, client){ {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, {name: "> šŸ·ļø __Dungeon ID__", value: `> ${dungeon_id}`, inline: true} ); - - embed.addFields( - {name: "> __Time__", value: "> ā±ļø", inline: true}, - {name: "> __User ID__", value: "> šŸ·ļø", inline: true} - ); - + embed.setTimestamp(); await interaction.editReply({ diff --git a/src/commands/server.js b/src/commands/server.js index 9b41f43..b953408 100644 --- a/src/commands/server.js +++ b/src/commands/server.js @@ -97,18 +97,14 @@ async function get_list(interaction, client){ is_online = false; } - description += `* ${restrict_text(field?.friendlyName, 25)}: \`${field?.id} šŸ·ļø\` \`${field?.version} šŸ”\` \`${field?.admin} šŸ› ļø\` \`${(is_online ? "Online" : "Down")} 🌐\`\n`; + description += `* ${restrict_text(field?.friendlyName, 25)}#${field?.id}: \`${field?.version} šŸ”\` \`${field?.admin} šŸ› ļø\` \`${(is_online ? "Online" : "Down")} 🌐\`\n`; } - embed.setDescription(description, {split: false}); + embed.setDescription(description); embed.addFields( {name: "> šŸŽ® __Platform__", value: `> ${platform}`, inline: true}, - {name: "> šŸ”ƒ __Requested version__", value: `> ${version.toString()}`, inline: true} - ); - - embed.addFields( - {name: "> __Server ID__", value: "> šŸ·ļø", inline: true}, + {name: "> šŸ”ƒ __Requested version__", value: `> ${version.toString()}`, inline: true}, {name: "> __Version__", value: "> šŸ”", inline: true}, {name: "> __Administrator level__", value: "> šŸ› ļø", inline: true}, {name: "> __Server status__", value: "> 🌐", inline: true} diff --git a/src/ultralist.json b/src/ultralist.json index 2cb4c73..f521399 100644 --- a/src/ultralist.json +++ b/src/ultralist.json @@ -415,11 +415,31 @@ "name": "Flying Skill", "tickets": null, "drops": null, - "id": null, + "id": 198, "description": "Gives you the skill to dash vertically with your wings", "uses": null, "is_relic": true, "max_in_inventory": 1 + }, + { + "name": "Dodge Charm", + "tickets": 100, + "drops": "All", + "id": 189, + "description": "1% chance of dodging any attack", + "uses": null, + "is_relic": true, + "max_in_inventory": 1 + }, + { + "name": "Gem Dust", + "tickets": 50, + "drops": "Stahmite Mines", + "id": 191, + "description": "Up to 5 dyams (+ Luck) per gem in chests", + "uses": null, + "is_relic": true, + "max_in_inventory": 25 } ] } From 9c0253b1bbe08ff77e140737191d41453b24577b Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Wed, 24 Jul 2024 00:15:36 +0200 Subject: [PATCH 28/54] reformat command messages. --- src/commands/date_new_king.js | 2 +- src/commands/ranking.js | 20 +++++--------------- src/commands/ultrarare_drop_chance.js | 27 +++++++++++++++++++++++++++ src/index.js | 9 +++++++-- 4 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 src/commands/ultrarare_drop_chance.js diff --git a/src/commands/date_new_king.js b/src/commands/date_new_king.js index 3227991..d52416f 100644 --- a/src/commands/date_new_king.js +++ b/src/commands/date_new_king.js @@ -8,7 +8,7 @@ date_new_king_command.setDescription("Get the date and time remaining before the async function get_date_new_king(interaction, client){ const utc = Math.floor(get_utf_time_next_king() / 1000); - interaction.reply(`New king on ().`); + await interaction.reply(`New king on ().`); } exports.get_date_new_king = get_date_new_king; diff --git a/src/commands/ranking.js b/src/commands/ranking.js index b1a3d85..b102511 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -216,7 +216,7 @@ async function get_weekly(interaction, client){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™\` \`${format_score(field?.kills)}āš”ļø\` \`${format_score(field?.deaths)}šŸ’€\` \`${format_score(field?.experience)}šŸ› ļø\` \`${level}šŸ’Ŗ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}āš”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; } embed.setDescription(description); @@ -264,7 +264,7 @@ async function get_monthly(interaction, client){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™\` \`${format_score(field?.kills)}āš”ļø\` \`${format_score(field?.deaths)}šŸ’€\` \`${format_score(field?.experience)}šŸ› ļø\` \`${level}šŸ’Ŗ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}āš”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; } embed.setDescription(description); @@ -312,7 +312,7 @@ async function get_overall(interaction, client){ const field = data[i]; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™\` \`${format_score(field?.kills)}āš”ļø\` \`${format_score(field?.deaths)}šŸ’€\` \`${format_score(field?.experience)}šŸ› ļø\` \`${level}šŸ’Ŗ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}āš”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; } embed.setDescription(description); @@ -359,15 +359,10 @@ async function get_adventurer(interaction, client){ for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const level = get_level_adventurer(field?.score); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${level}šŸ’Ŗ\` \`${format_score(field?.score)}šŸ’Æ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${format_score(field?.score)} XP (${level} LVL)\`\n`; } embed.setDescription(description); - - embed.addFields( - {name: "> __Level ADV__", value: "> šŸ’Ŗ", inline: true}, - {name: "> __Score__", value: "> šŸ’Æ", inline: true} - ); embed.setTimestamp(); @@ -403,16 +398,11 @@ async function get_relic_hunter(interaction, client){ for (let i = 0; i < max_fields && i < data.length; i++){ const field = data[i]; const level = get_level_adventurer(field?.score); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${level}šŸ’Ŗ\` \`${format_score(field?.score)}šŸ’Æ\`\n`; + description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${format_score(field?.score)} XP (${level} LVL)\`\n`; } embed.setDescription(description); - embed.addFields( - {name: "> __Level RLH__", value: "> šŸ’Ŗ", inline: true}, - {name: "> __Score__", value: "> šŸ’Æ", inline: true} - ); - embed.setTimestamp(); await interaction.editReply({ diff --git a/src/commands/ultrarare_drop_chance.js b/src/commands/ultrarare_drop_chance.js new file mode 100644 index 0000000..30ef3cd --- /dev/null +++ b/src/commands/ultrarare_drop_chance.js @@ -0,0 +1,27 @@ +const {SlashCommandBuilder} = require("discord.js"); + +const ultrarare_drop_chance_command = new SlashCommandBuilder(); +ultrarare_drop_chance_command.setName("get_ultrarare_drop_chance"); +ultrarare_drop_chance_command.setDescription("Calculate the probability of an ultra falling into a chest based on a level of luck."); +ultrarare_drop_chance_command.addNumberOption((option) => { + option.setName("luck"); + option.setDescription("Level of luck."); + option.setMinValue(0); + option.setMaxValue(20); + option.setRequired(true); + + return option; +}); + +async function get_ultrarare_drop_chance(interaction, client){ + const BASE = 0.6; + const STEP_LEVEL = 0.05; + + const luck = interaction.options.get("luck")?.value || 0; + const result = BASE + STEP_LEVEL * luck; + + await interaction.reply(`You have an \`${result.toPrecision(2)}%\` chance of getting an Ultrarare from a chest with a LUCK level of ${luck}.`); +} + +exports.get_ultrarare_drop_chance = get_ultrarare_drop_chance; +exports.ultrarare_drop_chance_command = ultrarare_drop_chance_command; diff --git a/src/index.js b/src/index.js index 69f96eb..f6af04c 100644 --- a/src/index.js +++ b/src/index.js @@ -14,6 +14,7 @@ const {get_ping, ping_command} = require("./commands/ping.js"); const {get_leave, leave_command} = require("./commands/leave.js"); const {get_setting, setting_command} = require("./commands/setting.js"); const {get_echo, echo_command} = require("./commands/echo.js"); +const {get_ultrarare_drop_chance, ultrarare_drop_chance_command} = require("./commands/ultrarare_drop_chance.js"); const global_commands = [ ranking_command, @@ -26,7 +27,8 @@ const global_commands = [ dungeon_command, date_new_king_command, ping_command, - leave_command + leave_command, + ultrarare_drop_chance_command ]; const private_commands = [ @@ -139,6 +141,9 @@ client.on("interactionCreate", async (interaction) => { case "echo": await get_echo(interaction, client); break; + case "get_ultrarare_drop_chance": + await get_ultrarare_drop_chance(interaction, client); + break; default: await interaction.reply({content: "This command no longer exists.", ephemeral: true}); } @@ -178,4 +183,4 @@ client.on("messageCreate", async (msg) => { }catch (error){ console.error("Impossible to answer:", error); } -}); \ No newline at end of file +}); From f7c3bc120954526cfc8f841b4a2eca07c1bf1ea8 Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Wed, 24 Jul 2024 00:18:00 +0200 Subject: [PATCH 29/54] Create .env --- .env | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..33f5f28 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +# Discord plaplication token. This token is private and should not be shared! +TOKEN = InsertBotTokenHere + +# Guild identifiers can use critical administration commands. +# The default is the Official GoBattle.io Discord server. +# You can also add your own server for debugging and tuning purposes of this application. +GUILD_ADMIN_ID = ["380588354934276097"] # JSON format. From 3bba6eb4616c8a8a655e04526ce44abeca77dfd3 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Tue, 30 Jul 2024 16:13:36 +0200 Subject: [PATCH 30/54] Adds layout of command results (buttons) and adding emojis. --- .env | 7 - src/commands/asset.js | 89 ++++++- src/commands/dungeon.js | 83 +++++- src/commands/item.js | 115 ++++++-- src/commands/ranking.js | 578 ++++++++++++++++++++++++++-------------- src/commands/server.js | 90 +++++-- src/commands/user.js | 4 +- src/head_list.json | 292 ++++++++++++++++++++ src/index.js | 50 +++- src/ultralist.json | 147 ++++++---- src/utils.js | 23 +- 11 files changed, 1145 insertions(+), 333 deletions(-) delete mode 100644 .env create mode 100644 src/head_list.json diff --git a/.env b/.env deleted file mode 100644 index 33f5f28..0000000 --- a/.env +++ /dev/null @@ -1,7 +0,0 @@ -# Discord plaplication token. This token is private and should not be shared! -TOKEN = InsertBotTokenHere - -# Guild identifiers can use critical administration commands. -# The default is the Official GoBattle.io Discord server. -# You can also add your own server for debugging and tuning purposes of this application. -GUILD_ADMIN_ID = ["380588354934276097"] # JSON format. diff --git a/src/commands/asset.js b/src/commands/asset.js index 346adaa..71af3e6 100644 --- a/src/commands/asset.js +++ b/src/commands/asset.js @@ -1,4 +1,4 @@ -const {SlashCommandBuilder, AttachmentBuilder, EmbedBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); const zlib = require("zlib"); const {restrict_text} = require("../utils.js"); @@ -53,22 +53,95 @@ async function get_list(interaction, client){ const data = await response.json(); - let content = ""; + const embed = new EmbedBuilder(); + embed.setTitle("šŸ—‚ļø List of assets šŸ—‚ļø"); + + const header_description = "List of assets with their respective identifiers:\n"; + const max_items_by_pages = 10; + const pages = new Array(Math.ceil(data.files.length / max_items_by_pages)); + + let current_page = -1; for (let i = 0; i < data.files.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const file = data.files[i]; const is_compressed = file.file.endsWith("$"); const file_name = is_compressed ? file.file.slice(0, -1) : file.file; - content += `#${i + 1} ${file_name}\n`; + if (is_compressed){ + pages[current_page] += `* **${file_name}**#${i + 1}\n`; + }else{ + const url = new URL(`${data.filesBaseURL}/${file_name}`); + pages[current_page] += `* **[${file_name}](${url})**#${i + 1}\n`; + } } - const attachment = new AttachmentBuilder(Buffer.from(content, "utf-8")); - attachment.name = "asset_list.txt"; + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); - await interaction.editReply({ - content: "# Here is the list of assets with their respective identifiers:", - files: [attachment] + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + embed.setTimestamp(); + + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); }catch(error){ await interaction.editReply(`Unable to retrieve file list. \nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index de74d76..2563beb 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -1,4 +1,4 @@ -const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); const {restrict_text} = require("../utils.js"); const {dungeon_list} = require("../dungeon_list.json"); @@ -41,36 +41,99 @@ async function get_dungeon(interaction, client){ } } -async function get_info(interaction, client){ +async function get_info(interaction, _client){ await interaction.reply("This subcommand is in development and cannot be used at this time."); } -async function get_list(interaction, client){ +async function get_list(interaction, _client){ await interaction.deferReply(); const embed = new EmbedBuilder(); embed.setTitle("šŸ° Dungeon List šŸ°"); - let description = "Note that this list is not updated in real time like other lists. This is a pre-list awaiting the GoBattle.io API update.\n"; + const header_description = "Note that this list is not updated in real time like other lists. This is a pre-list awaiting the GoBattle.io API update.\n"; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(dungeon_list.length / max_items_by_pages)); - embed.setDescription(description, {split: false}); + let current_page = -1; + for (let i = 0; i < dungeon_list.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } - for (const dungeon_data of dungeon_list){ - description += `* ${restrict_text(dungeon_data.name || "*Unknow?*", 45)}#${dungeon_data.id}: \`${dungeon_data.min_level || "Unknow?"} šŸ’Ŗ\`\n`; + const dungeon_data = dungeon_list[i]; + pages[current_page] += `* **${restrict_text(dungeon_data.name || "*Unknow?*", 45)}**#${dungeon_data.id}: \`${dungeon_data.min_level || "Unknow?"}šŸ’Ŗ\`\n`; } - embed.setDescription(description, {split: false}); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸ”¢ __Number of dungeons__", value: `> ${dungeon_list.length}`, inline: true}, {name: "> __Min level__", value: "> šŸ’Ŗ", inline: true} ); + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - await interaction.editReply({ - embeds: [embed], + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); } exports.get_dungeon = get_dungeon; diff --git a/src/commands/item.js b/src/commands/item.js index 5bbfe0a..9a146c4 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -1,7 +1,14 @@ -const {SlashCommandBuilder, EmbedBuilder, AttachmentBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType, Emoji, parseEmoji} = require("discord.js"); const {ultra_list} = require("../ultralist.json"); const {restrict_text, sum, format_score} = require("../utils.js"); +const items_map = new Map(); +for (const item_data of ultra_list){ + if (typeof item_data.id == "number"){ + items_map.set(item_data.id, item_data); + } +} + const item_command = new SlashCommandBuilder(); item_command.setName("item"); item_command.setDescription("Command relating to items and other consumables in the game."); @@ -56,25 +63,17 @@ async function get_info(interaction, client){ try{ const item_id = interaction.options.get("item_id")?.value; - let item_info; - for (const ultra of ultra_list){ - if (item_id == ultra.id){ - item_info = ultra; - break; - } - } + const item_info = items_map.get(item_id); if (!item_info){ await interaction.editReply("Sorry, the specified item does not exist or has not yet been indexed by the Gobattle API."); return; } - const attachment = new AttachmentBuilder(__dirname + "/../images/unknown_item.png"); - attachment.name = "item_icon.png"; - - const embed = new EmbedBuilder() - embed.setTitle(restrict_text(item_info.name, 60)); - embed.setThumbnail(`attachment://${attachment.name}`); + const emoji = new Emoji(client, parseEmoji(item_info?.emoji || "<:item_91:1267565851536789554>")); + const embed = new EmbedBuilder(); + embed.setTitle(`${emoji} ${restrict_text(item_info.name, 60)}`); + embed.setThumbnail(emoji.imageURL()); embed.setDescription(item_info.description ? restrict_text(item_info.description, 250) : "_Description Unknown_"); embed.setColor(0x500000); embed.addFields( @@ -98,7 +97,6 @@ async function get_info(interaction, client){ embed.setFooter({text: "Source: Sage of the Ivy"}); await interaction.editReply({ - files: [attachment], embeds: [embed], content: "This feature is under development, information may not be accurate." }); @@ -130,35 +128,98 @@ async function get_ultrarare_drops(interaction, client){ const drops_ratio = total_ultrarare / (total || 1); const embed = new EmbedBuilder(); + embed.setTitle("Current average chance of getting an Ultrarare from a chest."); - let description = `Results are calculated based on ${total} samples (chest opened by players) including ${total_ultrarare} ultrarare drops.\n`; + const chest_emoji = new Emoji(client, parseEmoji("<:item_760:1267561884610203650>")); + const header_description = `Results are calculated based on ${total} samples (*chest opened by players ${chest_emoji}*) including ${total_ultrarare} ultrarare drops.\n`; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(values.length / max_items_by_pages)); + const drops_entries = Object.entries(drops); - for (const [key, value] of drops_entries){ - let name_ultra; - for (const ultra_data of ultra_list){ - if (ultra_data.id && ultra_data.id == key){ - name_ultra = ultra_data.name; - break; - } + + let current_page = -1; + for (let i = 0; i < values.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; } - description += `* ${restrict_text(name_ultra || "*Unknow?*", 45)}#${key}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%)\`\n`; + const [key, value] = drops_entries[i]; + const ultra_info = items_map.get(parseInt(key, 10)); + const emoji = new Emoji(client, parseEmoji(ultra_info?.emoji || "<:item_91:1267565851536789554>")); + pages[current_page] += `* ${emoji} **${restrict_text(ultra_info?.name || "*Unknow?*", 45)}**#${key}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%)\`\n`; } - embed.setDescription(description, {split: false}); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> 🌟 __Ultrarare__", value: `> ${(drops_ratio * 100).toPrecision(2)}%`, inline: true}, {name: "> šŸ“¦ __Other__", value: `> ${((1 - drops_ratio) * 100).toPrecision(2)}%`, inline: true} ); + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - await interaction.editReply({ + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ content: "Special mention to _Sage of the Ivy_ for providing some metadata on most of the game's ultrarares while waiting for the GoBattle API update!\nThanks to him!", - embeds: [embed] + embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); }catch(error){ await interaction.editReply(`Unable to generate template.\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/ranking.js b/src/commands/ranking.js index b102511..4f760c4 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -1,5 +1,13 @@ -const {EmbedBuilder, SlashCommandBuilder} = require("discord.js"); +const {EmbedBuilder, SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType, Emoji, parseEmoji} = require("discord.js"); const {restrict_text, get_level, get_level_adventurer, format_score, format_speed_run_time} = require("../utils.js"); +const head_list = require("../head_list.json"); + +const heads_map = new Map(); +for (const head_data of head_list){ + if (typeof head_data.id == "number"){ + heads_map.set(head_data.id, head_data); + } +} const ranking_command = new SlashCommandBuilder(); ranking_command.setName("ranking"); @@ -7,90 +15,36 @@ ranking_command.setDescription("Commands relating to rankings."); ranking_command.addSubcommand((subcommand) => { subcommand.setName("king"); subcommand.setDescription("Get King ranking"); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); ranking_command.addSubcommand((subcommand) => { subcommand.setName("weekly"); subcommand.setDescription("Get Weekly ranking"); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); ranking_command.addSubcommand((subcommand) => { subcommand.setName("monthly"); subcommand.setDescription("Get Monthly ranking"); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); ranking_command.addSubcommand((subcommand) => { subcommand.setName("overall"); subcommand.setDescription("Get Overall (EXP) ranking"); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); ranking_command.addSubcommand((subcommand) => { subcommand.setName("adventurer"); subcommand.setDescription("Get Adventurer ranking"); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); ranking_command.addSubcommand((subcommand) => { subcommand.setName("relichunter"); subcommand.setDescription("Get Relic Hunter ranking"); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); @@ -105,15 +59,6 @@ ranking_command.addSubcommand((subcommand) => { return option; }); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); @@ -126,13 +71,13 @@ async function get_ranking(interaction, client){ await get_king(interaction, client); break; case "weekly": - await get_weekly(interaction, client); + await get_general(interaction, client, "weekly"); break; case "monthly": - await get_monthly(interaction, client); + await get_general(interaction, client, "monthly"); break; case "overall": - await get_overall(interaction, client); + await get_general(interaction, client, "overall"); break; case "adventurer": await get_adventurer(interaction, client); @@ -151,8 +96,6 @@ async function get_ranking(interaction, client){ async function get_king(interaction, client){ await interaction.deferReply(); - const max_fields = interaction.options.get("max_fields")?.value || 20; - try{ const response = await fetch("https://gobattle.io/api.php/valdoranking?platform=Discord&ud="); @@ -164,89 +107,106 @@ async function get_king(interaction, client){ const data = await response.json(); const embed = new EmbedBuilder(); - embed.setTitle("šŸ‘‘ King Ranking šŸ‘‘"); - let description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command.\n"; const ranking = data?.ranking; - for (let i = 0; i < max_fields && i < ranking.length; i++){ + + const header_description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command.\n"; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(ranking.length / max_items_by_pages)); + + let current_page = -1; + for (let i = 0; i < ranking.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const field = ranking[i]; - description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${format_score(field?.reputation)} REP\`\n`; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); + pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.reputation)} REP\`\n`; } - embed.setDescription(description); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸ—“ __Week__", value: `> ${data?.week || "_Unknown?_"}`, inline: true}, {name: "> šŸ—“ __Year__", value: `> ${data?.year || "_Unknown?_"}`, inline: true} ); - embed.setTimestamp(); + const nb_pages = pages.length || 1; - await interaction.editReply({ - embeds: [embed] - }); - }catch(error){ - await interaction.editReply(`Failed to generate King ranking...\nContact ${client.application.owner} to resolve this issue.`); - console.error(error); - } -} - -async function get_weekly(interaction, client){ - await interaction.deferReply(); - - const max_fields = interaction.options.get("max_fields")?.value || 20; + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + embed.setTimestamp(); - try{ - const response = await fetch("https://gobattle.io/api.php/stats/weekly?platform=Discord&ud="); + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); - if (!response.ok){ - await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); - return; - } + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); - const data = await response.json(); + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); - const embed = new EmbedBuilder(); + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] + }); - embed.setTitle("šŸ”„ Weekly Ranking šŸ”„"); + function collector_filter(m){ + const result = m.user.id == interaction.user.id; - let description = ""; - for (let i = 0; i < max_fields && i < data.length; i++){ - const field = data[i]; - const exp = parseInt(field?.experience, 10); - const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}āš”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; } - embed.setDescription(description); - - embed.addFields( - {name: "> __Coins__", value: "> šŸŖ™", inline: true}, - {name: "> __Kills__", value: "> āš”ļø", inline: true}, - {name: "> __Deaths__", value: "> šŸ’€", inline: true}, - {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, - {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} - ); - - embed.setTimestamp(); + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } - await interaction.editReply({ - embeds: [embed] - }); + await button_interaction_logic(response_interaction); }catch(error){ - await interaction.editReply(`Failed to generate Weekly ranking...\nContact ${client.application.owner} to resolve this issue.`); + await interaction.editReply(`Failed to generate King ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); } } -async function get_monthly(interaction, client){ +async function get_general(interaction, client, type){ await interaction.deferReply(); - const max_fields = interaction.options.get("max_fields")?.value || 20; - try{ - const response = await fetch("https://gobattle.io/api.php/stats/monthly?platform=Discord&ud="); + const response = await fetch(`https://gobattle.io/api.php/stats/${type}?platform=Discord&ud=`); if (!response.ok){ await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); @@ -257,81 +217,112 @@ async function get_monthly(interaction, client){ const embed = new EmbedBuilder(); - embed.setTitle("šŸ”„ Monthly Ranking šŸ”„"); + switch (type){ + case "weekly": + embed.setTitle("šŸ”„ Weekly Ranking šŸ”„"); + break; + case "monthly": + embed.setTitle("šŸ”„ Monthly Ranking šŸ”„"); + break; + case "overall": + embed.setTitle("šŸ”„ Overall Ranking (EXP) šŸ”„"); + break; + default: + embed.setTitle("šŸ”„ General Ranking šŸ”„"); + } - let description = ""; - for (let i = 0; i < max_fields && i < data.length; i++){ + const header_description = ""; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + + let current_page = -1; + for (let i = 0; i < data.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const field = data[i]; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); const exp = parseInt(field?.experience, 10); const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}āš”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; + pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}šŸ—”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; } - embed.setDescription(description); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> __Coins__", value: "> šŸŖ™", inline: true}, - {name: "> __Kills__", value: "> āš”ļø", inline: true}, + {name: "> __Kills__", value: "> šŸ—”ļø", inline: true}, {name: "> __Deaths__", value: "> šŸ’€", inline: true}, {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} ); - embed.setTimestamp(); + const nb_pages = pages.length || 1; - await interaction.editReply({ - embeds: [embed] - }); - }catch(error){ - await interaction.editReply(`Failed to generate Monthly ranking...\nContact ${client.application.owner} to resolve this issue.`); - console.error(error); - } -} + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + embed.setTimestamp(); -async function get_overall(interaction, client){ - await interaction.deferReply(); + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); - const max_fields = interaction.options.get("max_fields")?.value || 20; + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); - try{ - const response = await fetch("https://gobattle.io/api.php/stats/overall?platform=Discord&ud="); + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); - if (!response.ok){ - await interaction.editReply(`Impossible to recover the ranking. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); - return; - } - - const data = await response.json(); - - const embed = new EmbedBuilder(); + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] + }); - embed.setTitle("šŸ”„ Overall Ranking (EXP) šŸ”„"); + function collector_filter(m){ + const result = m.user.id == interaction.user.id; - let description = ""; - for (let i = 0; i < max_fields && i < data.length; i++){ - const field = data[i]; - const exp = parseInt(field?.experience, 10); - const level = get_level(exp); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}āš”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; } - embed.setDescription(description); - - embed.addFields( - {name: "> __Coins__", value: "> šŸŖ™", inline: true}, - {name: "> __Kills__", value: "> āš”ļø", inline: true}, - {name: "> __Deaths__", value: "> šŸ’€", inline: true}, - {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, - {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} - ); - - embed.setTimestamp(); + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } - await interaction.editReply({ - embeds: [embed] - }); + await button_interaction_logic(response_interaction); }catch(error){ - await interaction.editReply(`Failed to generate Overall ranking...\nContact ${client.application.owner} to resolve this issue.`); + await interaction.editReply(`Failed to generate Weekly ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); } } @@ -339,8 +330,6 @@ async function get_overall(interaction, client){ async function get_adventurer(interaction, client){ await interaction.deferReply(); - const max_fields = interaction.options.get("max_fields")?.value || 20; - try{ const response = await fetch("https://gobattle.io/api.php/v1/stats/ranking/adventurer?platform=Discord&ud="); @@ -355,20 +344,87 @@ async function get_adventurer(interaction, client){ embed.setTitle("🤠 Adventurer Ranking 🤠"); - let description = ""; - for (let i = 0; i < max_fields && i < data.length; i++){ + const header_description = ""; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + + let current_page = -1; + for (let i = 0; i < data.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const field = data[i]; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); const level = get_level_adventurer(field?.score); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${format_score(field?.score)} XP (${level} LVL)\`\n`; + pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; } - embed.setDescription(description); - + current_page = 1; + embed.setDescription(pages[current_page - 1]); + + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - await interaction.editReply({ - embeds: [embed] + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); }catch(error){ await interaction.editReply(`Failed to generate Adventurer ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -378,8 +434,6 @@ async function get_adventurer(interaction, client){ async function get_relic_hunter(interaction, client){ await interaction.deferReply(); - const max_fields = interaction.options.get("max_fields")?.value || 20; - try{ const response = await fetch("https://gobattle.io/api.php/v1/stats/ranking/relichunter?platform=Discord&ud="); @@ -394,20 +448,87 @@ async function get_relic_hunter(interaction, client){ embed.setTitle("šŸŗ Relic Hunter Ranking šŸŗ"); - let description = ""; - for (let i = 0; i < max_fields && i < data.length; i++){ + const header_description = ""; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + + let current_page = -1; + for (let i = 0; i < data.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const field = data[i]; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); const level = get_level_adventurer(field?.score); - description += `${i + 1}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${format_score(field?.score)} XP (${level} LVL)\`\n`; + pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; } - embed.setDescription(description); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); + + const nb_pages = pages.length || 1; + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - await interaction.editReply({ + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); }catch(error){ await interaction.editReply(`Failed to generate Hunter Ranking ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -418,7 +539,6 @@ async function get_speedrun(interaction, client){ await interaction.deferReply(); const dungeon_id = interaction.options.get("dungeon_id")?.value; - const max_fields = interaction.options.get("max_fields")?.value || 20; try{ const response = await fetch(`https://gobattle.io/api.php/v1/stats/speedrun/${dungeon_id}`); @@ -434,27 +554,95 @@ async function get_speedrun(interaction, client){ embed.setTitle(`šŸ Speedrun Ranking (${data.name || "_Unknown?_"}) šŸ`); - let description = "Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!\n"; const ranking = data.ranking; + + const header_description = "Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!\n"; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(ranking.length / max_items_by_pages)); + const list_size = ranking.length; - for (var i = 0; i < max_fields && i < list_size; i++){ + let current_page = -1; + for (let i = 0; i < list_size; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const field = ranking[i]; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); const time = format_speed_run_time(field?.time); - description += `${field?.rank}. ${restrict_text(field?.nick, 20)}#${field?.id}: \`${time}\`\n`; + pages[current_page] += `${field?.rank}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${time}\`\n`; } - embed.setDescription(description); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, {name: "> šŸ·ļø __Dungeon ID__", value: `> ${dungeon_id}`, inline: true} ); + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - await interaction.editReply({ - embeds: [embed] + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); }catch(error){ await interaction.editReply(`Failed to generate Speedrun ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/server.js b/src/commands/server.js index b953408..a34edfa 100644 --- a/src/commands/server.js +++ b/src/commands/server.js @@ -1,4 +1,4 @@ -const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); const {restrict_text} = require("../utils.js"); const server_command = new SlashCommandBuilder(); @@ -28,15 +28,6 @@ server_command.addSubcommand((subcommand) => { return option; }); - subcommand.addNumberOption((option) => { - option.setName("max_fields"); - option.setDescription("Maximum number of fields."); - option.setMinValue(1); - option.setMaxValue(50); - option.setRequired(false); - - return option; - }); return subcommand; }); @@ -57,7 +48,6 @@ async function get_list(interaction, client){ const platform = interaction.options.get("platform")?.value || "Web"; const version = interaction.options.get("version")?.value || 115; - const max_fields = interaction.options.get("max_fields")?.value || 20; try{ const response = await fetch(`https://gobattle.io/api.php/bootstrap/${version}?platform=${platform}&ud=`); @@ -81,9 +71,17 @@ async function get_list(interaction, client){ embed.setTitle("šŸ–„ļø Server List šŸ–„ļø"); - let description = ""; - const list_size = server_list.length; - for (var i = 0; i < max_fields && i < list_size; i++){ + const header_description = ""; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(server_list.length / max_items_by_pages)); + + let current_page = -1; + for (var i = 0; i < server_list.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + const field = server_list[i]; let is_online; @@ -97,10 +95,11 @@ async function get_list(interaction, client){ is_online = false; } - description += `* ${restrict_text(field?.friendlyName, 25)}#${field?.id}: \`${field?.version} šŸ”\` \`${field?.admin} šŸ› ļø\` \`${(is_online ? "Online" : "Down")} 🌐\`\n`; + pages[current_page] += `* **${restrict_text(field?.friendlyName, 25)}**#${field?.id}: \`${field?.version} šŸ”\` \`${field?.admin} šŸ› ļø\` \`${(is_online ? "Online" : "Down")} 🌐\`\n`; } - embed.setDescription(description); + current_page = 1; + embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸŽ® __Platform__", value: `> ${platform}`, inline: true}, @@ -110,11 +109,66 @@ async function get_list(interaction, client){ {name: "> __Server status__", value: "> 🌐", inline: true} ); + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - await interaction.editReply({ - embeds: [embed] + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); }catch(error){ await interaction.editReply(`Unable to retrieve server list.\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/user.js b/src/commands/user.js index b079b4a..8142bc4 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -1,5 +1,5 @@ const {EmbedBuilder, AttachmentBuilder, SlashCommandBuilder} = require("discord.js"); -const {restrict_text} = require("../utils.js"); +const {restrict_text, is_my_developer} = require("../utils.js"); const user_command = new SlashCommandBuilder(); user_command.setName("user"); @@ -93,7 +93,7 @@ async function get_info(interaction, client){ const user_id = interaction.options.get("user_id")?.value; const public = false; - if (interaction.user != client.application.owner && !public){ + if (!is_my_developer(client, interaction.user) && !public){ await interaction.editReply(`Sorry, user _#${user_id}_ does not wish to expose this information to the public.`); return; } diff --git a/src/head_list.json b/src/head_list.json new file mode 100644 index 0000000..e2e3914 --- /dev/null +++ b/src/head_list.json @@ -0,0 +1,292 @@ +[ + { + "id": 0, + "name": "Grey Head", + "emoji": "<:heads_item_0:1267602598668668949>" + }, + { + "id": 1, + "name": "Black Head", + "emoji": "<:heads_item_1:1267602583241883760>" + }, + { + "id": 2, + "name": "Blue Head", + "emoji": "<:heads_item_2:1267602568696172574>" + }, + { + "id": 3, + "name": "Dark Blue Head", + "emoji": "<:heads_item_3:1267602554250854531>" + }, + { + "id": 4, + "name": "Emerald Head", + "emoji": "<:heads_item_4:1267602540938137701>" + }, + { + "id": 5, + "name": "Fire Head", + "emoji": "<:heads_item_5:1267602525268344984>" + }, + { + "id": 6, + "name": "Skin color 1 Head", + "emoji": "<:heads_item_6:1267602507593416714>" + }, + { + "id": 7, + "name": "Skin color 2 Head", + "emoji": "<:heads_item_7:1267602488907927666>" + }, + { + "id": 8, + "name": "Skin color 3 Head", + "emoji": "<:heads_item_8:1267602477516062973>" + }, + { + "id": 9, + "name": "Skin color 4 Head", + "emoji": "<:heads_item_9:1267602463813533718>" + }, + { + "id": 10, + "name": "Gold Head", + "emoji": "<:heads_item_10:1267602450345623634>" + }, + { + "id": 11, + "name": "Green Head", + "emoji": "<:heads_item_11:1267602439142375616>" + }, + { + "id": 12, + "name": "Light Blue Head", + "emoji": "<:heads_item_12:1267602421878620344>" + }, + { + "id": 13, + "name": "Orange Head", + "emoji": "<:heads_item_13:1267602405952847915>" + }, + { + "id": 14, + "name": "Pink Head", + "emoji": "<:heads_item_14:1267602392074158130>" + }, + { + "id": 15, + "name": "Purple Head", + "emoji": "<:heads_item_15:1267602376085213327>" + }, + { + "id": 16, + "name": "Red Head", + "emoji": "<:heads_item_16:1267602359236690135>" + }, + { + "id": 17, + "name": "White Head", + "emoji": "<:heads_item_17:1267602315221794887>" + }, + { + "id": 18, + "name": "Yellow Head", + "emoji": "<:heads_item_18:1267602296817061938>" + }, + { + "id": 19, + "name": "Cat Knight", + "emoji": "<:heads_item_19:1267602283646947479>" + }, + { + "id": 20, + "name": "Sophie", + "emoji": "<:heads_item_20:1267602270938202184>" + }, + { + "id": 21, + "name": "Hiroto", + "emoji": "<:heads_item_21:1267602260284801157>" + }, + { + "id": 22, + "name": "Mary", + "emoji": "<:heads_item_22:1267602246141481174>" + }, + { + "id": 23, + "name": "Happy", + "emoji": "<:heads_item_23:1267602232703062090>" + }, + { + "id": 24, + "name": "Rhino", + "emoji": "<:heads_item_24:1267602215749550232>" + }, + { + "id": 25, + "name": "M.R. Dalmau", + "emoji": "<:heads_item_25:1267602198662090873>" + }, + { + "id": 26, + "name": "Cyclops", + "emoji": "<:heads_item_26:1267602185202438307>" + }, + { + "id": 27, + "name": "Monk", + "emoji": "<:heads_item_27:1267602170757382204>" + }, + { + "id": 28, + "name": "Santa", + "emoji": "<:heads_item_28:1267602156710658180>" + }, + { + "id": 29, + "name": "Fire Skull", + "emoji": "<:heads_item_29:1267602142454349824>" + }, + { + "id": 30, + "name": "Halloween", + "emoji": "<:heads_item_30:1267602129808392222>" + }, + { + "id": 31, + "name": "Mr. Chicken", + "emoji": "<:heads_item_31:1267602118135517244>" + }, + { + "id": 32, + "name": "Karateka", + "emoji": "<:heads_item_32:1267602101886910536>" + }, + { + "id": 33, + "name": "Mr. Chief", + "emoji": "<:heads_item_33:1267602087588397197>" + }, + { + "id": 34, + "name": "Julie", + "emoji": "<:heads_item_34:1267602074674139348>" + }, + { + "id": 35, + "name": "Gold", + "emoji": "<:heads_item_35:1267602061290373181>" + }, + { + "id": 36, + "name": "Samantha", + "emoji": "<:heads_item_36:1267602048078315663>" + }, + { + "id": 37, + "name": "Bruce", + "emoji": "<:heads_item_37:1267602035323179048>" + }, + { + "id": 38, + "name": "Mr. Strong", + "emoji": "<:heads_item_38:1267602021771513907>" + }, + { + "id": 39, + "name": "Neena", + "emoji": "<:heads_item_39:1267602007015817237>" + }, + { + "id": 40, + "name": "Kira", + "emoji": "<:heads_item_40:1267601987537473577>" + }, + { + "id": 41, + "name": "Scarlett", + "emoji": "<:heads_item_41:1267601974589788200>" + }, + { + "id": 42, + "name": "Beatrice", + "emoji": "<:heads_item_42:1267601960689991720>" + }, + { + "id": 43, + "name": "Sigrid", + "emoji": "<:heads_item_43:1267601945187582053>" + }, + { + "id": 44, + "name": "Nira", + "emoji": "<:heads_item_44:1267601933259112630>" + }, + { + "id": 45, + "name": "Kaneda", + "emoji": "<:heads_item_45:1267601919694606428>" + }, + { + "id": 46, + "name": "Kiroto", + "emoji": "<:heads_item_46:1267601907552096296>" + }, + { + "id": 47, + "name": "Ryo", + "emoji": "<:heads_item_47:1267601895329894440>" + }, + { + "id": 48, + "name": "Storm", + "emoji": "<:heads_item_48:1267601883954938059>" + }, + { + "id": 49, + "name": "Ra", + "emoji": "<:heads_item_49:1267601871669956850>" + }, + { + "id": 50, + "name": "Ari", + "emoji": "<:heads_item_50:1267601856859734108>" + }, + { + "id": 51, + "name": "Knight", + "emoji": "<:heads_item_51:1267601842544705567>" + }, + { + "id": 52, + "name": "Luna", + "emoji": "<:heads_item_52:1267601828988719227>" + }, + { + "id": 53, + "name": "Bea", + "emoji": "<:heads_item_53:1267601810990956668>" + }, + { + "id": 54, + "name": "Atria", + "emoji": "<:heads_item_54:1267601798097666192>" + }, + { + "id": 55, + "name": "Black Knight", + "emoji": "<:heads_item_55:1267601783400956037>" + }, + { + "id": 56, + "name": "Demonic black knight", + "emoji": "<:heads_item_56:1267601769538650387>" + }, + { + "id": 57, + "name": "Phamy", + "emoji": "<:heads_item_57:1267601753042456697>" + } +] \ No newline at end of file diff --git a/src/index.js b/src/index.js index f6af04c..81b966d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ -const {Client, Partials, IntentsBitField, Routes, ActivityType} = require("discord.js"); -const {get_first_chat_channel} = require("./utils.js"); +const {Client, Partials, IntentsBitField, Routes, ActivityType, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ActionRowBuilder, TextInputBuilder} = require("discord.js"); +const {get_first_chat_channel, is_my_developer, restrict_text} = require("./utils.js"); +const {ultra_list} = require("./ultralist.json"); const {get_ranking, ranking_command} = require("./commands/ranking.js"); const {get_help, help_command} = require("./commands/help.js"); @@ -58,7 +59,7 @@ client.on("ready", async (event) => { await client.application.fetch(); try{ - client.rest.put( + await client.rest.put( Routes.applicationCommands(client.user.id), {body: global_commands} ); @@ -77,6 +78,25 @@ client.on("ready", async (event) => { console.error(error); } } + + + //test + /*const guild = await client.guilds.fetch("380588354934276097"); + + guild.channels.cache.forEach((channel) => { + if (channel.type == 0){ + console.log(`${channel.name} (${channel.type}) - ${channel.id}`); + } + }); + + const gg = await client.channels.fetch("1148723323761606809"); + + gg.messages.fetch({limit: 100}).then(messages => { + console.log(`Received ${messages.size} messages`); + //Iterate through the messages here with the variable "messages". + messages.forEach(message => console.log(message.author.globalName, ": ", message.content)) + }) +*/ }); client.on("guildCreate", async (guild) => { @@ -158,26 +178,40 @@ client.on("messageCreate", async (msg) => { } try{ + /* + if (msg.guildId == "1235330175449825310"){ + const gg = await client.channels.fetch("380588355403907082"); + await gg.send("Kuwazy told me to send you this message:\n" + msg.content); + } + */ const command = msg.content.trim().toLowerCase(); // Quick commands for development. switch (command){ case "!gb_bot_guilds": - if (msg.author != client.application.owner){ - await msg.reply(`You are not ${client.application.owner}, you do not have the right to use this command.`); + if (!is_my_developer(client, msg.author)){ + await msg.reply("You do not have permission to use this command."); return; } const guilds = client.guilds.cache; let message = "# List of guilds I am in:\n\n"; - let i = 0; for (const guild of guilds.values()){ - i++; - message += `**#${i}** ${guild.name}: \`${guild.id}\`,\n`; + message += `* ${guild.name}: \`${guild.id}\`,\n`; } message += `(${guilds.size} Guilds)`; await msg.reply(message); + break; + case "!get_off_this_server": + if (!is_my_developer(client, msg.author)){ + await msg.reply("You do not have permission to use this command."); + return; + } + + await msg.reply("Well, I'm leaving this guild because I seem to be disturbing... STFU."); + await msg.guild.leave(); + break; } }catch (error){ diff --git a/src/ultralist.json b/src/ultralist.json index f521399..8a3ec49 100644 --- a/src/ultralist.json +++ b/src/ultralist.json @@ -6,10 +6,11 @@ "tickets": 500, "drops": "Sky's Prison", "id": 155, - "description": "This object does: It restores 50 points of your fatigue and boosts your defense by 50% for 40 seconds.", + "description": "It restores 50 points of your fatigue and boosts your defense by 50% for 40 seconds.", "uses": 3, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_506:1267561503029067847>" }, { "name": "Gobattle Ring", @@ -19,7 +20,8 @@ "description": "Increases 50% of your attack, defense, speed, and jump for 30 seconds. Gives Invincibility for 30 seconds.", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_512:1267561027055128599>" }, { "name": "Firebreath Ring", @@ -29,7 +31,8 @@ "description": "Invokes a fire attack, ring has 10 uses. The attack varies by the users attack. Sometimes, it will work with blue ring.", "uses": 10, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_502:1267561518808039454>" }, { "name": "Blue Ring", @@ -39,7 +42,8 @@ "description": "Increases your power attack 3x for 30 seconds", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_520:1267561452672122972>" }, { "name": "Red Ring", @@ -49,7 +53,8 @@ "description": "Regenerates your health 8 times faster for 1 minute", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_515:1267561468811935774>" }, { "name": "Bloodmoon Ring", @@ -59,7 +64,8 @@ "description": "A mystical ring that, when activated, conjures four fearsome bats with crimson eyes, serving as loyal companions and formidable allies under the blood moon's eerie light.", "uses": 3, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_510:1267561480509718548>" }, { "name": "Anti Freezing Glove", @@ -69,7 +75,8 @@ "description": "Freeze protection for 60 seconds, your speed decreases 20%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_431:1267569371455160390>" }, { "name": "Instant Strength Glove", @@ -79,7 +86,8 @@ "description": "Increases your power attack 20% during 40 seconds, your defense decreases 20%", "uses": 7, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_435:1267569402941800468>" }, { "name": "Epic Strength Glove", @@ -89,7 +97,8 @@ "description": "Increases your power attack 50% during 40 seconds, your defense decreases 30%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_441:1267569470382149804>" }, { "name": "Gravity Feather", @@ -99,7 +108,8 @@ "description": "Your gravity decreases 50% during 30 seconds, your defense decreases 20%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_754:1267573693458944010>" }, { "name": "Team Gravity Feather", @@ -109,7 +119,8 @@ "description": "For a duration of 40 seconds, the gravity affecting both you and your group members will decrease by 50%. Additionally, your attack with increase by 20%.", "uses": 3, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_749:1267561874350805043>" }, { "name": "Hermes Boots", @@ -119,7 +130,8 @@ "description": "Increases your speed 50% for 40 seconds", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_133:1267565695907139708>" }, { "name": "Speed Boots", @@ -129,7 +141,8 @@ "description": "Increases speed 20%, your defense decreases 20%", "uses": 7, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_139:1267565745345134662>" }, { "name": "Maximum Speed Boots", @@ -139,7 +152,8 @@ "description": "Increases speed 75% during 40 seconds, your defense decreases 30%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_140:1267565753297666099>" }, { "name": "Normal Invisibility Cloak", @@ -149,7 +163,8 @@ "description": "Makes you invisible for 20 seconds", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_235:1267564036426436791>" }, { "name": "Extreme Invisibility Cloak", @@ -159,7 +174,8 @@ "description": "Makes you invisible for 60 seconds, decreases your attack 20%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_264:1267564565525299301>" }, { "name": "Instant Defense Cloak", @@ -169,7 +185,8 @@ "description": "Increases your defense 50% during 40 seconds, your power attack decreases 20%", "uses": 7, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_236:1267564043107827744>" }, { "name": "Epic Instant Defense Cloak", @@ -179,17 +196,19 @@ "description": "Increases defense by 80%, attack decreases 40%.", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_262:1267564548672721007>" }, { - "name": "Health Regeneration Cloak", + "name": "Normal Health Regeneration Cloak", "tickets": 3000, "drops": "Sawmill", "id": 30, "description": "Regenerates health points 200% faster during 40", "uses": 7, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_240:1267564050737532929>" }, { "name": "Maximum Health Regeneration Cloak", @@ -199,7 +218,8 @@ "description": "Regenerates health points 400% faster during 60 seconds, your power attack decreases 20%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_260:1267564530196680795>" }, { "name": "Normal Venom Protection Cloak", @@ -209,17 +229,19 @@ "description": "Protects you from venom during 40 seconds", "uses": 7, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_242:1267564066763706419>" }, { - "name": "Extraordinary Venom Protection Cloak", + "name": "Extreme Venom Protection Cloak", "tickets": 2000, "drops": "Spider's Lair", "id": 36, "description": "Protects you from venom during 60 seconds, your power attack decreases 20%", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_261:1267564540917186580>" }, { "name": "Maximum Fire Protection Cloak", @@ -229,7 +251,8 @@ "description": "Protects you from fire during 60 seconds.", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_268:1267564588644307005>" }, { "name": "Fire Protection Cloak", @@ -239,7 +262,8 @@ "description": "It protects you from fire during 30 seconds.", "uses": 7, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_249:1267564125827895436>" }, { "name": "Breath Helmet", @@ -249,7 +273,8 @@ "description": "You can breathe underwater 4x the initial time (40 seconds total)", "uses": 5, "is_relic": false, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_267:1267564398747320432>" }, { "name": "Lava Armor Protector", @@ -259,17 +284,19 @@ "description": "Protects you from lava damage during 60 seconds, decreases 20% of your velocity.", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_251:1267568057325322321>" }, { - "name": "Fire Enchantment", + "name": "Anti Fire Enchantment", "tickets": 100, "drops": "All", "id": 116, "description": "Stops fire on people around you and yourself, not repairable", "uses": 5, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_388:1267565242775371887>" }, { "name": "Epic Invicibility Potion", @@ -279,7 +306,8 @@ "description": "Makes you invincible (you don't take damage besides from debuffs) for 40 seconds", "uses": 1, "is_relic": false, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_551:1267570796818206902>" }, { "name": "Firebreath's Blood", @@ -289,7 +317,8 @@ "description": "Invokes fire attack. Not repairable, one use.", "uses": 1, "is_relic": false, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_745:1267573567013519401>" }, { "name": "Special Halloween Broom", @@ -299,7 +328,8 @@ "description": "You can use this broom to fly for 10 minutes. Linked to Halloween World. If you dismount or you leave Halloween world, the object will disappear.", "uses": 1, "is_relic": false, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_761:1267573805639929936>" }, { "name": "Sleigh Enchantment", @@ -309,7 +339,8 @@ "description": "This enchantment invokes a flying sleigh that you can use as a mount for 10 minutes. You cannot use this object inside a dungeon. Bound object.", "uses": 1, "is_relic": false, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_395:1267569075194953868>" }, { "name": "Special Christmas Hat", @@ -319,7 +350,8 @@ "description": "You can use this hat to fly with a sleigh like Santa for 10 minutes. This object is linked to Winter Wonderland. If you dismount or you leave Winterland, it will disappear.", "uses": 1, "is_relic": false, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_764:1267563728409333873>" }, { "name": "Peppermint Strike", @@ -329,7 +361,8 @@ "description": "Unleash the Peppermint Strike, a sweet and powerful attack that swirls through adversaries with the refreshing force of a candy cane breeze.", "uses": 3, "is_relic": false, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_801:1267574341202088118>" }, { "name": "Dice of Destiny", @@ -339,7 +372,8 @@ "description": "Increases your luck 1%", "uses": null, "is_relic": true, - "max_in_inventory": 16 + "max_in_inventory": 16, + "emoji": "<:item_810:1267563864900239381>" }, { "name": "Iron Heart", @@ -349,7 +383,8 @@ "description": "Increases max health 2%", "uses": null, "is_relic": true, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_811:1267563849608069120>" }, { "name": "Rejuvenation Gem", @@ -359,7 +394,8 @@ "description": "Increases regeneration 2%", "uses": null, "is_relic": true, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_812:1267563856541126686>" }, { "name": "Inferno Touch", @@ -369,7 +405,8 @@ "description": "1% chance of burning effect (PVP)", "uses": null, "is_relic": true, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_813:1267563837427552337>" }, { "name": "Greed's Grip", @@ -379,17 +416,8 @@ "description": "Attract coins close to you", "uses": null, "is_relic": true, - "max_in_inventory": null - }, - { - "name": "Flying Skill", - "tickets": null, - "drops": null, - "id": 198, - "description": null, - "uses": null, - "is_relic": null, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_814:1267574519376384122>" }, { "name": "Dwarf's Strength", @@ -399,7 +427,8 @@ "description": "Increases reach of Axes 8%", "uses": null, "is_relic": true, - "max_in_inventory": 16 + "max_in_inventory": 16, + "emoji": "<:item_815:1267574531015573524>" }, { "name": "Talisman of the Phoenix", @@ -409,7 +438,8 @@ "description": null, "uses": null, "is_relic": true, - "max_in_inventory": null + "max_in_inventory": null, + "emoji": "<:item_798:1267574301750722651>" }, { "name": "Flying Skill", @@ -419,7 +449,8 @@ "description": "Gives you the skill to dash vertically with your wings", "uses": null, "is_relic": true, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_820:1267560357895868529>" }, { "name": "Dodge Charm", @@ -429,7 +460,8 @@ "description": "1% chance of dodging any attack", "uses": null, "is_relic": true, - "max_in_inventory": 1 + "max_in_inventory": 1, + "emoji": "<:item_817:1267574569955233802>" }, { "name": "Gem Dust", @@ -439,7 +471,8 @@ "description": "Up to 5 dyams (+ Luck) per gem in chests", "uses": null, "is_relic": true, - "max_in_inventory": 25 + "max_in_inventory": 25, + "emoji": "<:item_819:1267560608186761359>" } ] } diff --git a/src/utils.js b/src/utils.js index 885929b..40574dd 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,4 @@ -const {ChannelType, PermissionFlagsBits} = require("discord.js"); +const {ChannelType, PermissionFlagsBits, User, Team} = require("discord.js"); function restrict_text(str, nb){ if (str.length > nb){ @@ -97,6 +97,26 @@ async function send_echo(client, message){ } } +function is_my_developer(client, user){ + if (client.application.owner instanceof User){ + return user == client.application.owner; + }else if (client.application.owner instanceof Team){ + let is_member = false; + + const members_id = client.application.owner.members.keys(); + + for (const user_id of members_id){ + is_member = user_id == user.id; + + if (is_member){ + return true; + } + } + } + + return false; +} + exports.restrict_text = restrict_text; exports.format_score = format_score; exports.format_speed_run_time = format_speed_run_time; @@ -107,3 +127,4 @@ exports.get_utf_time_next_king = get_utf_time_next_king; exports.sum = sum; exports.send_echo = send_echo; exports.get_first_chat_channel = get_first_chat_channel; +exports.is_my_developer = is_my_developer; \ No newline at end of file From 64af68c4746a09278df37de11d72d727d7458087 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Sat, 3 Aug 2024 01:22:04 +0200 Subject: [PATCH 31/54] add login --- .gitignore | 6 + Dockerfile | 4 +- README.md | 6 +- docker-compose.yml | 4 +- package.json | 1 + src/commands/dungeon.js | 2 +- src/commands/item.js | 28 +- src/commands/ranking.js | 76 ++--- src/commands/server.js | 2 +- src/commands/user.js | 588 +++++++++++++++++++++++++++++++++--- src/database/database.js | 114 +++++++ src/database/schema.sql | 20 ++ src/head_list.json | 116 +++---- src/images/unknown_item.png | Bin 468 -> 0 bytes src/index.js | 242 +++++++++------ src/level_emojis.json | 22 ++ src/ultralist.json | 106 +++---- src/utils.js | 65 +++- 18 files changed, 1099 insertions(+), 303 deletions(-) create mode 100644 src/database/database.js create mode 100644 src/database/schema.sql delete mode 100644 src/images/unknown_item.png create mode 100644 src/level_emojis.json diff --git a/.gitignore b/.gitignore index c6bba59..8056630 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ +# Sqlite +sqlite +sqlite3 +*.sqlite +*.sqlite3 + # Logs logs *.log diff --git a/Dockerfile b/Dockerfile index b91f970..27e6d6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Nodejs -FROM node:22 +FROM node:22.5.1 WORKDIR /app @@ -13,4 +13,4 @@ RUN npm install --build-from-source COPY . . -CMD ["node", "--env-file=.env", "./src/index.js"] +CMD ["npm", "start"] diff --git a/README.md b/README.md index 6794123..9666089 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ sudo apt install -y npm node -v npm -v ``` - āš ļø Note that the minimum node js version for the project must be v20. āš ļø + āš ļø Note that the minimum node js version for the project must be v22.5.0. āš ļø 3. Install dependencies: ```bash @@ -35,6 +35,10 @@ npm install 4. Start the bot. ```bash +npm start +``` +OR +```bash node --env-file=.env ./src/index.js ``` diff --git a/docker-compose.yml b/docker-compose.yml index d7e1c8f..4921916 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.8' +version: "3.8" services: bot: @@ -8,5 +8,5 @@ services: - .env volumes: - .:/app - command: ["node", "--env-file=.env", "./src/index.js"] + command: ["npm", "start"] restart: unless-stopped diff --git a/package.json b/package.json index 64eb9bd..652b62b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "I am a Bot capable of giving you useful information about the MMORPG GoBattle.io.", "main": "./src/index.js", "scripts": { + "start": "node --env-file=.env --experimental-sqlite ./src/index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index 2563beb..ac91f15 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -51,7 +51,7 @@ async function get_list(interaction, _client){ const embed = new EmbedBuilder(); embed.setTitle("šŸ° Dungeon List šŸ°"); - const header_description = "Note that this list is not updated in real time like other lists. This is a pre-list awaiting the GoBattle.io API update.\n"; + const header_description = "Note that this list is not updated in real time unlike other lists. This is a pre-list awaiting the GoBattle.io API update.\n"; const max_items_by_pages = 7; const pages = new Array(Math.ceil(dungeon_list.length / max_items_by_pages)); diff --git a/src/commands/item.js b/src/commands/item.js index 9a146c4..3da27a9 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -1,6 +1,6 @@ -const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType, Emoji, parseEmoji} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); const {ultra_list} = require("../ultralist.json"); -const {restrict_text, sum, format_score} = require("../utils.js"); +const {restrict_text, sum, format_score, format_score_with_commas, application_emoji_cache} = require("../utils.js"); const items_map = new Map(); for (const item_data of ultra_list){ @@ -70,10 +70,14 @@ async function get_info(interaction, client){ return; } - const emoji = new Emoji(client, parseEmoji(item_info?.emoji || "<:item_91:1267565851536789554>")); + const unknown_item_emoji = application_emoji_cache.get("item_91") || "šŸ“¦"; + const item_emoji = application_emoji_cache.get(item_info?.emoji) || unknown_item_emoji; + const embed = new EmbedBuilder(); - embed.setTitle(`${emoji} ${restrict_text(item_info.name, 60)}`); - embed.setThumbnail(emoji.imageURL()); + embed.setTitle(`${item_emoji} ${restrict_text(item_info.name, 60)}`); + if (typeof item_emoji != "string"){ + embed.setThumbnail(item_emoji.imageURL()); + } embed.setDescription(item_info.description ? restrict_text(item_info.description, 250) : "_Description Unknown_"); embed.setColor(0x500000); embed.addFields( @@ -131,10 +135,12 @@ async function get_ultrarare_drops(interaction, client){ embed.setTitle("Current average chance of getting an Ultrarare from a chest."); - const chest_emoji = new Emoji(client, parseEmoji("<:item_760:1267561884610203650>")); - const header_description = `Results are calculated based on ${total} samples (*chest opened by players ${chest_emoji}*) including ${total_ultrarare} ultrarare drops.\n`; + const chest_emoji = application_emoji_cache.get("item_760") || "🧰"; + const total_ultrarare_formatted = format_score_with_commas(total_ultrarare); + const header_description = `Results are calculated based on **${format_score_with_commas(total)}** samples (*chest opened by players ${chest_emoji}*) including **${total_ultrarare_formatted}** ultrarare drops.\n`; const max_items_by_pages = 7; const pages = new Array(Math.ceil(values.length / max_items_by_pages)); + const unknown_item_emoji = application_emoji_cache.get("item_91") || "šŸ“¦"; const drops_entries = Object.entries(drops); @@ -147,16 +153,16 @@ async function get_ultrarare_drops(interaction, client){ const [key, value] = drops_entries[i]; const ultra_info = items_map.get(parseInt(key, 10)); - const emoji = new Emoji(client, parseEmoji(ultra_info?.emoji || "<:item_91:1267565851536789554>")); - pages[current_page] += `* ${emoji} **${restrict_text(ultra_info?.name || "*Unknow?*", 45)}**#${key}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%)\`\n`; + const item_emoji = application_emoji_cache.get(ultra_info?.emoji) || unknown_item_emoji; + pages[current_page] += `* ${item_emoji} **${restrict_text(ultra_info?.name || "*Unknow?*", 45)}**#${key}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%)\`\n`; } current_page = 1; embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); embed.addFields( - {name: "> 🌟 __Ultrarare__", value: `> ${(drops_ratio * 100).toPrecision(2)}%`, inline: true}, - {name: "> šŸ“¦ __Other__", value: `> ${((1 - drops_ratio) * 100).toPrecision(2)}%`, inline: true} + {name: "> 🌟 __Ultrarare__", value: `> ${total_ultrarare_formatted} (${(drops_ratio * 100).toPrecision(2)}%)`, inline: true}, + {name: `> ${unknown_item_emoji} __Other__`, value: `> ${format_score_with_commas(total - total_ultrarare)} (${((1 - drops_ratio) * 100).toPrecision(2)}%)`, inline: true} ); const nb_pages = pages.length || 1; diff --git a/src/commands/ranking.js b/src/commands/ranking.js index 4f760c4..0427472 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -1,5 +1,5 @@ -const {EmbedBuilder, SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType, Emoji, parseEmoji} = require("discord.js"); -const {restrict_text, get_level, get_level_adventurer, format_score, format_speed_run_time} = require("../utils.js"); +const {EmbedBuilder, SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); +const {restrict_text, get_level, get_level_adventurer, format_score, format_speed_run_time, application_emoji_cache} = require("../utils.js"); const head_list = require("../head_list.json"); const heads_map = new Map(); @@ -71,13 +71,9 @@ async function get_ranking(interaction, client){ await get_king(interaction, client); break; case "weekly": - await get_general(interaction, client, "weekly"); - break; case "monthly": - await get_general(interaction, client, "monthly"); - break; case "overall": - await get_general(interaction, client, "overall"); + await get_general(interaction, client, subcommand); break; case "adventurer": await get_adventurer(interaction, client); @@ -107,13 +103,15 @@ async function get_king(interaction, client){ const data = await response.json(); const embed = new EmbedBuilder(); - embed.setTitle("šŸ‘‘ King Ranking šŸ‘‘"); + const crown_emoji = application_emoji_cache.get("valdorancrown") || "šŸ‘‘"; + embed.setTitle(`${crown_emoji} King Ranking ${crown_emoji}`); const ranking = data?.ranking; const header_description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command.\n"; const max_items_by_pages = 7; const pages = new Array(Math.ceil(ranking.length / max_items_by_pages)); + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; let current_page = -1; for (let i = 0; i < ranking.length; i++){ @@ -124,12 +122,12 @@ async function get_king(interaction, client){ const field = ranking[i]; const head_data = heads_map.get(parseInt(field?.skin_head, 10)); - const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); - pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.reputation)} REP\`\n`; + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; + pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.reputation)} REP\`\n`; } current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸ—“ __Week__", value: `> ${data?.week || "_Unknown?_"}`, inline: true}, @@ -219,21 +217,31 @@ async function get_general(interaction, client, type){ switch (type){ case "weekly": - embed.setTitle("šŸ”„ Weekly Ranking šŸ”„"); + const weekly_emoji = application_emoji_cache.get("item_53") || "šŸ†"; + embed.setTitle(`${weekly_emoji} Weekly Ranking ${weekly_emoji}`); break; case "monthly": - embed.setTitle("šŸ”„ Monthly Ranking šŸ”„"); + const monthly_emoji = application_emoji_cache.get("item_10") || "šŸ†"; + embed.setTitle(`${monthly_emoji} Monthly Ranking ${monthly_emoji}`); break; case "overall": - embed.setTitle("šŸ”„ Overall Ranking (EXP) šŸ”„"); + const overall_emoji = application_emoji_cache.get("item_51") || "šŸ†"; + embed.setTitle(`${overall_emoji} Overall Ranking (EXP) ${overall_emoji}`); break; default: - embed.setTitle("šŸ”„ General Ranking šŸ”„"); + const general_emoji = application_emoji_cache.get("valdorancrown") || "šŸ†"; + embed.setTitle(`${general_emoji} General Ranking ${general_emoji}`); } const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; + const kills_emoji = application_emoji_cache.get("compass_item97") || "šŸ—”ļø"; + const deaths_emoji = application_emoji_cache.get("compass_item96") || "šŸ’€"; + const coins_emoji = application_emoji_cache.get("coin") || "šŸŖ™"; + const experience_emoji = application_emoji_cache.get("fuego") || "šŸ”„"; + const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ”°"; let current_page = -1; for (let i = 0; i < data.length; i++){ @@ -244,22 +252,14 @@ async function get_general(interaction, client, type){ const field = data[i]; const head_data = heads_map.get(parseInt(field?.skin_head, 10)); - const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; const exp = parseInt(field?.experience, 10); const level = get_level(exp); - pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**: \`${format_score(field?.coins)}šŸŖ™ ${format_score(field?.kills)}šŸ—”ļø ${format_score(field?.deaths)}šŸ’€ ${format_score(field?.experience)}šŸ› ļø ${level}šŸ’Ŗ\`\n`; + pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**: ${coins_emoji}\`${format_score(field?.coins)}\` ${kills_emoji}\`${format_score(field?.kills)}\` ${deaths_emoji}\`${format_score(field?.deaths)}\` ${experience_emoji}\`${format_score(field?.experience)}\` ${level_emoji}\`${level}\`\n`; } current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); - - embed.addFields( - {name: "> __Coins__", value: "> šŸŖ™", inline: true}, - {name: "> __Kills__", value: "> šŸ—”ļø", inline: true}, - {name: "> __Deaths__", value: "> šŸ’€", inline: true}, - {name: "> __Experience__", value: "> šŸ› ļø", inline: true}, - {name: "> __Level__", value: "> šŸ’Ŗ", inline: true} - ); + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); const nb_pages = pages.length || 1; @@ -347,6 +347,7 @@ async function get_adventurer(interaction, client){ const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; let current_page = -1; for (let i = 0; i < data.length; i++){ @@ -357,9 +358,9 @@ async function get_adventurer(interaction, client){ const field = data[i]; const head_data = heads_map.get(parseInt(field?.skin_head, 10)); - const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; const level = get_level_adventurer(field?.score); - pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; + pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; } current_page = 1; @@ -446,11 +447,13 @@ async function get_relic_hunter(interaction, client){ const embed = new EmbedBuilder(); - embed.setTitle("šŸŗ Relic Hunter Ranking šŸŗ"); + const relic_hunter_emoji = application_emoji_cache.get("item_809") || "šŸŽ’"; + embed.setTitle(`${relic_hunter_emoji} Relic Hunter Ranking ${relic_hunter_emoji}`); const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; let current_page = -1; for (let i = 0; i < data.length; i++){ @@ -461,13 +464,13 @@ async function get_relic_hunter(interaction, client){ const field = data[i]; const head_data = heads_map.get(parseInt(field?.skin_head, 10)); - const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; const level = get_level_adventurer(field?.score); - pages[current_page] += `${i + 1}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; + pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; } current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); const nb_pages = pages.length || 1; @@ -559,7 +562,8 @@ async function get_speedrun(interaction, client){ const header_description = "Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!\n"; const max_items_by_pages = 7; const pages = new Array(Math.ceil(ranking.length / max_items_by_pages)); - + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; + const list_size = ranking.length; let current_page = -1; for (let i = 0; i < list_size; i++){ @@ -570,13 +574,13 @@ async function get_speedrun(interaction, client){ const field = ranking[i]; const head_data = heads_map.get(parseInt(field?.skin_head, 10)); - const emoji = new Emoji(client, parseEmoji(head_data?.emoji || "<:heads_item_0:1267602598668668949>")); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; const time = format_speed_run_time(field?.time); - pages[current_page] += `${field?.rank}. ${emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${time}\`\n`; + pages[current_page] += `${field?.rank}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${time}\`\n`; } current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, diff --git a/src/commands/server.js b/src/commands/server.js index a34edfa..7bf8339 100644 --- a/src/commands/server.js +++ b/src/commands/server.js @@ -99,7 +99,7 @@ async function get_list(interaction, client){ } current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); embed.addFields( {name: "> šŸŽ® __Platform__", value: `> ${platform}`, inline: true}, diff --git a/src/commands/user.js b/src/commands/user.js index 8142bc4..3e50daf 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -1,9 +1,30 @@ -const {EmbedBuilder, AttachmentBuilder, SlashCommandBuilder} = require("discord.js"); -const {restrict_text, is_my_developer} = require("../utils.js"); +const {EmbedBuilder, SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType, ModalBuilder, TextInputBuilder, TextInputStyle} = require("discord.js"); +const {is_my_developer, restrict_text, format_score, format_score_with_commas, get_level, get_level_to_emojis, application_emoji_cache} = require("../utils.js"); +const database = require("../database/database.js"); +const head_list = require("../head_list.json"); + +const heads_map = new Map(); +for (const head_data of head_list){ + if (typeof head_data.id == "number"){ + heads_map.set(head_data.id, head_data); + } +} const user_command = new SlashCommandBuilder(); user_command.setName("user"); user_command.setDescription("Command relating to users in the game."); +user_command.addSubcommand((subcommand) => { + subcommand.setName("login"); + subcommand.setDescription("Log in to your GoBattle.io account."); + + return subcommand; +}); +user_command.addSubcommand((subcommand) => { + subcommand.setName("logout"); + subcommand.setDescription("Log out of your GoBattle.io account."); + + return subcommand; +}); user_command.addSubcommand((subcommand) => { subcommand.setName("info"); subcommand.setDescription("Get general information about a user."); @@ -64,98 +85,306 @@ user_command.addSubcommand((subcommand) => { return subcommand; }); +user_command.addSubcommandGroup((friend_command) => { + friend_command.setName("friend"); + friend_command.setDescription("Command relating to friends in the game."); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("pending_count"); + subcommand.setDescription("Get the number of friend requests a user has."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("incoming_requests"); + subcommand.setDescription("Get the list of a user's incoming friend requests."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("outgoing_requests"); + subcommand.setDescription("Get the list of a user's outgoing friend requests."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("list"); + subcommand.setDescription("Get a user's friends list."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("add"); + subcommand.setDescription("Send a friend request to a user."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + + return friend_command; +}); async function get_user(interaction, client){ + const subcommand_group = interaction.options.getSubcommandGroup(); + const subcommand = interaction.options.getSubcommand(); + + if (!subcommand_group){ + switch (subcommand){ + case "login": + await get_login(interaction, client); + break; + case "logout": + await get_logout(interaction, client); + break; + case "info": + await get_info(interaction, client); + break; + case "king": + await get_king(interaction, client); + break; + case "bank": + await get_bank(interaction, client); + break; + case "inventory": + await get_inventory(interaction, client); + break; + default: + await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); + } + }else if (subcommand_group == "friend"){ + await get_friend(interaction, client); + }else{ + await interaction.reply(`Invalid subcommand group.\nContact ${client.application.owner} to resolve this issue.`); + } +} + +async function get_friend(interaction, client){ const subcommand = interaction.options.getSubcommand(); switch (subcommand){ - case "info": - await get_info(interaction, client); + case "pending_count": + await get_friend_pending_count(interaction, client); + break; + case "incoming_requests": + await get_friend_pending_requests(interaction, client, "incoming"); + break; + case "outgoing_requests": + await get_friend_pending_requests(interaction, client, "outgoing"); break; - case "king": - await get_king(interaction, client); + case "list": + await get_friend_list(interaction, client); break; - case "bank": - await get_bank(interaction, client); + case "add": + await interaction.reply("This subcommand is under development."); break; - case "inventory": - await get_inventory(interaction, client); + case "accepte": + await interaction.reply("This subcommand is under development."); + break; + case "ignore": + await interaction.reply("This subcommand is under development."); break; default: await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); } } +async function get_login(interaction, client){ + const modal = new ModalBuilder(); + modal.setCustomId("login"); + modal.setTitle("Login to Gobattle account."); + + const email_input = new TextInputBuilder(); + email_input.setCustomId("email"); + email_input.setLabel("Email (Used on GoBattle.io)"); + email_input.setStyle(TextInputStyle.Short); + email_input.setRequired(true); + + const password_input = new TextInputBuilder(); + password_input.setCustomId("password"); + password_input.setLabel("Password (Don't give Discord password!)"); + password_input.setStyle(TextInputStyle.Short); + email_input.setRequired(true); + + const email_action_row = new ActionRowBuilder().addComponents(email_input); + const password_action_row = new ActionRowBuilder().addComponents(password_input); + + modal.addComponents(email_action_row, password_action_row); + + await interaction.showModal(modal); +} + +async function get_logout(interaction, client){ + const succes = database.remove_gobattle_accesse(interaction.user); + + if (succes){ + await interaction.reply({content: "Your session has ended and has been successfully deleted. You can reconnect at any time with `/user login`.", ephemeral: true}); + }else{ + await interaction.reply({content: "You do not have a registered session.", ephemeral: true}); + } +} + async function get_info(interaction, client){ await interaction.deferReply(); try{ const user_id = interaction.options.get("user_id")?.value; - const public = false; + const public = true; - if (!is_my_developer(client, interaction.user) && !public){ + const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); + if (!gobattle_token){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + if (!public){ await interaction.editReply(`Sorry, user _#${user_id}_ does not wish to expose this information to the public.`); return; } + const server_version = 115; + const platform = "Web"; + const response = await fetch(`https://gobattle.io/api.php/bootstrap/${server_version}/${gobattle_token}?platform=${platform}&ud=`); + const data = await response.json(); + + if (data?.error == "Invalid token"){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + } + + if (!response.ok){ + await interaction.editReply(`Unable to retrieve user data. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + const embed = new EmbedBuilder(); - embed.setTitle(restrict_text("Kuwazy", 60)); - embed.setThumbnail("attachment://head.png"); - embed.setDescription(restrict_text("_This user has no description..._", 250)); + + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; + const streamer_emoji = application_emoji_cache.get("compass_item122") || "šŸ”“"; + const head_data = heads_map.get(data.user.skin_head); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; + + embed.setTitle(`${head_emoji} ${data.user.streamer ? `${streamer_emoji} ` : ""}**${restrict_text(data.user.nick, 60)}**#${data.user.id}`); + if (typeof head_emoji != "string"){ + embed.setThumbnail(head_emoji.imageURL()); + } + + const level = get_level(data.user.experience); + embed.setDescription(`${get_level_to_emojis(Math.min(level, 2000))}\n${restrict_text("_This user has no description..._", 250)}`); embed.setColor(0x500000); embed.addFields( - {name: "> šŸ·ļø __ID__", value: `> ${user_id.toString()}`, inline: true}, - {name: "> šŸŖ™ __Coins__", value: `> ${"2,283,520"}`, inline: true}, - {name: "> šŸ’Ž __Diamonds__", value: `> ***${(27).toString()}***`, inline: true} + {name: "> šŸ·ļø __ID__", value: `> ${data.user.id}`, inline: true}, + {name: "> šŸŖ™ __Coins__", value: `> ${format_score_with_commas(data.user.coins)}`, inline: true}, + {name: "> šŸ’Ž __Diamonds__", value: `> ***${format_score_with_commas(data.user.diamonds)}***`, inline: true} ); embed.addFields( - {name: "> šŸ’Ŗ __LVL__", value: `> ${"174"}`, inline: true}, - {name: "> šŸ› ļø __EXP__", value: `> ${"92026/139600"}`, inline: true}, - {name: "> šŸ”± __REP__", value: `> ${"17"}`, inline: true} + {name: "> šŸ’Ŗ __LVL__", value: `> ${format_score_with_commas(level)}`, inline: true}, + {name: "> šŸ”„ __EXP__", value: `> ${format_score_with_commas(data.user.experience)}`, inline: true}, + {name: "> šŸŽ– __Role__", value: `> ${data.user.streamer ? `${streamer_emoji} Streamer` : "Player"}`, inline: true} ); + const equipped_attack = data.user.attack - data.user.base_attack; + const equipped_defense = data.user.defense - data.user.base_defense; + const equipped_luck = data.user.luck - data.user.base_luck; + const equipped_hp = data.user.hp - data.user.base_hp; + const equipped_regeneration = data.user.regeneration - data.user.base_regeneration; + const equipped_speed = data.user.speed - data.user.base_speed; + + const formatted_attack = data.user.attack > 0 ? "+" + data.user.attack : data.user.attack; + const formatted_defense = data.user.defense > 0 ? "+" + data.user.defense : data.user.defense; + const formatted_luck = data.user.luck > 0 ? "+" + data.user.luck : data.user.luck; + const formatted_hp = data.user.hp > 0 ? "+" + data.user.hp : data.user.hp; + const formatted_regeneration = data.user.regeneration > 0 ? "+" + data.user.regeneration : data.user.regeneration; + const formatted_speed = data.user.speed > 0 ? "+" + data.user.speed : data.user.speed; + + const formatted_base_attack = data.user.base_attack > 0 ? "+" + data.user.base_attack : data.user.base_attack; + const formatted_base_defense = data.user.base_defense > 0 ? "+" + data.user.base_defense : data.user.base_defense; + const formatted_base_luck = data.user.base_luck > 0 ? "+" + data.user.base_luck : data.user.base_luck; + const formatted_base_hp = data.user.base_hp > 0 ? "+" + data.user.base_hp : data.user.base_hp; + const formatted_base_regeneration = data.user.base_regeneration > 0 ? "+" + data.user.base_regeneration : data.user.base_regeneration; + const formatted_base_speed = data.user.base_speed > 0 ? "+" + data.user.base_speed : data.user.base_speed; + + const formatted_equipped_attack = equipped_attack > 0 ? "+" + equipped_attack : equipped_attack; + const formatted_equipped_defense = equipped_defense > 0 ? "+" + equipped_defense : equipped_defense; + const formatted_equipped_luck = equipped_luck > 0 ? "+" + equipped_luck : equipped_luck; + const formatted_equipped_hp = equipped_hp > 0 ? "+" + equipped_hp : equipped_hp; + const formatted_equipped_regeneration = equipped_regeneration > 0 ? "+" + equipped_regeneration : equipped_regeneration; + const formatted_equipped_speed = equipped_speed > 0 ? "+" + equipped_speed : equipped_speed; + embed.addFields( - {name: "> āš”ļø __ATT__", value: `> ${"+17"}`, inline: true}, - {name: "> šŸ›”ļø __DEF__", value: `> ${"+19"}`, inline: true}, - {name: "> šŸ€ __LCK__", value: `> ${"+9"}`, inline: true} + {name: "> āš”ļø __ATT__", value: `> **${formatted_attack}** _(Base: ${formatted_base_attack}, Equipped: ${formatted_equipped_attack})_`, inline: true}, + {name: "> šŸ›”ļø __DEF__", value: `> **${formatted_defense}** _(Base: ${formatted_base_defense}, Equipped: ${formatted_equipped_defense})_`, inline: true}, + {name: "> šŸ€ __LCK__", value: `> **${formatted_luck}** _(Base: ${formatted_base_luck}, Equipped: ${formatted_equipped_luck})_`, inline: true} ); embed.addFields( - {name: "> ā¤ļø __MHP__", value: `> ${"+11"}`, inline: true}, - {name: "> ā¤ļøā€šŸ©¹ __RGN__", value: `> ${"+6"}`, inline: true}, - {name: "> ⚔ __SPD__", value: `> ${"+13"}`, inline: true} + {name: "> ā¤ļø __MHP__", value: `> **${formatted_hp}** _(Base: ${formatted_base_hp}, Equipped: ${formatted_equipped_hp})_`, inline: true}, + {name: "> ā¤ļøā€šŸ©¹ __RGN__", value: `> **${formatted_regeneration}** _(Base: ${formatted_base_regeneration}, Equipped: ${formatted_equipped_regeneration})_`, inline: true}, + {name: "> ⚔ __SPD__", value: `> **${formatted_speed}** _(Base: ${formatted_base_speed}, Equipped: ${formatted_equipped_speed})_`, inline: true} ); + /* embed.addFields( - {name: "> šŸ’Æ __ADV Score__", value: `> ${"5630"}`, inline: true}, - {name: "> šŸ’Ŗ __ADV LVL__", value: `> ${"5"}`, inline: true}, - {name: "> 🤠 __ADV Rank__", value: `> ${"124587"}`, inline: true} + {name: "> šŸ’Æ __ADV Score__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> šŸ’Ŗ __ADV LVL__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> 🤠 __ADV Rank__", value: `> ${"_Unknown?_"}`, inline: true} ); embed.addFields( - {name: "> šŸ”” __Status__", value: `> ${"Connected"}`, inline: true}, - {name: "> 🌐 __Server__", value: `> ${"France Server"}`, inline: true}, - {name: "> šŸŽ– __Role__", value: `> ${"Player"}`, inline: true} + {name: "> šŸ”” __Status__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> 🌐 __Server__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> šŸ”± __REP__", value: `> ${"_Unknown?_"}`, inline: true} ); embed.addFields( - {name: "> šŸ”‡ __Is Muted__", value: `> ${"No"}`, inline: true}, - {name: "> 🚫 __Is Banned__", value: `> ${"No"}`, inline: true}, - {name: "> šŸ‘‘ __Is King__", value: `> ${"No"}`, inline: true} + {name: "> šŸ”‡ __Is Muted__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> 🚫 __Is Banned__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> šŸ‘‘ __Is King__", value: `> ${"_Unknown?_"}`, inline: true} ); + */ - embed.setFooter({text: "Last connection"}); embed.setTimestamp(); - const attachment = new AttachmentBuilder(__dirname + "/../images/mr_strong_head.png"); - attachment.name = "head.png"; - await interaction.editReply({ - files: [attachment], - embeds: [embed], - content: "This feature is under development. This is just an example of a template." + embeds: [embed] }); }catch(error){ await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); @@ -177,5 +406,278 @@ async function get_inventory(interaction, client){ await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); } +async function get_friend_pending_count(interaction, client){ + await interaction.deferReply(); + + const user_id = interaction.options.get("user_id")?.value; + const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); + + if (!gobattle_token){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + const platform = "Web"; + const api_version = 1; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/pending/count?platform=${platform}&ud=`); + const data = await response.json(); + + if (data?.error == "Invalid token"){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + } + + if (!response.ok){ + await interaction.editReply(`Unable to retrieve user data. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + await interaction.editReply(`User _#${user_id}_ currently has **${data.incoming}** incoming friend requests.`); +} + +async function get_friend_pending_requests(interaction, client, type){ + const user_id = interaction.options.get("user_id")?.value; + + const public = type != "incoming" || database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id; + await interaction.deferReply({ephemeral: public}); + + const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); + if (!gobattle_token){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + if (!public){ + await interaction.editReply(`Sorry, user _#${user_id}_ does not wish to expose this information to the public.`); + return; + } + + const platform = "Web"; + const api_version = 1; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/pending?platform=${platform}&ud=`); + const data = await response.json(); + + if (data?.error == "Invalid token"){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + } + + if (!response.ok){ + await interaction.editReply(`Unable to retrieve user data. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const embed = new EmbedBuilder(); + const friend_emoji = "šŸ¤"; + embed.setTitle(`${friend_emoji} Friend ${type} requests ${friend_emoji}`); + + const requests = data[type]; + const header_description = `User _#${user_id}_ has **${requests.length}** ${type} friend requests.\n`; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(requests.length / max_items_by_pages)); + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; + + let current_page = -1; + for (let i = 0; i < requests.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + + const field = requests[i]; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; + pages[current_page] += `* ${head_emoji} **${field?.nick ? restrict_text(field?.nick, 20) : "?"}**#${field?.id}\n`; + } + + current_page = 1; + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); + + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + embed.setTimestamp(); + + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] + }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); +} + +async function get_friend_list(interaction, client){ + const user_id = interaction.options.get("user_id")?.value; + const public = database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id; + + await interaction.deferReply({ephemeral: public}); + + const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); + if (!gobattle_token){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + if (!public){ + await interaction.editReply(`Sorry, user _#${user_id}_ does not wish to expose this information to the public.`); + return; + } + + const platform = "Web"; + const api_version = 1; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/list?platform=${platform}&ud=`); + const data = await response.json(); + + if (data?.error == "Invalid token"){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + } + + if (!response.ok){ + await interaction.editReply(`Unable to retrieve user data. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const embed = new EmbedBuilder(); + const friend_emoji = "šŸ¤"; + embed.setTitle(`${friend_emoji} Friends list ${friend_emoji}`); + + const header_description = `User _#${user_id}_ has **${data.length}/100** friends.\n`; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(data.length / max_items_by_pages)); + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; + const streamer_emoji = application_emoji_cache.get("compass_item122") || "šŸ”“"; + const experience_emoji = application_emoji_cache.get("fuego") || "šŸ”„"; + const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ”°"; + + let current_page = -1; + for (let i = 0; i < data.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = header_description; + } + + const field = data[i]; + const head_data = heads_map.get(parseInt(field?.skin_head, 10)); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; + const exp = parseInt(field?.experience, 10); + const level = get_level(exp); + /*const date = new Date(field?.ts + "Z"); + const timestamp = date.getTime() / 1000;*/ + const online_label = field?.online ? `**Online** (__*${field?.playing}*__) in __*${field?.joinFriendlyName}*__.` : "**Offline**"; + pages[current_page] += `* ${head_emoji} ${false && field?.status == 1 ? streamer_emoji : ""}**${field?.nick ? restrict_text(field?.nick, 20) : "?"}**#${field?.id}: ${online_label} ${experience_emoji}\`${format_score(field?.experience)}\` ${level_emoji}\`${level}\`\n`; + } + + current_page = 1; + embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); + + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + embed.setTimestamp(); + + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: [row] + }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); +} + exports.get_user = get_user; exports.user_command = user_command; diff --git a/src/database/database.js b/src/database/database.js new file mode 100644 index 0000000..cf7e188 --- /dev/null +++ b/src/database/database.js @@ -0,0 +1,114 @@ +"use strict"; + +const sqlite = require("node:sqlite"); +const fs = require("fs"); + +const database = new sqlite.DatabaseSync("database.sqlite"); + +let add_gobattle_accesse_statement; +let remove_gobattle_accesse_statement; +let get_gobattle_token_by_gobattle_user_id_statement; +let discord_user_id_to_gobattle_user_id_statement; +let gobattle_user_id_to_discord_user_id_statement; + +function init(){ + fs.readFile(__dirname + "/schema.sql", "utf8", function (error, data){ + if (error){ + console.error(error); + return; + } + + database.exec(data); + + add_gobattle_accesse_statement = database.prepare(` + INSERT INTO user_session (discord_user_id, gobattle_user_id, gobattle_token) + VALUES ($discord_user_id, $gobattle_user_id, $gobattle_token) + ON CONFLICT (discord_user_id) + DO UPDATE SET + discord_user_id = excluded.discord_user_id, + gobattle_token = excluded.gobattle_token, + session_date = CURRENT_TIMESTAMP + ON CONFLICT (gobattle_user_id) + DO UPDATE SET + discord_user_id = excluded.discord_user_id, + gobattle_token = excluded.gobattle_token, + session_date = CURRENT_TIMESTAMP; + `); + + remove_gobattle_accesse_statement = database.prepare(` + DELETE FROM user_session WHERE discord_user_id = $discord_user_id; + `); + + get_gobattle_token_by_gobattle_user_id_statement = database.prepare(` + SELECT gobattle_token FROM user_session WHERE gobattle_user_id = $gobattle_user_id; + `); + + discord_user_id_to_gobattle_user_id_statement = database.prepare(` + SELECT gobattle_user_id FROM user_session WHERE discord_user_id = $discord_user_id; + `); + + gobattle_user_id_to_discord_user_id_statement = database.prepare(` + SELECT discord_user_id FROM user_session WHERE gobattle_user_id = $gobattle_user_id; + `); + gobattle_user_id_to_discord_user_id_statement.setReadBigInts(true); + }); +} + +function add_gobattle_accesse(discord_user, gobattle_user_id, gobattle_token){ + const discord_user_prime = gobattle_user_id_to_discord_user_id(gobattle_user_id); + if (discord_user_prime?.toString() == discord_user.id){ + return false; + } + + add_gobattle_accesse_statement.run({ + $discord_user_id: BigInt(discord_user.id), + $gobattle_user_id: gobattle_user_id, + $gobattle_token: gobattle_token + }); + + return true; +} + +function remove_gobattle_accesse(discord_user){ + const gobattle_user_id = discord_user_to_gobattle_user_id(discord_user); + if (!gobattle_user_id){ + return false; + } + + remove_gobattle_accesse_statement.run({ + $discord_user_id: BigInt(discord_user.id), + }); + + return true; +} + +function get_gobattle_token_by_gobattle_user_id(gobattle_user_id){ + const data = get_gobattle_token_by_gobattle_user_id_statement.get({ + $gobattle_user_id: gobattle_user_id + }); + + return data.gobattle_token; +} + +function discord_user_to_gobattle_user_id(discord_user){ + const data = discord_user_id_to_gobattle_user_id_statement.get({ + $discord_user_id: BigInt(discord_user.id) + }); + + return data.gobattle_user_id; +} + +function gobattle_user_id_to_discord_user_id(gobattle_user_id){ + const data = gobattle_user_id_to_discord_user_id_statement.get({ + $gobattle_user_id: gobattle_user_id + }); + + return data.discord_user_id; +} + +exports.init = init; +exports.add_gobattle_accesse = add_gobattle_accesse; +exports.remove_gobattle_accesse = remove_gobattle_accesse; +exports.get_gobattle_token_by_gobattle_id = get_gobattle_token_by_gobattle_user_id; +exports.discord_user_to_gobattle_user_id = discord_user_to_gobattle_user_id +exports.gobattle_user_id_to_discord_user_id = gobattle_user_id_to_discord_user_id; \ No newline at end of file diff --git a/src/database/schema.sql b/src/database/schema.sql new file mode 100644 index 0000000..6b24a30 --- /dev/null +++ b/src/database/schema.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS user_session ( + discord_user_id INTEGER PRIMARY KEY CHECK (discord_user_id > 0), + gobattle_user_id INTEGER UNIQUE CHECK (gobattle_user_id > 0), + session_date TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP CHECK ( + -- Validating the date format (YYYY-MM-DD HH:MM:SS). + length(session_date) = 19 AND + substr(session_date, 1, 4) GLOB "[0-9][0-9][0-9][0-9]" AND + substr(session_date, 5, 1) = "-" AND + substr(session_date, 6, 2) GLOB "[0-9][0-9]" AND + substr(session_date, 8, 1) = "-" AND + substr(session_date, 9, 2) GLOB "[0-9][0-9]" AND + substr(session_date, 11, 1) = " " AND + substr(session_date, 12, 2) GLOB "[0-9][0-9]" AND + substr(session_date, 14, 1) = ":" AND + substr(session_date, 15, 2) GLOB "[0-9][0-9]" AND + substr(session_date, 17, 1) = ":" AND + substr(session_date, 18, 2) GLOB "[0-9][0-9]" + ), + gobattle_token TEXT NOT NULL UNIQUE CHECK (length(gobattle_token) = 64) +) WITHOUT ROWID; \ No newline at end of file diff --git a/src/head_list.json b/src/head_list.json index e2e3914..86985a4 100644 --- a/src/head_list.json +++ b/src/head_list.json @@ -2,291 +2,291 @@ { "id": 0, "name": "Grey Head", - "emoji": "<:heads_item_0:1267602598668668949>" + "emoji": "heads_item_0" }, { "id": 1, "name": "Black Head", - "emoji": "<:heads_item_1:1267602583241883760>" + "emoji": "heads_item_1" }, { "id": 2, "name": "Blue Head", - "emoji": "<:heads_item_2:1267602568696172574>" + "emoji": "heads_item_2" }, { "id": 3, "name": "Dark Blue Head", - "emoji": "<:heads_item_3:1267602554250854531>" + "emoji": "heads_item_3" }, { "id": 4, "name": "Emerald Head", - "emoji": "<:heads_item_4:1267602540938137701>" + "emoji": "heads_item_4" }, { "id": 5, "name": "Fire Head", - "emoji": "<:heads_item_5:1267602525268344984>" + "emoji": "heads_item_5" }, { "id": 6, "name": "Skin color 1 Head", - "emoji": "<:heads_item_6:1267602507593416714>" + "emoji": "heads_item_6" }, { "id": 7, "name": "Skin color 2 Head", - "emoji": "<:heads_item_7:1267602488907927666>" + "emoji": "heads_item_7" }, { "id": 8, "name": "Skin color 3 Head", - "emoji": "<:heads_item_8:1267602477516062973>" + "emoji": "heads_item_8" }, { "id": 9, "name": "Skin color 4 Head", - "emoji": "<:heads_item_9:1267602463813533718>" + "emoji": "heads_item_9" }, { "id": 10, "name": "Gold Head", - "emoji": "<:heads_item_10:1267602450345623634>" + "emoji": "heads_item_10" }, { "id": 11, "name": "Green Head", - "emoji": "<:heads_item_11:1267602439142375616>" + "emoji": "heads_item_11" }, { "id": 12, "name": "Light Blue Head", - "emoji": "<:heads_item_12:1267602421878620344>" + "emoji": "heads_item_12" }, { "id": 13, "name": "Orange Head", - "emoji": "<:heads_item_13:1267602405952847915>" + "emoji": "heads_item_13" }, { "id": 14, "name": "Pink Head", - "emoji": "<:heads_item_14:1267602392074158130>" + "emoji": "heads_item_14" }, { "id": 15, "name": "Purple Head", - "emoji": "<:heads_item_15:1267602376085213327>" + "emoji": "heads_item_15" }, { "id": 16, "name": "Red Head", - "emoji": "<:heads_item_16:1267602359236690135>" + "emoji": "heads_item_16" }, { "id": 17, "name": "White Head", - "emoji": "<:heads_item_17:1267602315221794887>" + "emoji": "heads_item_17" }, { "id": 18, "name": "Yellow Head", - "emoji": "<:heads_item_18:1267602296817061938>" + "emoji": "heads_item_18" }, { "id": 19, "name": "Cat Knight", - "emoji": "<:heads_item_19:1267602283646947479>" + "emoji": "heads_item_19" }, { "id": 20, "name": "Sophie", - "emoji": "<:heads_item_20:1267602270938202184>" + "emoji": "heads_item_20" }, { "id": 21, "name": "Hiroto", - "emoji": "<:heads_item_21:1267602260284801157>" + "emoji": "heads_item_21" }, { "id": 22, "name": "Mary", - "emoji": "<:heads_item_22:1267602246141481174>" + "emoji": "heads_item_22" }, { "id": 23, "name": "Happy", - "emoji": "<:heads_item_23:1267602232703062090>" + "emoji": "heads_item_23" }, { "id": 24, "name": "Rhino", - "emoji": "<:heads_item_24:1267602215749550232>" + "emoji": "heads_item_24" }, { "id": 25, "name": "M.R. Dalmau", - "emoji": "<:heads_item_25:1267602198662090873>" + "emoji": "heads_item_25" }, { "id": 26, "name": "Cyclops", - "emoji": "<:heads_item_26:1267602185202438307>" + "emoji": "heads_item_26" }, { "id": 27, "name": "Monk", - "emoji": "<:heads_item_27:1267602170757382204>" + "emoji": "heads_item_27" }, { "id": 28, "name": "Santa", - "emoji": "<:heads_item_28:1267602156710658180>" + "emoji": "heads_item_28" }, { "id": 29, "name": "Fire Skull", - "emoji": "<:heads_item_29:1267602142454349824>" + "emoji": "heads_item_29" }, { "id": 30, "name": "Halloween", - "emoji": "<:heads_item_30:1267602129808392222>" + "emoji": "heads_item_30" }, { "id": 31, "name": "Mr. Chicken", - "emoji": "<:heads_item_31:1267602118135517244>" + "emoji": "heads_item_31" }, { "id": 32, "name": "Karateka", - "emoji": "<:heads_item_32:1267602101886910536>" + "emoji": "heads_item_32" }, { "id": 33, "name": "Mr. Chief", - "emoji": "<:heads_item_33:1267602087588397197>" + "emoji": "heads_item_33" }, { "id": 34, "name": "Julie", - "emoji": "<:heads_item_34:1267602074674139348>" + "emoji": "heads_item_34" }, { "id": 35, "name": "Gold", - "emoji": "<:heads_item_35:1267602061290373181>" + "emoji": "heads_item_35" }, { "id": 36, "name": "Samantha", - "emoji": "<:heads_item_36:1267602048078315663>" + "emoji": "heads_item_36" }, { "id": 37, "name": "Bruce", - "emoji": "<:heads_item_37:1267602035323179048>" + "emoji": "heads_item_37" }, { "id": 38, "name": "Mr. Strong", - "emoji": "<:heads_item_38:1267602021771513907>" + "emoji": "heads_item_38" }, { "id": 39, "name": "Neena", - "emoji": "<:heads_item_39:1267602007015817237>" + "emoji": "heads_item_39" }, { "id": 40, "name": "Kira", - "emoji": "<:heads_item_40:1267601987537473577>" + "emoji": "heads_item_40" }, { "id": 41, "name": "Scarlett", - "emoji": "<:heads_item_41:1267601974589788200>" + "emoji": "heads_item_41" }, { "id": 42, "name": "Beatrice", - "emoji": "<:heads_item_42:1267601960689991720>" + "emoji": "heads_item_42" }, { "id": 43, "name": "Sigrid", - "emoji": "<:heads_item_43:1267601945187582053>" + "emoji": "heads_item_43" }, { "id": 44, "name": "Nira", - "emoji": "<:heads_item_44:1267601933259112630>" + "emoji": "heads_item_44" }, { "id": 45, "name": "Kaneda", - "emoji": "<:heads_item_45:1267601919694606428>" + "emoji": "heads_item_45" }, { "id": 46, "name": "Kiroto", - "emoji": "<:heads_item_46:1267601907552096296>" + "emoji": "heads_item_46" }, { "id": 47, "name": "Ryo", - "emoji": "<:heads_item_47:1267601895329894440>" + "emoji": "heads_item_47" }, { "id": 48, "name": "Storm", - "emoji": "<:heads_item_48:1267601883954938059>" + "emoji": "heads_item_48" }, { "id": 49, "name": "Ra", - "emoji": "<:heads_item_49:1267601871669956850>" + "emoji": "heads_item_49" }, { "id": 50, "name": "Ari", - "emoji": "<:heads_item_50:1267601856859734108>" + "emoji": "heads_item_50" }, { "id": 51, "name": "Knight", - "emoji": "<:heads_item_51:1267601842544705567>" + "emoji": "heads_item_51" }, { "id": 52, "name": "Luna", - "emoji": "<:heads_item_52:1267601828988719227>" + "emoji": "heads_item_52" }, { "id": 53, "name": "Bea", - "emoji": "<:heads_item_53:1267601810990956668>" + "emoji": "heads_item_53" }, { "id": 54, "name": "Atria", - "emoji": "<:heads_item_54:1267601798097666192>" + "emoji": "heads_item_54" }, { "id": 55, "name": "Black Knight", - "emoji": "<:heads_item_55:1267601783400956037>" + "emoji": "heads_item_55" }, { "id": 56, "name": "Demonic black knight", - "emoji": "<:heads_item_56:1267601769538650387>" + "emoji": "heads_item_56" }, { "id": 57, "name": "Phamy", - "emoji": "<:heads_item_57:1267601753042456697>" + "emoji": "heads_item_57" } ] \ No newline at end of file diff --git a/src/images/unknown_item.png b/src/images/unknown_item.png deleted file mode 100644 index ca3e1bc3ca976c4e69658ad3b9404607f3cd8203..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 468 zcmV;_0W1EAP)Px$j!8s8R9HvNm%U2FKoo@yK0^X_f`wpZC5Q;NzJQeog0PKXV{0L{HkJ!kVxuqM z1BeJ#wibeQ@(_wMoZy`GaIcY_$u!F*^Kfa*TUtsYw8MiMR=57ho;;_z?pJ0nWddh$N6*fOkQTR1rWwkUHD^-6K7L zVgj&m+eTNTP2YW0tq8D3))v$GnkEvdMk4@q4V{yfRIwtW2ynz0|MqedCcZs6O$ihi zuzz(K6s>L?B=wD4Dk30YMa+(xAuxIY$Q=kNB6sJL5bXNyR``8=cuM|*#GeVwT>wnT z3n9nX_tCxV5j^KeRpgSu=mnq}KMR?G2W%sDrBK<%e;A5baCCIE^pFDL?*`%MoN z6Tlmhl?qe`Jm8Joy$@s;0A*eh=&$wx%l&xYp9hKwV5RD4hJO70w>*G)$9TRk%$$mg z3-A)*Zv?wrMa=V?HzVt`3=_^xfJKDgd+&sO6{vDi&zZ=71$+TORd$T%z*JQL0000< KMNUMnLSTY@Z^c>w diff --git a/src/index.js b/src/index.js index 81b966d..57fad0f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ -const {Client, Partials, IntentsBitField, Routes, ActivityType, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ActionRowBuilder, TextInputBuilder} = require("discord.js"); -const {get_first_chat_channel, is_my_developer, restrict_text} = require("./utils.js"); -const {ultra_list} = require("./ultralist.json"); +const {Client, Events, Partials, IntentsBitField, Routes, ActivityType} = require("discord.js"); +const {get_first_chat_channel, is_my_developer, update_application_emoji_cache, get_level_to_emojis, application_emoji_cache} = require("./utils.js"); +const database = require("./database/database.js"); const {get_ranking, ranking_command} = require("./commands/ranking.js"); const {get_help, help_command} = require("./commands/help.js"); @@ -52,12 +52,15 @@ const client = new Client({ client.login(process.env.TOKEN); client.on("ready", async (event) => { + database.init(); + console.log(`${event.user.tag} ready.`); client.user.setActivity("GoBattle.io", {type: ActivityType.Playing}); await client.application.fetch(); - + await update_application_emoji_cache(client.application); + try{ await client.rest.put( Routes.applicationCommands(client.user.id), @@ -78,28 +81,9 @@ client.on("ready", async (event) => { console.error(error); } } - - - //test - /*const guild = await client.guilds.fetch("380588354934276097"); - - guild.channels.cache.forEach((channel) => { - if (channel.type == 0){ - console.log(`${channel.name} (${channel.type}) - ${channel.id}`); - } - }); - - const gg = await client.channels.fetch("1148723323761606809"); - - gg.messages.fetch({limit: 100}).then(messages => { - console.log(`Received ${messages.size} messages`); - //Iterate through the messages here with the variable "messages". - messages.forEach(message => console.log(message.author.globalName, ": ", message.content)) - }) -*/ }); -client.on("guildCreate", async (guild) => { +client.on(Events.GuildCreate, async (guild) => { try { const channel = get_first_chat_channel(guild, client); @@ -111,82 +95,130 @@ client.on("guildCreate", async (guild) => { } }); -client.on("interactionCreate", async (interaction) => { - if (interaction.isButton()){ - return; - } +client.on(Events.InteractionCreate, async (interaction) => { + try{ + if (interaction.isChatInputCommand()){ + switch (interaction.commandName){ + case "item": + await get_item(interaction, client); + break; + case "user": + await get_user(interaction, client); + break; + case "server": + await get_server(interaction, client); + break; + case "ranking": + await get_ranking(interaction, client); + break; + case "get_date_new_king": + await get_date_new_king(interaction, client); + break; + case "info": + await get_info(interaction, client); + break; + case "help": + await get_help(interaction, client); + break; + case "ping": + await get_ping(interaction, client); + break; + case "leave": + await get_leave(interaction, client); + break; + case "asset": + await get_asset(interaction, client); + break; + case "dungeon": + await get_dungeon(interaction, client); + break; + case "setting": + await get_setting(interaction, client); + break; + case "echo": + await get_echo(interaction, client); + break; + case "get_ultrarare_drop_chance": + await get_ultrarare_drop_chance(interaction, client); + break; + default: + await interaction.reply({content: "This command no longer exists.", ephemeral: true}); + } + }else if (interaction.isModalSubmit()){ + switch (interaction.customId){ + case "login": + try{ + await interaction.deferReply({ephemeral: true}); - if (!interaction.isChatInputCommand()){ - return; - } + const email = interaction.fields.getTextInputValue("email"); + const password = interaction.fields.getTextInputValue("password"); - try{ - switch (interaction.commandName){ - case "item": - await get_item(interaction, client); - break; - case "user": - await get_user(interaction, client); - break; - case "server": - await get_server(interaction, client); - break; - case "ranking": - await get_ranking(interaction, client); - break; - case "get_date_new_king": - await get_date_new_king(interaction, client); - break; - case "info": - await get_info(interaction, client); - break; - case "help": - await get_help(interaction, client); - break; - case "ping": - await get_ping(interaction, client); - break; - case "leave": - await get_leave(interaction, client); - break; - case "asset": - await get_asset(interaction, client); - break; - case "dungeon": - await get_dungeon(interaction, client); - break; - case "setting": - await get_setting(interaction, client); - break; - case "echo": - await get_echo(interaction, client); - break; - case "get_ultrarare_drop_chance": - await get_ultrarare_drop_chance(interaction, client); - break; - default: - await interaction.reply({content: "This command no longer exists.", ephemeral: true}); + const platform = "Web"; + const request_info = { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + "email": email, + "password": password + }) + }; + + const response = await fetch(`https://gobattle.io/api.php/login?platform=${platform}&ud=`, request_info); + const data = await response.json(); + + if (!response.ok){ + if (data?.error == "Invalid credentials"){ + await interaction.editReply("# āš ļø Login error\nThe email or the passworld are incorrect."); + return; + } + + await interaction.editReply(`Impossible to connect. There is a problem with the GoBattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + const success = database.add_gobattle_accesse(interaction.user, data.id, data.token); + if (success){ + interaction.editReply("Your account has been successfully registered. You can log out at any time with `/user logout`."); + }else{ + interaction.editReply("Your account is already registered. You can log out at any time with `/user logout`."); + } + + /* + https://gobattle.io/api.php/recover?platform=Web&ud= + application/x-www-form-urlencoded + post email: sammarzin22@gmail.com + {id: "703", email: "sammarzin22@gmail.com"} + */ + }catch (error){ + await interaction.editReply(`An internal error has occurred...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } + break; + case "recover": + await interaction.reply({content: "Interaction not supported at this time.", ephemeral: true}); + break; + default: + await interaction.reply({content: "The form submission was not processed successfully because I am not familiar with the form.", ephemeral: true}); + } } }catch (error){ - console.error("Impossible to answer:", error); + console.error(error); } }); -client.on("messageCreate", async (msg) => { +client.on(Events.MessageCreate, async (msg) => { if (msg.author.bot){ return; } try{ - /* - if (msg.guildId == "1235330175449825310"){ - const gg = await client.channels.fetch("380588355403907082"); - await gg.send("Kuwazy told me to send you this message:\n" + msg.content); - } - */ - const command = msg.content.trim().toLowerCase(); + const command_info = msg.content.trim().split(" "); + const command = command_info[0].toLowerCase(); // Quick commands for development. + let content = ""; switch (command){ case "!gb_bot_guilds": if (!is_my_developer(client, msg.author)){ @@ -195,12 +227,12 @@ client.on("messageCreate", async (msg) => { } const guilds = client.guilds.cache; - let message = "# List of guilds I am in:\n\n"; + content = "# List of guilds I am in:\n\n"; for (const guild of guilds.values()){ - message += `* ${guild.name}: \`${guild.id}\`,\n`; + content += `* ${guild.name}: \`${guild.id}\`,\n`; } - message += `(${guilds.size} Guilds)`; - await msg.reply(message); + content += `(${guilds.size} Guilds)`; + await msg.reply(content); break; case "!get_off_this_server": @@ -209,12 +241,38 @@ client.on("messageCreate", async (msg) => { return; } - await msg.reply("Well, I'm leaving this guild because I seem to be disturbing... STFU."); + await msg.reply("Well, I'm leaving this guild because I seem to be disturbing..."); await msg.guild.leave(); + break; + case "!gb_emoji": + if (!is_my_developer(client, msg.author)){ + await msg.reply("You do not have permission to use this command."); + return; + } + + const nb_emoji = 60; + for (let i = 0; i < nb_emoji; i++){ + const emojis = Array.from(application_emoji_cache.values()); + const index = Math.floor(Math.random() * emojis.length); + + content += `${emojis[index]}`; + } + await msg.reply(content); + + break; + case "!gb_level_to_emojis": + if (!is_my_developer(client, msg.author)){ + await msg.reply("You do not have permission to use this command."); + return; + } + + const level = parseInt(command_info[1] || 0, 10); + await msg.reply(`Level ${level} is: (${get_level_to_emojis(level)})`); + break; } }catch (error){ - console.error("Impossible to answer:", error); + console.error(error); } }); diff --git a/src/level_emojis.json b/src/level_emojis.json new file mode 100644 index 0000000..5995884 --- /dev/null +++ b/src/level_emojis.json @@ -0,0 +1,22 @@ +[ + "compass_item102", + "compass_item103", + "compass_item104", + "compass_item105", + "compass_item106", + "compass_item107", + "compass_item108", + "compass_item109", + "compass_item110", + "compass_item111", + "compass_item112", + "compass_item113", + "compass_item114", + "compass_item115", + "compass_item116", + "compass_item117", + "compass_item118", + "compass_item119", + "compass_item120", + "compass_item121" +] diff --git a/src/ultralist.json b/src/ultralist.json index 8a3ec49..036a64e 100644 --- a/src/ultralist.json +++ b/src/ultralist.json @@ -10,7 +10,7 @@ "uses": 3, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_506:1267561503029067847>" + "emoji": "item_506" }, { "name": "Gobattle Ring", @@ -21,7 +21,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_512:1267561027055128599>" + "emoji": "item_512" }, { "name": "Firebreath Ring", @@ -32,7 +32,7 @@ "uses": 10, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_502:1267561518808039454>" + "emoji": "item_502" }, { "name": "Blue Ring", @@ -43,7 +43,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_520:1267561452672122972>" + "emoji": "item_520" }, { "name": "Red Ring", @@ -54,7 +54,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_515:1267561468811935774>" + "emoji": "item_515" }, { "name": "Bloodmoon Ring", @@ -65,7 +65,7 @@ "uses": 3, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_510:1267561480509718548>" + "emoji": "item_510" }, { "name": "Anti Freezing Glove", @@ -76,7 +76,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_431:1267569371455160390>" + "emoji": "item_431" }, { "name": "Instant Strength Glove", @@ -87,7 +87,7 @@ "uses": 7, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_435:1267569402941800468>" + "emoji": "item_435" }, { "name": "Epic Strength Glove", @@ -98,7 +98,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_441:1267569470382149804>" + "emoji": "item_441" }, { "name": "Gravity Feather", @@ -109,7 +109,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_754:1267573693458944010>" + "emoji": "item_754" }, { "name": "Team Gravity Feather", @@ -120,7 +120,7 @@ "uses": 3, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_749:1267561874350805043>" + "emoji": "item_749" }, { "name": "Hermes Boots", @@ -131,7 +131,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_133:1267565695907139708>" + "emoji": "item_133" }, { "name": "Speed Boots", @@ -142,7 +142,7 @@ "uses": 7, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_139:1267565745345134662>" + "emoji": "item_139" }, { "name": "Maximum Speed Boots", @@ -153,7 +153,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_140:1267565753297666099>" + "emoji": "item_140" }, { "name": "Normal Invisibility Cloak", @@ -164,7 +164,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_235:1267564036426436791>" + "emoji": "item_235" }, { "name": "Extreme Invisibility Cloak", @@ -175,7 +175,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_264:1267564565525299301>" + "emoji": "item_264" }, { "name": "Instant Defense Cloak", @@ -186,7 +186,7 @@ "uses": 7, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_236:1267564043107827744>" + "emoji": "item_236" }, { "name": "Epic Instant Defense Cloak", @@ -197,7 +197,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_262:1267564548672721007>" + "emoji": "item_262" }, { "name": "Normal Health Regeneration Cloak", @@ -208,7 +208,7 @@ "uses": 7, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_240:1267564050737532929>" + "emoji": "item_240" }, { "name": "Maximum Health Regeneration Cloak", @@ -219,7 +219,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_260:1267564530196680795>" + "emoji": "item_260" }, { "name": "Normal Venom Protection Cloak", @@ -230,7 +230,7 @@ "uses": 7, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_242:1267564066763706419>" + "emoji": "item_242" }, { "name": "Extreme Venom Protection Cloak", @@ -241,7 +241,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_261:1267564540917186580>" + "emoji": "item_261" }, { "name": "Maximum Fire Protection Cloak", @@ -252,7 +252,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_268:1267564588644307005>" + "emoji": "item_268" }, { "name": "Fire Protection Cloak", @@ -263,7 +263,7 @@ "uses": 7, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_249:1267564125827895436>" + "emoji": "item_249" }, { "name": "Breath Helmet", @@ -274,7 +274,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": null, - "emoji": "<:item_267:1267564398747320432>" + "emoji": "item_267" }, { "name": "Lava Armor Protector", @@ -285,7 +285,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_251:1267568057325322321>" + "emoji": "item_251" }, { "name": "Anti Fire Enchantment", @@ -296,7 +296,7 @@ "uses": 5, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_388:1267565242775371887>" + "emoji": "item_388" }, { "name": "Epic Invicibility Potion", @@ -307,7 +307,7 @@ "uses": 1, "is_relic": false, "max_in_inventory": null, - "emoji": "<:item_551:1267570796818206902>" + "emoji": "item_551" }, { "name": "Firebreath's Blood", @@ -318,7 +318,7 @@ "uses": 1, "is_relic": false, "max_in_inventory": null, - "emoji": "<:item_745:1267573567013519401>" + "emoji": "item_745" }, { "name": "Special Halloween Broom", @@ -329,7 +329,7 @@ "uses": 1, "is_relic": false, "max_in_inventory": null, - "emoji": "<:item_761:1267573805639929936>" + "emoji": "item_761" }, { "name": "Sleigh Enchantment", @@ -340,7 +340,7 @@ "uses": 1, "is_relic": false, "max_in_inventory": null, - "emoji": "<:item_395:1267569075194953868>" + "emoji": "item_395" }, { "name": "Special Christmas Hat", @@ -351,7 +351,7 @@ "uses": 1, "is_relic": false, "max_in_inventory": null, - "emoji": "<:item_764:1267563728409333873>" + "emoji": "item_764" }, { "name": "Peppermint Strike", @@ -362,7 +362,7 @@ "uses": 3, "is_relic": false, "max_in_inventory": 1, - "emoji": "<:item_801:1267574341202088118>" + "emoji": "item_801" }, { "name": "Dice of Destiny", @@ -370,10 +370,10 @@ "drops": null, "id": 182, "description": "Increases your luck 1%", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": 16, - "emoji": "<:item_810:1267563864900239381>" + "emoji": "item_810" }, { "name": "Iron Heart", @@ -381,10 +381,10 @@ "drops": null, "id": 183, "description": "Increases max health 2%", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": null, - "emoji": "<:item_811:1267563849608069120>" + "emoji": "item_811" }, { "name": "Rejuvenation Gem", @@ -392,10 +392,10 @@ "drops": null, "id": 184, "description": "Increases regeneration 2%", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": null, - "emoji": "<:item_812:1267563856541126686>" + "emoji": "item_812" }, { "name": "Inferno Touch", @@ -403,10 +403,10 @@ "drops": null, "id": 185, "description": "1% chance of burning effect (PVP)", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": null, - "emoji": "<:item_813:1267563837427552337>" + "emoji": "item_813" }, { "name": "Greed's Grip", @@ -414,10 +414,10 @@ "drops": null, "id": 186, "description": "Attract coins close to you", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": null, - "emoji": "<:item_814:1267574519376384122>" + "emoji": "item_814" }, { "name": "Dwarf's Strength", @@ -425,10 +425,10 @@ "drops": null, "id": 187, "description": "Increases reach of Axes 8%", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": 16, - "emoji": "<:item_815:1267574531015573524>" + "emoji": "item_815" }, { "name": "Talisman of the Phoenix", @@ -436,10 +436,10 @@ "drops": null, "id": 161, "description": null, - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": null, - "emoji": "<:item_798:1267574301750722651>" + "emoji": "item_798" }, { "name": "Flying Skill", @@ -447,10 +447,10 @@ "drops": null, "id": 198, "description": "Gives you the skill to dash vertically with your wings", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": 1, - "emoji": "<:item_820:1267560357895868529>" + "emoji": "item_820" }, { "name": "Dodge Charm", @@ -458,10 +458,10 @@ "drops": "All", "id": 189, "description": "1% chance of dodging any attack", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": 1, - "emoji": "<:item_817:1267574569955233802>" + "emoji": "item_817" }, { "name": "Gem Dust", @@ -469,10 +469,10 @@ "drops": "Stahmite Mines", "id": 191, "description": "Up to 5 dyams (+ Luck) per gem in chests", - "uses": null, + "uses": -1, "is_relic": true, "max_in_inventory": 25, - "emoji": "<:item_819:1267560608186761359>" + "emoji": "item_819" } ] } diff --git a/src/utils.js b/src/utils.js index 40574dd..049f777 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,7 @@ -const {ChannelType, PermissionFlagsBits, User, Team} = require("discord.js"); +const {ChannelType, PermissionFlagsBits, User, Team, Emoji} = require("discord.js"); +const level_emojis = require("./level_emojis.json"); + +const application_emoji_cache = new Map(); function restrict_text(str, nb){ if (str.length > nb){ @@ -31,6 +34,19 @@ function format_score(score){ return score_formated; } +function format_score_with_commas(score){ + const score_string = score.toString(); + let score_formated = ""; + for (let i = score_string.length - 1, count = 0; i >= 0; i--, count++){ + if (count && count % 3 === 0){ + score_formated = "," + score_formated; + } + score_formated = score_string[i] + score_formated; + } + + return score_formated; +} + function format_speed_run_time(time){ const total_milliseconds = Math.floor(time * 1000); @@ -53,6 +69,24 @@ function get_level_adventurer(experience){ return Math.min(20, Math.floor(Math.pow(experience / 100, 1 / 3)) + 1); } +function get_level_to_emojis(level){ + level = Math.floor(level); + + let emojis = ""; + + const landing = Math.floor(level / level_emojis.length); + for (let i = 0; i < landing; i++){ + emojis += application_emoji_cache.get(level_emojis.at(-1)).toString(); + } + + const rest = level % level_emojis.length; + if (rest){ + emojis += application_emoji_cache.get(level_emojis[rest - 1]).toString(); + } + + return emojis; +} + function get_utc_date(){ const today = new Date(); return new Date(today.getTime() + (today.getTimezoneOffset() * 60000)); @@ -104,7 +138,7 @@ function is_my_developer(client, user){ let is_member = false; const members_id = client.application.owner.members.keys(); - + for (const user_id of members_id){ is_member = user_id == user.id; @@ -117,14 +151,39 @@ function is_my_developer(client, user){ return false; } +async function update_application_emoji_cache(application){ + const api_version = 10; + const headers = { + "Authorization": `Bot ${application.client.token}` + }; + + const response = await fetch(`https://discord.com/api/v${api_version}/applications/${application.id}/emojis`, {method: "GET", headers: headers}); + if (!response.ok){ + return; + } + + const data = await response.json(); + application_emoji_cache.clear(); + for (const item of data.items){ + const emoji = new Emoji(application.client, item); + application_emoji_cache.set(emoji.name, emoji); + } + + return application_emoji_cache; +} + exports.restrict_text = restrict_text; exports.format_score = format_score; +exports.format_score_with_commas = format_score_with_commas; exports.format_speed_run_time = format_speed_run_time; exports.get_level = get_level; exports.get_level_adventurer = get_level_adventurer; +exports.get_level_to_emojis = get_level_to_emojis; exports.get_utc_date = get_utc_date; exports.get_utf_time_next_king = get_utf_time_next_king; exports.sum = sum; exports.send_echo = send_echo; exports.get_first_chat_channel = get_first_chat_channel; -exports.is_my_developer = is_my_developer; \ No newline at end of file +exports.is_my_developer = is_my_developer; +exports.update_application_emoji_cache = update_application_emoji_cache; +exports.application_emoji_cache = application_emoji_cache; From dce47947bc12819ada8e87024192538b5261e1ba Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Sat, 3 Aug 2024 02:54:14 +0200 Subject: [PATCH 32/54] fix login bugs --- src/commands/user.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/commands/user.js b/src/commands/user.js index 3e50daf..036d896 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -227,6 +227,12 @@ async function get_friend(interaction, client){ } async function get_login(interaction, client){ + const gobattle_user_id = database.discord_user_to_gobattle_user_id(interaction.user); + if (gobattle_user_id){ + interaction.reply({content: `You are already using user account _#${gobattle_user_id}_. Use the \`/user logout\` command before registering another account.`, ephemeral: true}); + return; + } + const modal = new ModalBuilder(); modal.setCustomId("login"); modal.setTitle("Login to Gobattle account."); @@ -437,7 +443,7 @@ async function get_friend_pending_count(interaction, client){ async function get_friend_pending_requests(interaction, client, type){ const user_id = interaction.options.get("user_id")?.value; - const public = type != "incoming" || database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id; + const public = type != "incoming" || database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id || is_my_developer(client, interaction.user); await interaction.deferReply({ephemeral: public}); const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); @@ -555,7 +561,7 @@ async function get_friend_pending_requests(interaction, client, type){ async function get_friend_list(interaction, client){ const user_id = interaction.options.get("user_id")?.value; - const public = database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id; + const public = database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id || is_my_developer(client, interaction.user); await interaction.deferReply({ephemeral: public}); From a980a0960baaf069fcfccf0cf0245f49eba2b7f4 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Sat, 3 Aug 2024 02:57:58 +0200 Subject: [PATCH 33/54] fix readme.md nodejs version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9666089..b168eb7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The door is open to all potential contributors. The project is now managed by Sh ## Configuration and prerequisites - You must have a Discord app that you have previously created in the Discord Developer Portal. -- You also need to have nodejs v20 or higher installed on your server. +- You also need to have nodejs v22 or higher installed on your server. - You must have the application token and configure the `.env` file accordingly. ## To start the project, open a terminal at the root of the project and use the following commands: From 6d9c4ea08f3f04102074e552518ea7b39ffc86fa Mon Sep 17 00:00:00 2001 From: Kuwazy <70151472+Marzin-bot@users.noreply.github.com> Date: Sat, 3 Aug 2024 03:03:38 +0200 Subject: [PATCH 34/54] Create .env --- .env | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..33f5f28 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +# Discord plaplication token. This token is private and should not be shared! +TOKEN = InsertBotTokenHere + +# Guild identifiers can use critical administration commands. +# The default is the Official GoBattle.io Discord server. +# You can also add your own server for debugging and tuning purposes of this application. +GUILD_ADMIN_ID = ["380588354934276097"] # JSON format. From 0653fa60a29f9bae36d58f8841af1726bb416c29 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Tue, 6 Aug 2024 01:32:49 +0200 Subject: [PATCH 35/54] added friend orders and optimized embeds layout. --- src/commands/dungeon.js | 70 +---- src/commands/item.js | 72 +---- src/commands/ranking.js | 333 ++--------------------- src/commands/server.js | 69 +---- src/commands/user.js | 564 +++++++++++++++++++++++++++++---------- src/database/database.js | 14 + src/head_list.json | 4 +- src/index.js | 60 ++++- src/utils.js | 79 +++++- 9 files changed, 599 insertions(+), 666 deletions(-) diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index ac91f15..02e6adb 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -1,5 +1,5 @@ -const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); -const {restrict_text} = require("../utils.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {restrict_text, send_embed_layout} = require("../utils.js"); const {dungeon_list} = require("../dungeon_list.json"); const dungeon_command = new SlashCommandBuilder(); @@ -51,7 +51,7 @@ async function get_list(interaction, _client){ const embed = new EmbedBuilder(); embed.setTitle("šŸ° Dungeon List šŸ°"); - const header_description = "Note that this list is not updated in real time unlike other lists. This is a pre-list awaiting the GoBattle.io API update.\n"; + const header_description = "Note that this list is not updated in real time unlike other lists. This is a pre-list awaiting the GoBattle.io API update."; const max_items_by_pages = 7; const pages = new Array(Math.ceil(dungeon_list.length / max_items_by_pages)); @@ -59,81 +59,21 @@ async function get_list(interaction, _client){ for (let i = 0; i < dungeon_list.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const dungeon_data = dungeon_list[i]; pages[current_page] += `* **${restrict_text(dungeon_data.name || "*Unknow?*", 45)}**#${dungeon_data.id}: \`${dungeon_data.min_level || "Unknow?"}šŸ’Ŗ\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); - embed.addFields( {name: "> šŸ”¢ __Number of dungeons__", value: `> ${dungeon_list.length}`, inline: true}, {name: "> __Min level__", value: "> šŸ’Ŗ", inline: true} ); - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages, header_description); } exports.get_dungeon = get_dungeon; diff --git a/src/commands/item.js b/src/commands/item.js index 3da27a9..d912c36 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -1,6 +1,6 @@ -const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); const {ultra_list} = require("../ultralist.json"); -const {restrict_text, sum, format_score, format_score_with_commas, application_emoji_cache} = require("../utils.js"); +const {restrict_text, sum, format_score, format_score_with_commas, send_embed_layout, application_emoji_cache} = require("../utils.js"); const items_map = new Map(); for (const item_data of ultra_list){ @@ -137,7 +137,7 @@ async function get_ultrarare_drops(interaction, client){ const chest_emoji = application_emoji_cache.get("item_760") || "🧰"; const total_ultrarare_formatted = format_score_with_commas(total_ultrarare); - const header_description = `Results are calculated based on **${format_score_with_commas(total)}** samples (*chest opened by players ${chest_emoji}*) including **${total_ultrarare_formatted}** ultrarare drops.\n`; + const header_description = `Results are calculated based on **${format_score_with_commas(total)}** samples (*chest opened by players ${chest_emoji}*) including **${total_ultrarare_formatted}** ultrarare drops.`; const max_items_by_pages = 7; const pages = new Array(Math.ceil(values.length / max_items_by_pages)); const unknown_item_emoji = application_emoji_cache.get("item_91") || "šŸ“¦"; @@ -148,7 +148,7 @@ async function get_ultrarare_drops(interaction, client){ for (let i = 0; i < values.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const [key, value] = drops_entries[i]; @@ -157,75 +157,15 @@ async function get_ultrarare_drops(interaction, client){ pages[current_page] += `* ${item_emoji} **${restrict_text(ultra_info?.name || "*Unknow?*", 45)}**#${key}: \`${format_score(value)} (${(value / total_ultrarare * 100).toPrecision(2)}%)\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || "***There are no items to display in this list at the moment...***"); - embed.addFields( {name: "> 🌟 __Ultrarare__", value: `> ${total_ultrarare_formatted} (${(drops_ratio * 100).toPrecision(2)}%)`, inline: true}, {name: `> ${unknown_item_emoji} __Other__`, value: `> ${format_score_with_commas(total - total_ultrarare)} (${((1 - drops_ratio) * 100).toPrecision(2)}%)`, inline: true} ); - - const nb_pages = pages.length || 1; - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - content: "Special mention to _Sage of the Ivy_ for providing some metadata on most of the game's ultrarares while waiting for the GoBattle API update!\nThanks to him!", - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + const message_content = "Special mention to _Sage of the Ivy_ for providing some metadata on most of the game's ultrarares while waiting for the GoBattle API update!\nThanks to him!"; + await send_embed_layout(interaction, embed, pages, header_description, message_content); }catch(error){ await interaction.editReply(`Unable to generate template.\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/ranking.js b/src/commands/ranking.js index 0427472..d990134 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -1,5 +1,5 @@ -const {EmbedBuilder, SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); -const {restrict_text, get_level, get_level_adventurer, format_score, format_speed_run_time, application_emoji_cache} = require("../utils.js"); +const {EmbedBuilder, SlashCommandBuilder} = require("discord.js"); +const {restrict_text, get_level, get_level_adventurer, format_score, format_speed_run_time, send_embed_layout, application_emoji_cache} = require("../utils.js"); const head_list = require("../head_list.json"); const heads_map = new Map(); @@ -108,7 +108,7 @@ async function get_king(interaction, client){ const ranking = data?.ranking; - const header_description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command.\n"; + const header_description = "You can earn reputation points by killing enemies, bosses, completing dungeons, and killing other players in the arena.\nThe new king of Valdoran will be chosen every Sunday at midnight (UTC Time) and will earn 150 diamonds, ascend the throne, wear the crown and can use the `/coin` command."; const max_items_by_pages = 7; const pages = new Array(Math.ceil(ranking.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -117,7 +117,7 @@ async function get_king(interaction, client){ for (let i = 0; i < ranking.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = ranking[i]; @@ -126,74 +126,14 @@ async function get_king(interaction, client){ pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.reputation)} REP\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); - embed.addFields( {name: "> šŸ—“ __Week__", value: `> ${data?.week || "_Unknown?_"}`, inline: true}, {name: "> šŸ—“ __Year__", value: `> ${data?.year || "_Unknown?_"}`, inline: true} ); - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages, header_description); }catch(error){ await interaction.editReply(`Failed to generate King ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -233,7 +173,6 @@ async function get_general(interaction, client, type){ embed.setTitle(`${general_emoji} General Ranking ${general_emoji}`); } - const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -247,7 +186,7 @@ async function get_general(interaction, client, type){ for (let i = 0; i < data.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = data[i]; @@ -258,69 +197,9 @@ async function get_general(interaction, client, type){ pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**: ${coins_emoji}\`${format_score(field?.coins)}\` ${kills_emoji}\`${format_score(field?.kills)}\` ${deaths_emoji}\`${format_score(field?.deaths)}\` ${experience_emoji}\`${format_score(field?.experience)}\` ${level_emoji}\`${level}\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); - - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages); }catch(error){ await interaction.editReply(`Failed to generate Weekly ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -344,7 +223,6 @@ async function get_adventurer(interaction, client){ embed.setTitle("🤠 Adventurer Ranking 🤠"); - const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -353,7 +231,7 @@ async function get_adventurer(interaction, client){ for (let i = 0; i < data.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = data[i]; @@ -363,69 +241,9 @@ async function get_adventurer(interaction, client){ pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1]); - - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages); }catch(error){ await interaction.editReply(`Failed to generate Adventurer ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -450,7 +268,6 @@ async function get_relic_hunter(interaction, client){ const relic_hunter_emoji = application_emoji_cache.get("item_809") || "šŸŽ’"; embed.setTitle(`${relic_hunter_emoji} Relic Hunter Ranking ${relic_hunter_emoji}`); - const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -459,7 +276,7 @@ async function get_relic_hunter(interaction, client){ for (let i = 0; i < data.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = data[i]; @@ -469,69 +286,9 @@ async function get_relic_hunter(interaction, client){ pages[current_page] += `${i + 1}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${format_score(field?.score)} EXP (${level} LVL)\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); - - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages); }catch(error){ await interaction.editReply(`Failed to generate Hunter Ranking ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -559,7 +316,7 @@ async function get_speedrun(interaction, client){ const ranking = data.ranking; - const header_description = "Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!\n"; + const header_description = "Do you also want to be included in the leaderboard?\nUse the \`/speedrun\` command on GoBattle.io to start a speedrun session and try to do better than the others!"; const max_items_by_pages = 7; const pages = new Array(Math.ceil(ranking.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -569,7 +326,7 @@ async function get_speedrun(interaction, client){ for (let i = 0; i < list_size; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = ranking[i]; @@ -579,74 +336,14 @@ async function get_speedrun(interaction, client){ pages[current_page] += `${field?.rank}. ${head_emoji} **${restrict_text(field?.nick, 20)}**#${field?.id}: \`${time}\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); - embed.addFields( {name: "> šŸ° __Dungeon name__", value: `> **${data.name || "_Unknown?_"}**`, inline: true}, {name: "> šŸ·ļø __Dungeon ID__", value: `> ${dungeon_id}`, inline: true} ); - - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - embed.setTimestamp(); - - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } + embed.setTimestamp(); - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages, header_description); }catch(error){ await interaction.editReply(`Failed to generate Speedrun ranking...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/server.js b/src/commands/server.js index 7bf8339..5379693 100644 --- a/src/commands/server.js +++ b/src/commands/server.js @@ -1,5 +1,5 @@ -const {SlashCommandBuilder, EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); -const {restrict_text} = require("../utils.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {restrict_text, send_embed_layout} = require("../utils.js"); const server_command = new SlashCommandBuilder(); server_command.setName("server"); @@ -71,7 +71,6 @@ async function get_list(interaction, client){ embed.setTitle("šŸ–„ļø Server List šŸ–„ļø"); - const header_description = ""; const max_items_by_pages = 7; const pages = new Array(Math.ceil(server_list.length / max_items_by_pages)); @@ -79,7 +78,7 @@ async function get_list(interaction, client){ for (var i = 0; i < server_list.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = server_list[i]; @@ -98,9 +97,6 @@ async function get_list(interaction, client){ pages[current_page] += `* **${restrict_text(field?.friendlyName, 25)}**#${field?.id}: \`${field?.version} šŸ”\` \`${field?.admin} šŸ› ļø\` \`${(is_online ? "Online" : "Down")} 🌐\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); - embed.addFields( {name: "> šŸŽ® __Platform__", value: `> ${platform}`, inline: true}, {name: "> šŸ”ƒ __Requested version__", value: `> ${version.toString()}`, inline: true}, @@ -109,66 +105,9 @@ async function get_list(interaction, client){ {name: "> __Server status__", value: "> 🌐", inline: true} ); - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages); }catch(error){ await interaction.editReply(`Unable to retrieve server list.\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/user.js b/src/commands/user.js index 036d896..3bbfa70 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -1,5 +1,5 @@ -const {EmbedBuilder, SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType, ModalBuilder, TextInputBuilder, TextInputStyle} = require("discord.js"); -const {is_my_developer, restrict_text, format_score, format_score_with_commas, get_level, get_level_to_emojis, application_emoji_cache} = require("../utils.js"); +const {EmbedBuilder, SlashCommandBuilder, ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle} = require("discord.js"); +const {is_my_developer, restrict_text, format_score, format_score_with_commas, get_level, get_level_to_emojis, send_embed_layout, application_emoji_cache} = require("../utils.js"); const database = require("../database/database.js"); const head_list = require("../head_list.json"); @@ -25,6 +25,12 @@ user_command.addSubcommand((subcommand) => { return subcommand; }); +user_command.addSubcommand((subcommand) => { + subcommand.setName("recover"); + subcommand.setDescription("Recover a GoBattle.io account whose password you forgot."); + + return subcommand; +}); user_command.addSubcommand((subcommand) => { subcommand.setName("info"); subcommand.setDescription("Get general information about a user."); @@ -85,6 +91,35 @@ user_command.addSubcommand((subcommand) => { return subcommand; }); +user_command.addSubcommand((subcommand) => { + subcommand.setName("gobattle_to_discord"); + subcommand.setDescription("Get Discord user from GoBattle user."); + + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); +user_command.addSubcommand((subcommand) => { + subcommand.setName("discord_to_gobattle"); + subcommand.setDescription("Get GoBattle user from Discord user."); + + subcommand.addUserOption((option) => { + option.setName("user"); + option.setDescription("Discord user."); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); user_command.addSubcommandGroup((friend_command) => { friend_command.setName("friend"); friend_command.setDescription("Command relating to friends in the game."); @@ -158,6 +193,62 @@ user_command.addSubcommandGroup((friend_command) => { return subcommand; }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("delete"); + subcommand.setDescription("Remove a user from your friends list."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("cancel"); + subcommand.setDescription("Cancel the friend request sent to a user."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("accept"); + subcommand.setDescription("Accept a friend request from a user."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); + friend_command.addSubcommand((subcommand) => { + subcommand.setName("ignore"); + subcommand.setDescription("Ignore a friend request from a user."); + subcommand.addNumberOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + + return subcommand; + }); return friend_command; }); @@ -174,6 +265,9 @@ async function get_user(interaction, client){ case "logout": await get_logout(interaction, client); break; + case "recover": + await get_recover(interaction, client); + break; case "info": await get_info(interaction, client); break; @@ -186,6 +280,12 @@ async function get_user(interaction, client){ case "inventory": await get_inventory(interaction, client); break; + case "gobattle_to_discord": + await get_gobattle_to_discord(interaction, client); + break; + case "discord_to_gobattle": + await get_discord_to_gobattle(interaction, client); + break; default: await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); } @@ -213,20 +313,26 @@ async function get_friend(interaction, client){ await get_friend_list(interaction, client); break; case "add": - await interaction.reply("This subcommand is under development."); + await get_friend_add(interaction, client); + break; + case "delete": + await get_friend_delete(interaction, client); + break; + case "cancel": + await get_friend_cancel(interaction, client); break; - case "accepte": - await interaction.reply("This subcommand is under development."); + case "accept": + await get_friend_accept(interaction, client); break; case "ignore": - await interaction.reply("This subcommand is under development."); + await get_friend_ignore(interaction, client); break; default: await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); } } -async function get_login(interaction, client){ +async function get_login(interaction, _client){ const gobattle_user_id = database.discord_user_to_gobattle_user_id(interaction.user); if (gobattle_user_id){ interaction.reply({content: `You are already using user account _#${gobattle_user_id}_. Use the \`/user logout\` command before registering another account.`, ephemeral: true}); @@ -257,7 +363,7 @@ async function get_login(interaction, client){ await interaction.showModal(modal); } -async function get_logout(interaction, client){ +async function get_logout(interaction, _client){ const succes = database.remove_gobattle_accesse(interaction.user); if (succes){ @@ -267,6 +373,24 @@ async function get_logout(interaction, client){ } } +async function get_recover(interaction, _client){ + const modal = new ModalBuilder(); + modal.setCustomId("recover"); + modal.setTitle("Recover your Gobattle account."); + + const email_input = new TextInputBuilder(); + email_input.setCustomId("email"); + email_input.setLabel("Email (Used on GoBattle.io)"); + email_input.setStyle(TextInputStyle.Short); + email_input.setRequired(true); + + const email_action_row = new ActionRowBuilder().addComponents(email_input); + + modal.addComponents(email_action_row); + + await interaction.showModal(modal); +} + async function get_info(interaction, client){ await interaction.deferReply(); @@ -292,6 +416,7 @@ async function get_info(interaction, client){ if (data?.error == "Invalid token"){ await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; } if (!response.ok){ @@ -315,16 +440,23 @@ async function get_info(interaction, client){ embed.setDescription(`${get_level_to_emojis(Math.min(level, 2000))}\n${restrict_text("_This user has no description..._", 250)}`); embed.setColor(0x500000); + const id_emoji = application_emoji_cache.get("item_348") || "šŸ·ļø"; + const coins_emoji = application_emoji_cache.get("coin") || "šŸŖ™"; + const diamonds_emoji = "šŸ’Ž"; + const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ’Ŗ"; + const experience_emoji = application_emoji_cache.get("fuego") || "šŸ”„"; + const role_emoji = application_emoji_cache.get("item_36") || "šŸŽ–"; + embed.addFields( - {name: "> šŸ·ļø __ID__", value: `> ${data.user.id}`, inline: true}, - {name: "> šŸŖ™ __Coins__", value: `> ${format_score_with_commas(data.user.coins)}`, inline: true}, - {name: "> šŸ’Ž __Diamonds__", value: `> ***${format_score_with_commas(data.user.diamonds)}***`, inline: true} + {name: `> ${id_emoji} __Identifier__`, value: `> ${data.user.id}`, inline: true}, + {name: `> ${coins_emoji} __Coins__`, value: `> ${format_score_with_commas(data.user.coins)}`, inline: true}, + {name: `> ${diamonds_emoji} __Diamonds__`, value: `> ***${format_score_with_commas(data.user.diamonds)}***`, inline: true} ); embed.addFields( - {name: "> šŸ’Ŗ __LVL__", value: `> ${format_score_with_commas(level)}`, inline: true}, - {name: "> šŸ”„ __EXP__", value: `> ${format_score_with_commas(data.user.experience)}`, inline: true}, - {name: "> šŸŽ– __Role__", value: `> ${data.user.streamer ? `${streamer_emoji} Streamer` : "Player"}`, inline: true} + {name: `> ${level_emoji} __Level__`, value: `> ${format_score_with_commas(level)}`, inline: true}, + {name: `> ${experience_emoji} __Experience__`, value: `> ${format_score_with_commas(data.user.experience)}`, inline: true}, + {name: `> ${role_emoji} __Role__`, value: `> ${data.user.streamer ? `${streamer_emoji} Streamer` : "Player"}`, inline: true} ); const equipped_attack = data.user.attack - data.user.base_attack; @@ -355,16 +487,23 @@ async function get_info(interaction, client){ const formatted_equipped_regeneration = equipped_regeneration > 0 ? "+" + equipped_regeneration : equipped_regeneration; const formatted_equipped_speed = equipped_speed > 0 ? "+" + equipped_speed : equipped_speed; + const attack_emoji = application_emoji_cache.get("compass_item95") || "āš”ļø"; + const defense_emoji = application_emoji_cache.get("item_119") || "šŸ›”ļø"; + const luck_emoji = application_emoji_cache.get("item_478") || "šŸ€"; + const hp_emoji = application_emoji_cache.get("heart") || "ā¤ļø"; + const regeneration_emoji = application_emoji_cache.get("item_622") || "ā¤ļøā€šŸ©¹"; + const speed_emoji = application_emoji_cache.get("item_122") || "⚔"; + embed.addFields( - {name: "> āš”ļø __ATT__", value: `> **${formatted_attack}** _(Base: ${formatted_base_attack}, Equipped: ${formatted_equipped_attack})_`, inline: true}, - {name: "> šŸ›”ļø __DEF__", value: `> **${formatted_defense}** _(Base: ${formatted_base_defense}, Equipped: ${formatted_equipped_defense})_`, inline: true}, - {name: "> šŸ€ __LCK__", value: `> **${formatted_luck}** _(Base: ${formatted_base_luck}, Equipped: ${formatted_equipped_luck})_`, inline: true} + {name: `> ${attack_emoji} __ATT__`, value: `> **${formatted_attack}** _(Base: ${formatted_base_attack}, Equipped: ${formatted_equipped_attack})_`, inline: true}, + {name: `> ${defense_emoji} __DEF__`, value: `> **${formatted_defense}** _(Base: ${formatted_base_defense}, Equipped: ${formatted_equipped_defense})_`, inline: true}, + {name: `> ${luck_emoji} __LCK__`, value: `> **${formatted_luck}** _(Base: ${formatted_base_luck}, Equipped: ${formatted_equipped_luck})_`, inline: true} ); embed.addFields( - {name: "> ā¤ļø __MHP__", value: `> **${formatted_hp}** _(Base: ${formatted_base_hp}, Equipped: ${formatted_equipped_hp})_`, inline: true}, - {name: "> ā¤ļøā€šŸ©¹ __RGN__", value: `> **${formatted_regeneration}** _(Base: ${formatted_base_regeneration}, Equipped: ${formatted_equipped_regeneration})_`, inline: true}, - {name: "> ⚔ __SPD__", value: `> **${formatted_speed}** _(Base: ${formatted_base_speed}, Equipped: ${formatted_equipped_speed})_`, inline: true} + {name: `> ${hp_emoji} __MHP__`, value: `> **${formatted_hp}** _(Base: ${formatted_base_hp}, Equipped: ${formatted_equipped_hp})_`, inline: true}, + {name: `> ${regeneration_emoji} __RGN__`, value: `> **${formatted_regeneration}** _(Base: ${formatted_base_regeneration}, Equipped: ${formatted_equipped_regeneration})_`, inline: true}, + {name: `> ${speed_emoji} __SPD__`, value: `> **${formatted_speed}** _(Base: ${formatted_base_speed}, Equipped: ${formatted_equipped_speed})_`, inline: true} ); /* @@ -398,20 +537,53 @@ async function get_info(interaction, client){ } } -async function get_king(interaction, client){ +async function get_king(interaction, _client){ await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); } -async function get_bank(interaction, client){ +async function get_bank(interaction, _client){ const user_id = interaction.options.get("user_id")?.value; await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); } -async function get_inventory(interaction, client){ +async function get_inventory(interaction, _client){ const user_id = interaction.options.get("user_id")?.value; await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); } +async function get_gobattle_to_discord(interaction, client){ + await interaction.deferReply(); + + const user_id = interaction.options.get("user_id")?.value; + const discord_user_id = database.gobattle_user_id_to_discord_user_id(user_id); + + if (!discord_user_id){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + const user_discord_promise = client.users.fetch(discord_user_id.toString()); + user_discord_promise.then(async (user_discord) => { + await interaction.editReply(`GoBattle account _#${user_id}_ belongs to ${user_discord}.`); + }).catch(async (_error) => { + await interaction.editReply(`GoBattle account _#${user_id}_ belongs to an unknown or deleted user account. The user must log in to their account with \`/user login\`.`); + }); +} + +async function get_discord_to_gobattle(interaction, _client){ + await interaction.deferReply(); + + const discord_user = interaction.options.get("user").user; + const gobattle_user_id = database.discord_user_to_gobattle_user_id(discord_user); + + if (!gobattle_user_id){ + await interaction.editReply(`The GoBattle.io session for ${discord_user} is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + await interaction.editReply(`The Discord account ${discord_user} belongs to the GoBattle account _#${gobattle_user_id}_.`); +} + async function get_friend_pending_count(interaction, client){ await interaction.deferReply(); @@ -430,6 +602,7 @@ async function get_friend_pending_count(interaction, client){ if (data?.error == "Invalid token"){ await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; } if (!response.ok){ @@ -443,8 +616,8 @@ async function get_friend_pending_count(interaction, client){ async function get_friend_pending_requests(interaction, client, type){ const user_id = interaction.options.get("user_id")?.value; - const public = type != "incoming" || database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id || is_my_developer(client, interaction.user); - await interaction.deferReply({ephemeral: public}); + const public = type != database.gobattle_user_id_to_discord_user_id(user_id)?.toString() == interaction.user.id || is_my_developer(client, interaction.user); + await interaction.deferReply({ephemeral: public && type == "incoming"}); const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); if (!gobattle_token){ @@ -464,6 +637,7 @@ async function get_friend_pending_requests(interaction, client, type){ if (data?.error == "Invalid token"){ await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; } if (!response.ok){ @@ -476,7 +650,7 @@ async function get_friend_pending_requests(interaction, client, type){ embed.setTitle(`${friend_emoji} Friend ${type} requests ${friend_emoji}`); const requests = data[type]; - const header_description = `User _#${user_id}_ has **${requests.length}** ${type} friend requests.\n`; + const header_description = `User _#${user_id}_ has **${requests.length}** ${type} friend requests.`; const max_items_by_pages = 7; const pages = new Array(Math.ceil(requests.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -485,7 +659,7 @@ async function get_friend_pending_requests(interaction, client, type){ for (let i = 0; i < requests.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = requests[i]; @@ -494,69 +668,9 @@ async function get_friend_pending_requests(interaction, client, type){ pages[current_page] += `* ${head_emoji} **${field?.nick ? restrict_text(field?.nick, 20) : "?"}**#${field?.id}\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); - - const nb_pages = pages.length || 1; - - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); embed.setTimestamp(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); - - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); - - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); - - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); - - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); - } - - return result; - } - - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; - } - - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); - - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); - } - } - - await button_interaction_logic(response_interaction); + await send_embed_layout(interaction, embed, pages, header_description); } async function get_friend_list(interaction, client){ @@ -583,6 +697,7 @@ async function get_friend_list(interaction, client){ if (data?.error == "Invalid token"){ await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; } if (!response.ok){ @@ -594,7 +709,7 @@ async function get_friend_list(interaction, client){ const friend_emoji = "šŸ¤"; embed.setTitle(`${friend_emoji} Friends list ${friend_emoji}`); - const header_description = `User _#${user_id}_ has **${data.length}/100** friends.\n`; + const header_description = `User _#${user_id}_ has **${data.length}/100** friends.`; const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; @@ -606,7 +721,7 @@ async function get_friend_list(interaction, client){ for (let i = 0; i < data.length; i++){ if (Math.floor(i / max_items_by_pages) != current_page){ current_page++; - pages[current_page] = header_description; + pages[current_page] = ""; } const field = data[i]; @@ -620,69 +735,246 @@ async function get_friend_list(interaction, client){ pages[current_page] += `* ${head_emoji} ${false && field?.status == 1 ? streamer_emoji : ""}**${field?.nick ? restrict_text(field?.nick, 20) : "?"}**#${field?.id}: ${online_label} ${experience_emoji}\`${format_score(field?.experience)}\` ${level_emoji}\`${level}\`\n`; } - current_page = 1; - embed.setDescription(pages[current_page - 1] || header_description + "***There are no items to display in this list at the moment...***"); + embed.setTimestamp(); - const nb_pages = pages.length || 1; + await send_embed_layout(interaction, embed, pages, header_description); +} - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - embed.setTimestamp(); +async function get_friend_add(interaction, client){ + await interaction.deferReply(); - const previous_button = new ButtonBuilder(); - previous_button.setCustomId("previous"); - previous_button.setEmoji("ā—€ļø"); - previous_button.setStyle(ButtonStyle.Primary); - previous_button.setDisabled(current_page == 1); + try{ + const user_id = interaction.options.get("user_id")?.value; + + const my_gobattle_user_id = database.discord_user_to_gobattle_user_id(interaction.user); + if (!my_gobattle_user_id){ + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + } - const next_button = new ButtonBuilder(); - next_button.setCustomId("next"); - next_button.setEmoji("ā–¶ļø"); - next_button.setStyle(ButtonStyle.Primary); - next_button.setDisabled(current_page == nb_pages); + if (my_gobattle_user_id == user_id){ + await interaction.editReply("You cannot send yourself a friend request..."); + return; + } - const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); + const gobattle_token = database.get_gobattle_token_by_discord_user(interaction.user); + if (!gobattle_token){ + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + } - const response_interaction = await interaction.editReply({ - embeds: [embed], - components: [row] - }); + const api_version = 1; + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/request/${user_id}/add?platform=${platform}&ud=`, request_info); - function collector_filter(m){ - const result = m.user.id == interaction.user.id; - - if (!result){ - m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { - console.error(error); - }); + if (!response.ok){ + const data = await response.json(); + switch (data?.error){ + case "Invalid token": + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + case "Account is already a friend": + await interaction.editReply(`You are already friends with user _#${user_id}_! Congratulations!`); + return; + case "Error creating friend request": + await interaction.editReply(`User _#${user_id}_ no longer accepts friend requests.`); + return; + case "Account not found": + await interaction.editReply(`The user _#${user_id}_ no longer exists or never existed.`); + return; + case "No more requests today": + await interaction.editReply("You cannot send more friend requests today."); + return; + default: + await interaction.editReply(`Unable to send a friend request to the user. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; } - - return result; + + const data_text = await response.text(); + if (data_text == "ok"){ + await interaction.editReply(`The friend request was sent to user _#${user_id}_.`); + }else{ + await interaction.editReply(`You have already sent a friend request to user _#${user_id}_. Wait for him to give an answer.`); + } + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); } +} + +async function get_friend_delete(interaction, client){ + await interaction.deferReply(); + + try{ + const user_id = interaction.options.get("user_id")?.value; + + const gobattle_token = database.get_gobattle_token_by_discord_user(interaction.user); + if (!gobattle_token){ + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + } + + const api_version = 1; + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/${user_id}/delete?platform=${platform}&ud=`, request_info); + + const data = await response.json(); + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + default: + await interaction.editReply(`Unable to remove user _#${user_id}_ from friends list. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; + } + + if (data == "ok"){ + await interaction.editReply(`The user _#${user_id}_ has been deleted from the friends list.`); + }else{ + await interaction.editReply(`The user _#${user_id}_ is already not present in your friends list.`); + } + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} - async function button_interaction_logic(response_interaction){ - try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); +async function get_friend_cancel(interaction, client){ + await interaction.deferReply(); + + try{ + const user_id = interaction.options.get("user_id")?.value; + + const gobattle_token = database.get_gobattle_token_by_discord_user(interaction.user); + if (!gobattle_token){ + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + } - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; + const api_version = 1; + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/request/${user_id}/delete?platform=${platform}&ud=`, request_info); + + const data = await response.json(); + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + default: + await interaction.editReply(`Unable to cancel friend request sent to user _#${user_id}_. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); } + return; + } + + if (data == "ok"){ + await interaction.editReply(`The friend request sent to user _#${user_id}_ was canceled.`); + }else{ + await interaction.editReply(`The friend request sent to user _#${user_id}_ has already been canceled.`); + } + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } +} + +async function get_friend_accept(interaction, client){ + await interaction.deferReply(); - embed.setDescription(pages[current_page - 1]); - embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); - previous_button.setDisabled(current_page == 1); - next_button.setDisabled(current_page == nb_pages); + try{ + const user_id = interaction.options.get("user_id")?.value; - response_interaction = await confirmation.update({embeds: [embed], components: [row]}); - await button_interaction_logic(response_interaction); - }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + const gobattle_token = database.get_gobattle_token_by_discord_user(interaction.user); + if (!gobattle_token){ + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; } + + const api_version = 1; + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/request/${user_id}/accept?platform=${platform}&ud=`, request_info); + + const data = await response.json(); + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + case "Error accepting request": + await interaction.editReply(`You have reached the limit on the number of possible friends or the user _#${user_id}_ has reached his limit on the number of friends or is no longer accepting new friends.`); + return; + default: + await interaction.editReply(`Unable to accept friend request for user _#${user_id}_. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; + } + + if (data == "ok"){ + await interaction.editReply(`The friend request for user _#${user_id}_ has been accepted.`); + }else{ + await interaction.editReply(`You have already accepted the friend request from user _#${user_id}_.`); + } + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); } +} + +async function get_friend_ignore(interaction, client){ + await interaction.deferReply(); + + try{ + const user_id = interaction.options.get("user_id")?.value; - await button_interaction_logic(response_interaction); + const gobattle_token = database.get_gobattle_token_by_discord_user(interaction.user); + if (!gobattle_token){ + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + } + + const api_version = 1; + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/v${api_version}/${gobattle_token}/friend/request/${user_id}/ignore?platform=${platform}&ud=`, request_info); + + const data = await response.json(); + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); + return; + default: + await interaction.editReply(`Unable to ignore friend request for user _#${user_id}_. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; + } + + if (data == "ok"){ + await interaction.editReply(`The friend request for user _#${user_id}_ has been ignored.`); + }else{ + await interaction.editReply(`The friend request for user _#${user_id}_ has already been ignored.`); + } + }catch(error){ + await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } } exports.get_user = get_user; diff --git a/src/database/database.js b/src/database/database.js index cf7e188..46a3b93 100644 --- a/src/database/database.js +++ b/src/database/database.js @@ -8,6 +8,7 @@ const database = new sqlite.DatabaseSync("database.sqlite"); let add_gobattle_accesse_statement; let remove_gobattle_accesse_statement; let get_gobattle_token_by_gobattle_user_id_statement; +let get_gobattle_token_by_discord_user_id_statement; let discord_user_id_to_gobattle_user_id_statement; let gobattle_user_id_to_discord_user_id_statement; @@ -43,6 +44,10 @@ function init(){ SELECT gobattle_token FROM user_session WHERE gobattle_user_id = $gobattle_user_id; `); + get_gobattle_token_by_discord_user_id_statement = database.prepare(` + SELECT gobattle_token FROM user_session WHERE discord_user_id = $discord_user_id; + `); + discord_user_id_to_gobattle_user_id_statement = database.prepare(` SELECT gobattle_user_id FROM user_session WHERE discord_user_id = $discord_user_id; `); @@ -90,6 +95,14 @@ function get_gobattle_token_by_gobattle_user_id(gobattle_user_id){ return data.gobattle_token; } +function get_gobattle_token_by_discord_user(discord_user){ + const data = get_gobattle_token_by_discord_user_id_statement.get({ + $discord_user_id: discord_user.id + }); + + return data.gobattle_token; +} + function discord_user_to_gobattle_user_id(discord_user){ const data = discord_user_id_to_gobattle_user_id_statement.get({ $discord_user_id: BigInt(discord_user.id) @@ -110,5 +123,6 @@ exports.init = init; exports.add_gobattle_accesse = add_gobattle_accesse; exports.remove_gobattle_accesse = remove_gobattle_accesse; exports.get_gobattle_token_by_gobattle_id = get_gobattle_token_by_gobattle_user_id; +exports.get_gobattle_token_by_discord_user = get_gobattle_token_by_discord_user; exports.discord_user_to_gobattle_user_id = discord_user_to_gobattle_user_id exports.gobattle_user_id_to_discord_user_id = gobattle_user_id_to_discord_user_id; \ No newline at end of file diff --git a/src/head_list.json b/src/head_list.json index 86985a4..6e58714 100644 --- a/src/head_list.json +++ b/src/head_list.json @@ -285,8 +285,8 @@ "emoji": "heads_item_56" }, { - "id": 57, + "id": 255, "name": "Phamy", "emoji": "heads_item_57" } -] \ No newline at end of file +] diff --git a/src/index.js b/src/index.js index 57fad0f..5921f6c 100644 --- a/src/index.js +++ b/src/index.js @@ -51,7 +51,7 @@ const client = new Client({ client.login(process.env.TOKEN); -client.on("ready", async (event) => { +client.on(Events.ClientReady, async (event) => { database.init(); console.log(`${event.user.tag} ready.`); @@ -174,7 +174,7 @@ client.on(Events.InteractionCreate, async (interaction) => { return; } - await interaction.editReply(`Impossible to connect. There is a problem with the GoBattle API.\nContact ${client.application.owner} to resolve this issue.`); + await interaction.editReply(`Unable to connect. There is a problem with the GoBattle API.\nContact ${client.application.owner} to resolve this issue.`); return; } @@ -184,20 +184,46 @@ client.on(Events.InteractionCreate, async (interaction) => { }else{ interaction.editReply("Your account is already registered. You can log out at any time with `/user logout`."); } - - /* - https://gobattle.io/api.php/recover?platform=Web&ud= - application/x-www-form-urlencoded - post email: sammarzin22@gmail.com - {id: "703", email: "sammarzin22@gmail.com"} - */ }catch (error){ await interaction.editReply(`An internal error has occurred...\nContact ${client.application.owner} to resolve this issue.`); console.error(error); } break; case "recover": - await interaction.reply({content: "Interaction not supported at this time.", ephemeral: true}); + try{ + await interaction.deferReply({ephemeral: true}); + + const email = interaction.fields.getTextInputValue("email"); + + const platform = "Web"; + const request_info = { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + "email": email + }) + }; + + const response = await fetch(`https://gobattle.io/api.php/recover?platform=${platform}&ud=`, request_info); + const data = await response.json(); + + if (!response.ok){ + if (data?.error == "Error sending email"){ + await interaction.editReply("# āš ļø Recovery error\nThe email is incorrect."); + return; + } + + await interaction.editReply(`Unable to recover. There is a problem with the GoBattle API.\nContact ${client.application.owner} to resolve this issue.`); + return; + } + + interaction.editReply(`This is the Email of the user _#${data.id}_. Let's ckeck out your email and follow instructions fer next steps. Let's play GoBattle!`); + }catch (error){ + await interaction.editReply(`An internal error has occurred...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } break; default: await interaction.reply({content: "The form submission was not processed successfully because I am not familiar with the form.", ephemeral: true}); @@ -245,7 +271,7 @@ client.on(Events.MessageCreate, async (msg) => { await msg.guild.leave(); break; - case "!gb_emoji": + case "!gb_emojis": if (!is_my_developer(client, msg.author)){ await msg.reply("You do not have permission to use this command."); return; @@ -254,7 +280,7 @@ client.on(Events.MessageCreate, async (msg) => { const nb_emoji = 60; for (let i = 0; i < nb_emoji; i++){ const emojis = Array.from(application_emoji_cache.values()); - const index = Math.floor(Math.random() * emojis.length); + const index = Math.floor(Math.random() * emojis.length); content += `${emojis[index]}`; } @@ -270,6 +296,16 @@ client.on(Events.MessageCreate, async (msg) => { const level = parseInt(command_info[1] || 0, 10); await msg.reply(`Level ${level} is: (${get_level_to_emojis(level)})`); + break; + case "!gb_emoji_name": + if (!is_my_developer(client, msg.author)){ + await msg.reply("You do not have permission to use this command."); + return; + } + + const emoji = application_emoji_cache.get(command_info[1]) || "_Unknown?_"; + await msg.reply(`The emoji is: ${emoji}`); + break; } }catch (error){ diff --git a/src/utils.js b/src/utils.js index 049f777..9f3b87a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,4 @@ -const {ChannelType, PermissionFlagsBits, User, Team, Emoji} = require("discord.js"); +const {ChannelType, PermissionFlagsBits, User, Team, Emoji, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); const level_emojis = require("./level_emojis.json"); const application_emoji_cache = new Map(); @@ -111,8 +111,11 @@ function sum(array){ } function get_first_chat_channel(guild, client){ + const everyone = guild.roles.everyone; return guild.channels.cache.find((channel) => { - return channel.type == ChannelType.GuildText && channel.permissionsFor(client.user).has(PermissionFlagsBits.ViewChannel | PermissionFlagsBits.SendMessages); + return channel.type == ChannelType.GuildText && + channel.permissionsFor(client.user).has(PermissionFlagsBits.SendMessages) && + channel.permissionsFor(everyone).has(PermissionFlagsBits.ViewChannel); }); } @@ -172,6 +175,77 @@ async function update_application_emoji_cache(application){ return application_emoji_cache; } +async function send_embed_layout(interaction, embed, pages, header_description = "", content_message){ + let current_page = 1; + + if (header_description){ + header_description += "\n"; + } + + embed.setDescription(header_description + (pages[current_page - 1] || "***There are no items to display on this page at the moment...***")); + + const nb_pages = pages.length || 1; + + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + + const previous_button = new ButtonBuilder(); + previous_button.setCustomId("previous"); + previous_button.setEmoji("ā—€ļø"); + previous_button.setStyle(ButtonStyle.Primary); + previous_button.setDisabled(current_page == 1); + + const next_button = new ButtonBuilder(); + next_button.setCustomId("next"); + next_button.setEmoji("ā–¶ļø"); + next_button.setStyle(ButtonStyle.Primary); + next_button.setDisabled(current_page == nb_pages); + + const row = new ActionRowBuilder(); + row.addComponents(previous_button, next_button); + + const response_interaction = await interaction.editReply({ + content: content_message, + embeds: [embed], + components: [row] + }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id; + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId === "previous"){ + current_page--; + } else if (confirmation.customId === "next"){ + current_page++; + } + + embed.setDescription(header_description + pages[current_page - 1]); + embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + previous_button.setDisabled(current_page == 1); + next_button.setDisabled(current_page == nb_pages); + + response_interaction = await confirmation.update({embeds: [embed], components: [row]}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + await button_interaction_logic(response_interaction); +} + exports.restrict_text = restrict_text; exports.format_score = format_score; exports.format_score_with_commas = format_score_with_commas; @@ -186,4 +260,5 @@ exports.send_echo = send_echo; exports.get_first_chat_channel = get_first_chat_channel; exports.is_my_developer = is_my_developer; exports.update_application_emoji_cache = update_application_emoji_cache; +exports.send_embed_layout = send_embed_layout; exports.application_emoji_cache = application_emoji_cache; From 198c782dd9c3a0f2ca36aace57b55b6c799e3a49 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Tue, 6 Aug 2024 04:30:14 +0200 Subject: [PATCH 36/54] update help command --- src/commands/asset.js | 2 +- src/commands/dungeon.js | 2 +- src/commands/help.js | 72 ++++++++++++++++++++++++++++++++++------- src/commands/item.js | 2 +- src/commands/ranking.js | 2 +- src/commands/user.js | 30 +++++++++-------- 6 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/commands/asset.js b/src/commands/asset.js index 71af3e6..8905d4f 100644 --- a/src/commands/asset.js +++ b/src/commands/asset.js @@ -8,7 +8,7 @@ asset_command.setDescription("Command relating to game data files (client side). asset_command.addSubcommand((subcommand) => { subcommand.setName("info"); subcommand.setDescription("Get a specific client file."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("file_id"); option.setDescription("The identifier of the item."); option.setMinValue(1); diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index 02e6adb..3b2f17d 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -8,7 +8,7 @@ dungeon_command.setDescription("Command relating to dungeons and other maps in t dungeon_command.addSubcommand((subcommand) => { subcommand.setName("info"); subcommand.setDescription("Obtain the characteristics of a dungeon."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("dungeon_id"); option.setDescription("The dungeon identifier."); option.setMinValue(1); diff --git a/src/commands/help.js b/src/commands/help.js index 564df49..d42c55c 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -1,28 +1,76 @@ -const {SlashCommandBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {send_embed_layout} = require("../utils.js"); const help_command = new SlashCommandBuilder(); help_command.setName("help"); help_command.setDescription("Get help on how to use me."); +async function get_list_commands(application){ + const result = new Map(); + const commands = await application.commands.fetch(); + + for (const command of commands.values()){ + let base = true; + + for (const option of command.options){ + if (option.type == 1){ + base = false; + } + + let sub_base = true; + if (option.options){ + for (const sub_option of option.options){ + if (sub_option.type == 1){ + sub_base = false; + result.set(`${command.name} ${option.name} ${sub_option.name}`, {id: command.id, description: sub_option.description, base: command}); + } + } + }else if (option.type != 1){ + sub_base = false; + } + + if (sub_base){ + result.set(`${command.name} ${option.name}`, {id: command.id, description: option.description, base: command}); + } + } + + if (base){ + result.set(command.name, {id: command.id, description: command.description, base: command}); + } + } + + return result; +} + async function get_help(interaction, client){ - await interaction.deferReply({ephemeral: true}); + await interaction.deferReply(); try{ - const commands = await client.application.commands.fetch(); - - let description = ""; - - for (const command of commands.values()){ - description += `\n- `; + const embed = new EmbedBuilder(); + embed.setTitle("ā„¹ļø List of commands ā„¹ļø"); - for (const option of command.options){ - description += ` _<${option.name}${option.required ? "*" : "?"}>_`; + const commands = await get_list_commands(client.application); + + const header_description = `Hi ${interaction.user}, Here are some commands that might be useful to you:`; + const max_items_by_pages = 7; + const pages = new Array(Math.ceil(commands.size / max_items_by_pages)); + + let current_page = -1; + let i = 0; + for (const [name, data] of commands.entries()){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = ""; } - description += `: **${command.description}**`; + pages[current_page] += `* : **${data.description}**\n`; + + i++; } - interaction.editReply(`Hi ${interaction.user}, Here are some commands that might be useful to you: ${description}`); + embed.setTimestamp(); + + await send_embed_layout(interaction, embed, pages, header_description); }catch (error){ await interaction.editReply(`Unable to list commands.\nContact ${client.application.owner} to resolve this issue.`); console.error(error); diff --git a/src/commands/item.js b/src/commands/item.js index d912c36..e9cf86e 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -15,7 +15,7 @@ item_command.setDescription("Command relating to items and other consumables in item_command.addSubcommand((subcommand) => { subcommand.setName("info"); subcommand.setDescription("Obtain the characteristics of an item."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("item_id"); option.setDescription("The identifier of the item."); option.setMinValue(1); diff --git a/src/commands/ranking.js b/src/commands/ranking.js index d990134..79d1618 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -51,7 +51,7 @@ ranking_command.addSubcommand((subcommand) => { ranking_command.addSubcommand((subcommand) => { subcommand.setName("speedrun"); subcommand.setDescription("Get Speedrun ranking"); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("dungeon_id"); option.setDescription("The dungeon identifier."); option.setMinValue(1); diff --git a/src/commands/user.js b/src/commands/user.js index 3bbfa70..3bf1d01 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -34,7 +34,7 @@ user_command.addSubcommand((subcommand) => { user_command.addSubcommand((subcommand) => { subcommand.setName("info"); subcommand.setDescription("Get general information about a user."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -45,6 +45,7 @@ user_command.addSubcommand((subcommand) => { return subcommand; }); +/* user_command.addSubcommand((subcommand) => { subcommand.setName("king"); subcommand.setDescription("Get general information about the current king."); @@ -55,7 +56,7 @@ user_command.addSubcommand((subcommand) => { subcommand.setName("bank"); subcommand.setDescription("Get the list of items contained in a game user's bank."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -64,7 +65,7 @@ user_command.addSubcommand((subcommand) => { return option; }); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("index_book"); option.setDescription("Index of the bank book to consult."); option.setMinValue(1); @@ -80,7 +81,7 @@ user_command.addSubcommand((subcommand) => { subcommand.setName("inventory"); subcommand.setDescription("Get the list of items contained in a game user's inventory."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -91,11 +92,12 @@ user_command.addSubcommand((subcommand) => { return subcommand; }); +*/ user_command.addSubcommand((subcommand) => { subcommand.setName("gobattle_to_discord"); subcommand.setDescription("Get Discord user from GoBattle user."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -126,7 +128,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("pending_count"); subcommand.setDescription("Get the number of friend requests a user has."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -140,7 +142,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("incoming_requests"); subcommand.setDescription("Get the list of a user's incoming friend requests."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -154,7 +156,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("outgoing_requests"); subcommand.setDescription("Get the list of a user's outgoing friend requests."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -168,7 +170,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("list"); subcommand.setDescription("Get a user's friends list."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -182,7 +184,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("add"); subcommand.setDescription("Send a friend request to a user."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -196,7 +198,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("delete"); subcommand.setDescription("Remove a user from your friends list."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -210,7 +212,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("cancel"); subcommand.setDescription("Cancel the friend request sent to a user."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -224,7 +226,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("accept"); subcommand.setDescription("Accept a friend request from a user."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); @@ -238,7 +240,7 @@ user_command.addSubcommandGroup((friend_command) => { friend_command.addSubcommand((subcommand) => { subcommand.setName("ignore"); subcommand.setDescription("Ignore a friend request from a user."); - subcommand.addNumberOption((option) => { + subcommand.addIntegerOption((option) => { option.setName("user_id"); option.setDescription("The user identifier."); option.setMinValue(1); From f7f00cfc582604af95ddbf77a7cfca8db64e3188 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Tue, 6 Aug 2024 04:46:44 +0200 Subject: [PATCH 37/54] aaaaaaaa --- src/commands/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/user.js b/src/commands/user.js index 3bf1d01..ad46499 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -378,7 +378,7 @@ async function get_logout(interaction, _client){ async function get_recover(interaction, _client){ const modal = new ModalBuilder(); modal.setCustomId("recover"); - modal.setTitle("Recover your Gobattle account."); + modal.setTitle("Recover your GoBattle.io account."); const email_input = new TextInputBuilder(); email_input.setCustomId("email"); From f6e7a5c7b16edd0d76262c80e907144cd3796c91 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Tue, 6 Aug 2024 19:59:25 +0200 Subject: [PATCH 38/54] bug token correction --- src/commands/help.js | 28 +++++++++++++--------------- src/commands/user.js | 11 ++++++++++- src/database/database.js | 22 ++++++++++++++++++---- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/commands/help.js b/src/commands/help.js index d42c55c..811d420 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -1,36 +1,34 @@ -const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {SlashCommandBuilder, EmbedBuilder, ApplicationCommandType, ApplicationCommandOptionType} = require("discord.js"); const {send_embed_layout} = require("../utils.js"); const help_command = new SlashCommandBuilder(); help_command.setName("help"); help_command.setDescription("Get help on how to use me."); -async function get_list_commands(application){ +async function get_list_commands(application, command_type = ApplicationCommandType.ChatInput){ const result = new Map(); const commands = await application.commands.fetch(); for (const command of commands.values()){ let base = true; + if (command.type != command_type){ + continue; + } + for (const option of command.options){ - if (option.type == 1){ + if (option.type == ApplicationCommandOptionType.Subcommand){ + base = false; + if (option.type == command_type){ + result.set(`${command.name} ${option.name}`, {id: command.id, description: option.description, base: command}); + } + }else if (option.type == ApplicationCommandOptionType.SubcommandGroup){ base = false; - } - - let sub_base = true; - if (option.options){ for (const sub_option of option.options){ - if (sub_option.type == 1){ - sub_base = false; + if (sub_option.type == command_type){ result.set(`${command.name} ${option.name} ${sub_option.name}`, {id: command.id, description: sub_option.description, base: command}); } } - }else if (option.type != 1){ - sub_base = false; - } - - if (sub_base){ - result.set(`${command.name} ${option.name}`, {id: command.id, description: option.description, base: command}); } } diff --git a/src/commands/user.js b/src/commands/user.js index ad46499..bbf3dca 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -366,7 +366,7 @@ async function get_login(interaction, _client){ } async function get_logout(interaction, _client){ - const succes = database.remove_gobattle_accesse(interaction.user); + const succes = database.remove_gobattle_accesse_by_discord_user(interaction.user); if (succes){ await interaction.reply({content: "Your session has ended and has been successfully deleted. You can reconnect at any time with `/user login`.", ephemeral: true}); @@ -417,6 +417,7 @@ async function get_info(interaction, client){ const data = await response.json(); if (data?.error == "Invalid token"){ + database.remove_gobattle_accesse_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -603,6 +604,7 @@ async function get_friend_pending_count(interaction, client){ const data = await response.json(); if (data?.error == "Invalid token"){ + database.remove_gobattle_accesse_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -638,6 +640,7 @@ async function get_friend_pending_requests(interaction, client, type){ const data = await response.json(); if (data?.error == "Invalid token"){ + database.remove_gobattle_accesse_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -698,6 +701,7 @@ async function get_friend_list(interaction, client){ const data = await response.json(); if (data?.error == "Invalid token"){ + database.remove_gobattle_accesse_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -776,6 +780,7 @@ async function get_friend_add(interaction, client){ const data = await response.json(); switch (data?.error){ case "Invalid token": + database.remove_gobattle_accesse_by_gobattle_user_id(my_gobattle_user_id); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; case "Account is already a friend": @@ -831,6 +836,7 @@ async function get_friend_delete(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": + database.remove_gobattle_accesse_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; default: @@ -873,6 +879,7 @@ async function get_friend_cancel(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": + database.remove_gobattle_accesse_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; default: @@ -915,6 +922,7 @@ async function get_friend_accept(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": + database.remove_gobattle_accesse_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; case "Error accepting request": @@ -960,6 +968,7 @@ async function get_friend_ignore(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": + database.remove_gobattle_accesse_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; default: diff --git a/src/database/database.js b/src/database/database.js index 46a3b93..66a813c 100644 --- a/src/database/database.js +++ b/src/database/database.js @@ -37,7 +37,7 @@ function init(){ `); remove_gobattle_accesse_statement = database.prepare(` - DELETE FROM user_session WHERE discord_user_id = $discord_user_id; + DELETE FROM user_session WHERE gobattle_user_id = $gobattle_user_id; `); get_gobattle_token_by_gobattle_user_id_statement = database.prepare(` @@ -74,14 +74,27 @@ function add_gobattle_accesse(discord_user, gobattle_user_id, gobattle_token){ return true; } -function remove_gobattle_accesse(discord_user){ +function remove_gobattle_accesse_by_discord_user(discord_user){ const gobattle_user_id = discord_user_to_gobattle_user_id(discord_user); if (!gobattle_user_id){ return false; } remove_gobattle_accesse_statement.run({ - $discord_user_id: BigInt(discord_user.id), + $discord_user_id: gobattle_user_id, + }); + + return true; +} + +function remove_gobattle_accesse_by_gobattle_user_id(gobattle_user_id){ + const discord_user_id = gobattle_user_id_to_discord_user_id(gobattle_user_id); + if (!discord_user_id){ + return false; + } + + remove_gobattle_accesse_statement.run({ + $gobattle_user_id: gobattle_user_id, }); return true; @@ -121,7 +134,8 @@ function gobattle_user_id_to_discord_user_id(gobattle_user_id){ exports.init = init; exports.add_gobattle_accesse = add_gobattle_accesse; -exports.remove_gobattle_accesse = remove_gobattle_accesse; +exports.remove_gobattle_accesse_by_discord_user = remove_gobattle_accesse_by_discord_user; +exports.remove_gobattle_accesse_by_gobattle_user_id = remove_gobattle_accesse_by_gobattle_user_id; exports.get_gobattle_token_by_gobattle_id = get_gobattle_token_by_gobattle_user_id; exports.get_gobattle_token_by_discord_user = get_gobattle_token_by_discord_user; exports.discord_user_to_gobattle_user_id = discord_user_to_gobattle_user_id From fdc4e7da9a2c0c20ce2efecb9180e8998a2b3ef7 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Tue, 6 Aug 2024 22:47:48 +0200 Subject: [PATCH 39/54] aaaaaaaaa --- .env | 7 ------ src/commands/info.js | 20 +++++++++++++--- src/commands/user.js | 36 +++++++++++++++++++++++++++- src/index.js | 57 ++++++++++++++++++++++++++++++++++++++++---- src/utils.js | 16 +++++++++++++ 5 files changed, 121 insertions(+), 15 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 33f5f28..0000000 --- a/.env +++ /dev/null @@ -1,7 +0,0 @@ -# Discord plaplication token. This token is private and should not be shared! -TOKEN = InsertBotTokenHere - -# Guild identifiers can use critical administration commands. -# The default is the Official GoBattle.io Discord server. -# You can also add your own server for debugging and tuning purposes of this application. -GUILD_ADMIN_ID = ["380588354934276097"] # JSON format. diff --git a/src/commands/info.js b/src/commands/info.js index a4220cb..eaf5f4f 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,4 +1,5 @@ const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); +const {get_info_application, format_score_with_commas} = require("./../utils.js"); const info_command = new SlashCommandBuilder(); info_command.setName("info"); @@ -13,10 +14,23 @@ async function get_info(interaction, client){ embed.setURL(client.application.customInstallURL || `https://discord.com/api/oauth2/authorize?client_id=${client.application.id}&permissions=18685255743552&scope=bot+applications.commands`); embed.setDescription(client.application.description); embed.setThumbnail(client.user.avatarURL()); + + const info = await get_info_application(client.application); + const approximate_guild_count = info?.approximate_guild_count || "_Unknown?_"; + const tos = info?.terms_of_service_url || "https://gobattle.io/tos.html"; + const pp = info?.privacy_policy_url || "https://www.iubenda.com/privacy-policy/8108614"; + const project_initiator_user_id = "461495109855215616"; // Please be courteous and do not change this value. + + embed.addFields( + {name: "> Gobattle.io server", value: "> [Link](https://discord.gg/gobattle-io-official-380588354934276097)", inline: true}, + {name: "> Application Owner", value: `> ${client.application.owner}`, inline: true}, + {name: "> Approximate guild count", value: `> ${typeof approximate_guild_count == "number" ? format_score_with_commas(approximate_guild_count) : approximate_guild_count}`, inline: true} + ); + embed.addFields( - {name: "Gobattle.io server", value: "[Link](https://discord.gg/gobattle-io-official-380588354934276097)", inline: false}, - {name: "BOT Developer server", value: "[Link](https://discord.gg/jhGdY5ArBU)", inline: false}, - {name: "BOT Owner", value: `${client.application.owner}`, inline: false} + {name: "> Terms of service", value: `> [Link](${tos})`, inline: true}, + {name: "> Privacy policy", value: `> [Link](${pp})`, inline: true}, + {name: "> Project initiator", value: `> <@${project_initiator_user_id}>`, inline: true} ); await interaction.editReply({ diff --git a/src/commands/user.js b/src/commands/user.js index bbf3dca..5ac7803 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -13,6 +13,12 @@ for (const head_data of head_list){ const user_command = new SlashCommandBuilder(); user_command.setName("user"); user_command.setDescription("Command relating to users in the game."); +user_command.addSubcommand((subcommand) => { + subcommand.setName("create"); + subcommand.setDescription("Create a GoBattle.io account."); + + return subcommand; +}); user_command.addSubcommand((subcommand) => { subcommand.setName("login"); subcommand.setDescription("Log in to your GoBattle.io account."); @@ -261,6 +267,9 @@ async function get_user(interaction, client){ if (!subcommand_group){ switch (subcommand){ + case "create": + await get_create(interaction, client); + break; case "login": await get_login(interaction, client); break; @@ -334,6 +343,31 @@ async function get_friend(interaction, client){ } } +async function get_create(interaction, _client){ + const modal = new ModalBuilder(); + modal.setCustomId("create"); + modal.setTitle("Create a GoBattle.io account."); + + const email_input = new TextInputBuilder(); + email_input.setCustomId("email"); + email_input.setLabel("Email"); + email_input.setStyle(TextInputStyle.Short); + email_input.setRequired(true); + + const password_input = new TextInputBuilder(); + password_input.setCustomId("password"); + password_input.setLabel("Password (Don't give Discord password!)"); + password_input.setStyle(TextInputStyle.Short); + email_input.setRequired(true); + + const email_action_row = new ActionRowBuilder().addComponents(email_input); + const password_action_row = new ActionRowBuilder().addComponents(password_input); + + modal.addComponents(email_action_row, password_action_row); + + await interaction.showModal(modal); +} + async function get_login(interaction, _client){ const gobattle_user_id = database.discord_user_to_gobattle_user_id(interaction.user); if (gobattle_user_id){ @@ -343,7 +377,7 @@ async function get_login(interaction, _client){ const modal = new ModalBuilder(); modal.setCustomId("login"); - modal.setTitle("Login to Gobattle account."); + modal.setTitle("Login to GoBattle.io account."); const email_input = new TextInputBuilder(); email_input.setCustomId("email"); diff --git a/src/index.js b/src/index.js index 5921f6c..9ae59e3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ const {Client, Events, Partials, IntentsBitField, Routes, ActivityType} = require("discord.js"); -const {get_first_chat_channel, is_my_developer, update_application_emoji_cache, get_level_to_emojis, application_emoji_cache} = require("./utils.js"); +const {get_first_chat_channel, is_my_developer, update_application_emoji_cache, get_level_to_emojis, application_emoji_cache, get_info_application} = require("./utils.js"); const database = require("./database/database.js"); const {get_ranking, ranking_command} = require("./commands/ranking.js"); @@ -60,7 +60,7 @@ client.on(Events.ClientReady, async (event) => { await client.application.fetch(); await update_application_emoji_cache(client.application); - + try{ await client.rest.put( Routes.applicationCommands(client.user.id), @@ -146,6 +146,55 @@ client.on(Events.InteractionCreate, async (interaction) => { } }else if (interaction.isModalSubmit()){ switch (interaction.customId){ + case "create": + try{ + await interaction.deferReply({ephemeral: true}); + + const email = interaction.fields.getTextInputValue("email"); + const password = interaction.fields.getTextInputValue("password"); + + const platform = "Web"; + const request_info = { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + "email": email, + "password": password, + "trackingId": "" + }) + }; + + const response = await fetch(`https://gobattle.io/api.php/register?platform=${platform}&ud=`, request_info); + const data = await response.json(); + + if (!response.ok){ + switch (data?.error){ + case "Invalid email": + await interaction.editReply("# āš ļø Account creation error\nThe email introduced is not valid. Please reenter your email."); + return; + case "Password short or too easy": + await interaction.editReply("# āš ļø Account creation error\nYour password is too easy to guess. Please use a better password."); + return; + case "Duplicated user": + await interaction.editReply("# āš ļø Account creation error\nEmail is already in use."); + return; + default: + await interaction.editReply(`Unable to create account. There is a problem with the GoBattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; + } + + const info = await get_info_application(client.application); + const tos = info?.terms_of_service_url || "https://gobattle.io/tos.html"; + const pp = info?.privacy_policy_url || "https://www.iubenda.com/privacy-policy/8108614"; + interaction.editReply(`Thank you for register to GoBattle.io.\nReview your Email and follow the instructions to activate your account _#${data.id}_.\nBy continuing to create your account we assume that you accept our [Terms of Use](${tos}) and our [Privacy Policy](${pp}).`); + }catch (error){ + await interaction.editReply(`An internal error has occurred...\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } + break; case "login": try{ await interaction.deferReply({ephemeral: true}); @@ -157,7 +206,7 @@ client.on(Events.InteractionCreate, async (interaction) => { const request_info = { method: "POST", headers: { - "Content-Type": "application/x-www-form-urlencoded", + "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ "email": email, @@ -199,7 +248,7 @@ client.on(Events.InteractionCreate, async (interaction) => { const request_info = { method: "POST", headers: { - "Content-Type": "application/x-www-form-urlencoded", + "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ "email": email diff --git a/src/utils.js b/src/utils.js index 9f3b87a..7f6c188 100644 --- a/src/utils.js +++ b/src/utils.js @@ -175,6 +175,21 @@ async function update_application_emoji_cache(application){ return application_emoji_cache; } +async function get_info_application(application){ + const api_version = 10; + const headers = { + "Authorization": `Bot ${application.client.token}` + }; + + const response = await fetch(`https://discord.com/api/v${api_version}/applications/${application.id}`, {method: "GET", headers: headers}); + if (!response.ok){ + return; + } + + const data = await response.json(); + return data; +} + async function send_embed_layout(interaction, embed, pages, header_description = "", content_message){ let current_page = 1; @@ -261,4 +276,5 @@ exports.get_first_chat_channel = get_first_chat_channel; exports.is_my_developer = is_my_developer; exports.update_application_emoji_cache = update_application_emoji_cache; exports.send_embed_layout = send_embed_layout; +exports.get_info_application = get_info_application; exports.application_emoji_cache = application_emoji_cache; From 9c74e8e7d20ebb9b2619a18233166e0283b35eed Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Wed, 7 Aug 2024 01:48:28 +0200 Subject: [PATCH 40/54] add screenshot gallery --- screenshot_gallery/info.png | Bin 0 -> 58597 bytes screenshot_gallery/item_ultrarare_drop.png | Bin 0 -> 94846 bytes screenshot_gallery/ranking_king.png | Bin 0 -> 83659 bytes screenshot_gallery/speedrun.png | Bin 0 -> 79306 bytes screenshot_gallery/user_friend_list.png | Bin 0 -> 79649 bytes screenshot_gallery/user_info.png | Bin 0 -> 65570 bytes src/commands/info.js | 8 ++++---- 7 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 screenshot_gallery/info.png create mode 100644 screenshot_gallery/item_ultrarare_drop.png create mode 100644 screenshot_gallery/ranking_king.png create mode 100644 screenshot_gallery/speedrun.png create mode 100644 screenshot_gallery/user_friend_list.png create mode 100644 screenshot_gallery/user_info.png diff --git a/screenshot_gallery/info.png b/screenshot_gallery/info.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c348d84e16842b40424ff54cf5bb4a0a48a2d4 GIT binary patch literal 58597 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 B2D<XI^Z% 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 Z0?q8|h4=(#Q-L3#DaonHmcKOn^dGJciQWJJ literal 0 HcmV?d00001 diff --git a/screenshot_gallery/ranking_king.png b/screenshot_gallery/ranking_king.png new file mode 100644 index 0000000000000000000000000000000000000000..c19a0f53728ed9a4c5641381dd4ad9e3011431cc GIT binary patch literal 83659 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

>sUglgY9ul5Cv(O@D{dR#_5cFl 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 literal 0 HcmV?d00001 diff --git a/screenshot_gallery/speedrun.png b/screenshot_gallery/speedrun.png new file mode 100644 index 0000000000000000000000000000000000000000..c4cadfdeb2c82176102c13d7bca7964d2cc33002 GIT binary patch literal 79306 zcmYJa18^l>7cQJk?1^nnY`tjc+QAePWvxS{4iK>MS z5X=VX$VkG>MPg+4=1juM%*sx}%)-mY&db8Jlf79F21Wt~5ED^#&$`%jOVd;9dj5K{ zEloWYU`tE~)C$8F3I9YQ5n!h&$Se4v|F=@VO}|!%&NI1IS6ux2p>pw)yg5J$d|rxm z>|TFn^3*1En>bOHb&Mh%5f{y*CN3UW8`z&=KD!BIpx z##QY%`53EIX$N5NJl0hXbnzp3U^VJLQ>=I)U*69PvI|?LcHsBeC-e)8!qWN-O}n?b zngZr;$j9#NI(>6BAXYYhTUP1Tqh^GiX5S1d z3p_60hY9cfl3JsdRl#s7LN@v-Nzm z#!~a)NlQYp2VOgHFXd$=ytB$6(b z@82CS5=bj3S>MTKAiT#=CHb@XE0`BWyPJN(`*VEargiiErKST=qMWNZ5rc$l=ACZ! z>&bB>6C8|!Zm=2qsbx)3;yMd>k;R^R zq3G{$ZqTpSl~bcqOQEO<7>Wq3`?DBRVIvusa6;1J%BJnNSGM?{$#FZ1)o~zFHhv$F zQcgN9?wpm&Q7Ndbj!daOcRzliUb#`?N~D%E`~)E2Z~=)NS)=KP!b*XY1#nU@aLp8e zy@W`nqN1WSp|8VQF|v4tI^al+$+`SmoG{*NyUEZ=g$;oN&(hM!^Fj#ssc2P;)4VR2 zr;irKYXbmN4Y@mo!-L2}l4V6%CEBEu?UwU;Dj{pojJ{v#4c<|e_a6E`BTqx`TVg)w zwEpGz4X`Lh@#_Hlc(#s1jy6Bxl$M;}s^ky3l!8JS8u^qCojz4I7pjay;y*8YqM{3+ zS@u|!7Te20?sY*67xVM$o;X4}j}xz7=N%K6Y&L7U3}ahZ?&n3SuH#McXv1V-tN?iL za?c|s0WwTa#|JVPW9JTkmOM3zz`Fu19Ub>m7VzNUV0L?ZMuw@Yf5~PY{CQ}`m$iuT z0SRftZ5!hKpHTP62a4e2bT$WAR{K2$=%VNI%_n57?a|D!w&UEXNdl3bgO%C9%UNm*v>rEr+kxMT6K%rX|XrmFJBZe>|JCR86( zdk~n}ocG=|q|UXO^K|Xs)uoe0G2?)qDfn;aoh%7%~QAS-iM}* z%|w}SN>XcvucS9njY5x*sweAgAvng1zllCkX6=P{d~SsIXxWjEq2hNsg<_vNJ8PVbA=ooCT+Z;YoIzCzZ7)yQ5oMEB$NCPZ zo9B<;zj!N!-u=&N^!M7MMl2++qsdoMyhqmIs0kEYU9n0mly!B*tgP0<&NNh1CbZ15 z!Tkqz^YZe*7T9mzd?15J$0sH{+zxv;KjW!c%0;S8YWWiA`Lz>RB4B7 zdR{IKak+HA#G7q5*+zOYG~<_>Twb-TN;yDp%*EZO@DR0U2n zHBYE!`w>Q>eI3m7Y>3Qi_3u^I?Db!*d$>=0ZiY*j#iA2G(}ed+I~#C0&!Kl-!#T^>>zM|0wW#D9%Ow#QJsO`_~iMB-!AYrte|6nJ=I~i2b&n1eq26@ zJyM3dJS!E~!mE|qeQ>D@9jy`lEmy-@>wtOv&TZfQ)gg@Pp3DM`J$9xSM5)r&$?Udc;yzvdtxXe?y-bUHkm@SfXwnU~^FG6RSEVi-`G) zawOiuie&%6Y_47V=Gr^u{k1pF6Y~VARij#Lej$&z;_hi@BRljir1z$z13 zKv`|9J6Q;ju((mb$rs-V0p*Sa=9W9d}kq)3^HRA8tTuzwmWk>dU8@!P>|(7N%?naFe>dkkb+z;SjO0#dpZQEpvYmWr< zeRmmL*Bb5lMjG#rbhJ9H^kf>0D%ZoH2V%A@+jh>`vDEsq8q?nHiIvE}J&)yzP7FkFLoF<(&Sx|(-$Q6==?Du8i_OI= zh%f%y^76vYs;JvrRwBPW8s#yJNGt{tA_YX#KcJMupnF%Q^X^Jo-}@AJ-!vWjb-vWc zdVA~0b9@1O-~K>Bi@t(`ms>EoJ7Sp3;0dazkS6kaANzQ;0|?5u-cC$fcqWZ}>dTQ7 zvz}%UvwR6-5*21ybRNm&RC*+uVG<`}`()E8*c=_#LWMyK?VYZF^+}e;7jxr9KALpx z4XKsoZ}oxSPHGJwKtSqc&epmVQr2^F z-g;y+{4sqLZ`0XjC}NT=SvPH2>Tz5MZ~Cjx)`AHuDNUy&pUB zwe~sJKzeNC&YpC|EIk>bfx`0gl52F1gO`RYEf}Mk0cYhKdl%%>F-ZAWGp_Y$Kp9tX zPXH&r91U_;<)F-soL2zC2`>2cH6~mbK^U0}eNpa=x8K?>W)`(^&W?AE5O?Rn$g@$g z5rLWD<(^vi!W?#PHTS1$JBz0zrP)cGwO%}X0OLdfxeW@7`cU&P7j1R^Qe6VZd-dVai!MVh>4Xt2lVW0 zJ$HVh4cn)kTacTwcsW>5*9wttg z6?oks4Voq8<76MGF3++V^NiiHBDx9o;ijSsx{zY+SboO&`g{xLVXO039Y*{{sRd&b z`|G}7NMwcy20*S{AC{-Ucf$E!uGLxSzQT9w6gzoABx3sjPVIbscwG|a5=gf>=TWjyjYpSWqIbA4K*4PFU_G2E6;MzW-Gw9B- z=bmhTxHi7f=AxsEeDCDHy}M20I)()Mb;B+6_EPH~u)~Cd*kEy&V$@-u>TWqxp0 z`5AeQ%R+{9ePg7TwksjN+ETl7m~L>;emzA=bX9D+ufA4#rA74lkMOMf+2ixWi=};; ze^Afv+M~J1`^42O0&tn+=4{5dpN~ODt zk1CA`PNQw@I>iPZCglS`gIuq^}+b$M$9gC-3j zra%9UwrodsoXWMocFS2WAzr*~XW9!teu5!2PLuj>LIu^znfrxSVdoOeQ=VRY`fEz^ ztWSS(woesQxgcFVWUlylclKdArI=?g-|L*Huf`>z#3@KVx!N<0-HtoUxVh{&s~ zw0YE3vN}x+7WHl=4qmkzTE2ns0aK2|s6|CjT4JQhS#Npi{G> zF#CXEXKF>Z_?2}rZNnWDnu&!DT9h_Rv6;;XR3!4GCTo0q+eUS#hfSFwJ9H^LQQB~g zq|c1nq>3LDlO&IKL{nG;;L%DqVH01 zB-RlENup3utqFgsdy!umGST>F%VtTIcPm_lk8$OQ!5KqTd3+>yX5bI9S=J9MDFVr#;<#E;@l7Zw01M(+ z^S|7Lyd5=o1|!soKO^O>eB2(+9~dX^mKYX$0gVabpA-^Ct>JQM!ri-CNk9(;n_E^W z_z?7e*rw7|r5Wq+E7AWkRf1DurvHzf`u};V|NqFRz__^+Hyr~bdf;+`Vs?4v`393a zn~?(0Kf6%$z;44Zmd@L?Il!-|@jset*I+aaMkm6Q8V6uB29G(UrYfHiR$^H+ z|L3GVrr?KWN^r$$oEm1W5JKw6OzXUZbJL$jmkH=V0m2_-Fd*~>aRohjEC|gP$kbXd z#hMAxk5idp)MWQhPYVm1qVl%Lwk?^O^fAlFqAAd1<<7Kn{HWB_e4+02pam3J4y(n< zNq$oqWN3x)3Axu+CRnzLpM19y+bs-=G0|eVRV-1d(>EUR<-O^&Ad2-ER~O__Ru!?g z<%I=T;+yB`LOnu|Ty|mc5)J97Yk%X8C9&2^uq>A=*m;PN$)Kvh-{w2(PrQdtoDFczluyAjDC}nQ3jYX4T*4= zDlFu9(()7`VC|myA(MNW1S-iWpXNNG-(F_MOW;(cJaP16OY(Sm}G=FqNTTqY`>{T)MKDD&I-i)c&NS8l`952~& zkcH9e05l*eEPdO+bu!=ZY=mtEagT!HG46HA@dmOML(c4(5GHVdgt}ASdZ{G$yigN) ztiEYiD*3Fi$|f99E_+@MdN^zMdOkuiW0u!%*}nq?AU*jMDeLGFFrppMOH>p!`ZWP(Y;cW`$!R^;(Kr-kP&9`Rn4>8mbIW zcq^AWLzCs>_U~qwSLJo+Lw*GWM?obeY0PhK$|#(#$c9-0Y}=`Iqi3)NdJZ}7xAYBn z3|H~!!sOdACQ<^#PU^m)7GD!ib_Yx*Mq-K;>i`ZE25kJ`**p%`ZycmjSW{qH{Vkmx1Zg#GEo-_h; zU1(BMuMfsbxxN#<=u6OF|2@~j^NEpMx>s~wbIGcy+N;-S-WB?&q`&Y<8xcf|DI<_^ zRP=nr)||b=h^@LLXpqfxAk~n!K3W~wJ{FGkZ&Ayc#HE%a-v*-E=niDs`AQwv%%oWm zkMzqh6%{<@^4EEquZC0xdXzF5Nrat;THJ51iCgoq4 z6LU_q!r*RI$Yz^D%-^~oWx50OIS8?_^oqGeXiP%4^3;S-B~uXaXF2v!zz#S*;+;2- zJ|4{gwBh}SgVM+OYu4OoQ?2+!{U?@qoMmfnlgsAoJgOKnFf!l90^P5E=~`k`->}RN z&vFP{#-h>lBMaD(I!^4m0g2?*&r7Vb==H1S7i#9a~L%FvC~ok2}+N?B(D`Kf&yPU4dcqwQ#2*d51|)Cs*$bTQ?H6}EXIS*h3Q|G zcjVR)i1!BgIb&J;e2J=iU>~a>#2Mfw>r*#i7UoV7UdSp^wzyY?7f2?fULWCJ<}Oi~ z8n$g=^L^*Oy}kWs_N)Xn{5MZRYGh;-0F2*#3fK;%2V}jqIdSt??l|-eHV3RJUYn=`ZN$eD`13K1tO9>_;~-3QtA9FAU*DcMlXi_ zRGZ*>JEr9gH%g473q4f@#~Zb=3yW@1uKQ2C*tPeb8p5d6GlHk4WGqVV4FcwN7;ksn zM#%_szY&>MXPe?}d?OzS(cj6WeG5wI&)`p8Q1_a^Hek8;|K$Dv*@DYSCFW zyCjH5o2$5P)OoddwfgQtT>c%_o<$u|2kln;QdY_+X$PYOCgPTID*J_*%OG<$I+=_W z#{&AYj|=_I*MEc0{E(jIbDd|OJa51?yjwsqZV8F)ZY-4_(dY?0&KNl9jafWTcOG(( zb^fcY$C|#^{iKP>8;<_CD&K(ST;-R7O=fdX*=Jo*LaH4+VVvvSXC%OCG|`I6ycXiewPeV<@sVrljifqE(S9R$QJG(q%g z1Ex@<0``Xu6v<08Lc`gYc)HYV&Bz{`0)u*YaSb2OZtrQTl1%V%tIJX&o!2V*{18sZ z!o8-_w0aA^f+j*d;g;sweC|Kq<^23jd+|=B@6yD7o94t!RFge%7@Zp8!TnjkZ=NX?lHFxL=m>6W|EL zRDJe)0-MbtOTy0IUsEU=NsLzJjVkT1S&F0)DkZ#dg=1<+Tx8Y3Qianf>KgpAPR4M3W?dvE7NlNLm%;l_UW)8Wyg zGMwd3#3gHWG)K*~gIjVwr`%Yk>rP9ZbiAkJ($8UcbF0G#&E}GFK*F^y*;-=!uQV$Q z_PdlxVxmnwa6yG1`XJkNe&-d_%r9UlT*KpEH1E8CO^{w<%)CWZs*cur$N7{-yZ?{} zLzQO})Urn*zXhnoNaa&yIKeK5DARx1Y(%>{_{#_K)oYY#w6usjHKE^n!xaS+^RP8b z{(_U5(}qH1t~YgkYk9l>onO#z5!m@U4sUtBO^Jq#1|K}Diq8x02IuPVn5&kMxa0t^I8e z2^`4p>gj0`FG;TylSJ)%NslQ`{p!)cIBRiMZ&6*DmA#NpTOc&dL#8+ z*Dagv4^pLBcBwP3uJkHMfGFue^A`-D0quUt*2t^ftFERbYsev0e<+*>>jOWhm>jAk zVP+$-0=Bjzw!J9(G*rt5lwyO@v^vai%DRp$?kv3G;)U|fw(KNSlTUz3c^N&~z;WN{ z-+wROMGMerR2AMQqaq2=jPe~!#`86@8tO5zY^e-J$J2P~-5E2#h|??mo{r*h=KD%L zMLzRH5@U|w0HkHijhN%QAGZSdCVY}zguB(n^hRL`eb_{Sv@R}RGkEQHNaX#o6Mz+$ zUvCI6COE%JI`PBB!qS+tB&SUIpMFM+UL_!r;Qgq@i!+k5r1ecL@o$Zi42-_-^L-SJ z>~y3`OFPs+LV2}?>FM5B(yCh>8&ATG7(8XM`0ti(DXVW%7Y9KZ9=f{PkT3|%^g*BP za`#0r<3H%fitWx3*u{|ysTXaiRh;uKEx8u(xR@7gAm{h%R(IoqCLc+)l=>wYol>UA zM-F=r9TzTF-uQ+{vY|UFE@*Te4K)xGXcBjQ+kRJQG(2@pfiP*G{Ad@v|xnyYh_Tmvzs_^+ALg2r2&ND-qWu(0^cGpt;4mpWGJY&;S$AjY3P8$x1_3PH= z5n@LAq~%F5B^B;nTLHpnb!dtmDtOSGT9i!Gp;q1Yxb>P@8u|v|>)8QvjmG&#>2MO? zCkPW#R{V)Ne{0@At?=26tXNT0Q@#NByn_r@o2Af6Ef~|ctizB^d}NC;#`YyHc?SJ~ zfOWMJBo#)I3^i$^<;@~|==OX1!{^-=l@KADdEp$(6=21d7%O7{HF3Ga-40D%7Sw3@ zO7ABHrq)vEq~D+Q5zAv=%tdIlcvkL?<#`oBY=c$DgW2BAunuu}rlBh(#dGxp!EbIi*Ch>G?{ua^)BSq0A^8_zpm+7@reom zU(%RinCr2Z+rEtbosZz8RcD{HMcXqha z4=MGetc=jpEFXc@kqn#x#qob&2UupIRc_Gf`Egq?g1WUSWPVDY7i9zUss`*QNt8WU*YrcBIfFFY z_~8FEDBEAZH= z3QM?lYxKmg4=CM_lJZ?NjL+i!DUc^~#JqQ>&aP*~_G%H<@$G0k77Q<0?#BgSMf4YV zpjnbkiK3i1U-A+wH)o&MsHXEN?A@fgRpYa)Gy#NmoFPMX1%HC#;=#CXATj?Cf)2EF zBSr9}N2-JRA*Aj)SEB6sih!+ZprT@!T@NuS^wiWQeGY5tUWS{6GF_U|VaZ(=XHik= z@4dciElp+4;5N<>!txjqCmF6ty3)8d9|&-o$o|Pq<7Np7R&)wyz84}s=Zu2`Ai2}d zolV`WrpBy_ zD`iGPKYq<{{R`=Rm#GsMW%8DT#)US+(!E`tgImRdXh9NZ*JciG)EVl!UDACg#5@6O1#z+%%tzXmTqb8JG3-+ z!I~$oeY>vA-)&i+du+^+J}dKG2Y-aZ=9Um!=c2eHP>otu*ymlZ=t{8S%G$kW95{?k zLWKs^-@Xsme6rac3rSI0NhL9oDm%aKAl`O^aB*!3DboTUZbBA`FZ!oh??F7a1ZRD^ zs3(c7cyCo!@8cLqgnyl$2F+TkB z^A!*pKFjM(LK*PFP-5i)7uc%Ghzr*Zp+uf1<4mV>yjJF73o0n)u~LA$j~))?=_aNx zX+H0&RC0K++vkH)r8n+fs;a7nqEuM1iK(ffGuPYMdA!zrhTjF!$;g)#oSd+~yzil! z)wt%+Pe=%m$;N12z1o={l=QQM6_Op2g@zLU?3iop*NK0t95#ja0Q3nw!PiW_`1kbG zUD}7rz=4*(h3plm@>*J}CT3=d>FFXNObCmn@zNu8>n7H&$xS&DjjK2Yf(|)peSgvhvwq;!>q&X=N6B=D&QC9JrLhJdR$Xk%ucE;TF z9#t^fbM1=HB;`cYIGqM`h@aSSIbtx5$%bLsyc8)6qM&!t*(+=ZHv)goTLjs2NBxd6 zSytqL0wgHLDN@3zDb>@X%PGwe?9B!WF@mjWI<>&sivKjasetq|D4qG7IdsJs+;l&TCnqPbSVL@bg^LA#e)={vG{jMWcwfzKZ=M;!H+rG>U6eVuspY%H zTHj=?MpMEjW?RhC?JtR8?FH)%Pxp5yIed5tkz2h4dW@x-l}QM%q(^Srpf;s#$Yl(!*(`fP}DO-Q4GoK$0zQj5EBG z_q3_SD6RzEffH(cnZa7n*ce%J#mJr+4J!CARqH9J7^8D={Bd?}YBU~+84&#bb~(Wc za|AW;6Yo+N^S?WjydyN?Jb0^l;i{QdwiO3gP0H@g{ow3K#8oL}^LcfCedV<{H}Btf zL^5sNyWy^`uJ$5Y9O(6bGGPTU<51998NM&~QSm2Jgsaj9+onSXiJ*l;^11zWyxmLm zIIngyx3M8d3;!NT9X=v)r@5U^KS4nhn<$OU|2TMl=Xb#h^`FF_m&r&2AQLEJpHI04 zKo26*SWcpa(=M;z#^v%5orHnyu;IW~URm3B{+Ks<_jOx00u#`}=D+g6RF$4WAFfR=N5!N%1wr4g^DiwdnoVJK!m6)-5d8Xl zhrmO~w^#d>#QN+hSGu`JAv zMHw%^O`LEf-8M)Bd})6~?2ju;XqUO2dqDtl+lUpd_nHexKQkCx8x zzxOurP(|6o)ib3_mqLpynzLrLqz$*eCr{^<51j<{1C36NQ=vs0%BU^wxh`^V6e zbA<1))QTp2p0E1??CN-j0icu?^zt~nyU_&Q;j8GXV>y1qB6xxAV&U zw#0w_{3%kVX>V_@TfgMamN%Ty3%+sBY7gVfF>ISsg^aJf{zX+cP6xA z#r(ReglR>=k6lVb10`P*j~uw?5DugGU!V-Mx5jm;OP2mUI!!|rFST>Cf@7dR9SDU` zRz^Rx??{>-7auP|ftH`2A0S2XQ%46sqQGzrt@*xp-xaL^qO<}Dw-%_$8?qW(Vlik; zTT#H`F;2nC#s=z+BFm9oSlg7bGxoPvg_7+QRzl_VxjjcIP!alAy-h`)> zj!Y8-l5u9Hf?T@9V#S&ht=u-_Cj&cl``EZ+p$<)8A4zt0w$vz2NT8fkTw;3oFSngp zj10FW!tnk78_&d|&uj-b=_iVWR%Em+a6b{5^kdQ0RG6dKPm^Mda6mpwn@kojRcEu# zSy53jI{h#1&{=s|F=#n8U?f$Jn*c9OiaoiY*oJxve~HazPnDHgW^?8EGatsX2n|B$e*@a@2wA9$5m6duQ^i3N%;uhThqi5qiV;CK< z+MXNQ!~K!?%uG22J8WD*q4e~0$ZrszAhegK%I)hzOZSB}GkHszdI)8S2cy zic}L_@kPw{Ex0PZMmPox(qvo^w#3O&)vsTIVwtCBU|N~8OUuGq>!B3;|GXB`NS2@; zP<+*PY`1bLU!ss3Y}7x!=4k>jI*!q%0Q}k;3{Of*x>&8JPn4yyu)E%ArC}T&N4lL@ zFwwv@F&WWvKSIn{uBOPh^PkkaUKg$$dZgGJZIF zO5^eqq|SCmEC1VTedF8XS0{riECOR?xC~11I!p~4ak=^78`k@kA@1-0=DGb@SgT7E zV-@lB?b~^r(TnJ7r z|L5T(QQ_o7*5Le48+V=m*=3Kvy&A&&MI<$ZWqtrczJN z4^1!+k+mv9B}{MKgHi=G@+b&>5TFR$!{#fx(^veL^1uc%Qw>wBe3PO`XrkslJx7_x zMr5q7CbRnB!Uq8wHw==)A!O#4fp@7{CVLyY2>#vd6 zlZy+y$_n`Zi69Es;^?AnDPMz+b$mi5o!^2iky`$jw-!q=usE$EQS;KN%I};Zq00x- zv!E%>SG$lk`Z7CLbur}R8vk{LxOT=*_3VG%)?Y8GB#~P9Di&1jhe@`A_FqsnGAKl9 zjyd_io&ejgNVb9}%H{nAla!)CaB1oNE1`5_+^KTwF!(yh|HE9v6tR5yGkFm-5PKZG z8aIOKkn<@@q9&VU4nwZjZCfsg4l08zzjAJN;ZBlLAs~5UEE^R@5 zp}=>K3O`~v&Y`w7s_C2=qQ){D$uMg@e{!pDI8S`BK^44`AbLGKJg9C>uC&wPpgN{Z zZ%iWI_GZM?Q*f9tSV9#cbG@q7^f)gtf?#Sp%-wK)xYo)|7|!D~7Ntw;qbbe(c_is} zdlEX}M-=lLqT3COrz?h!u{9m$HJJs}!l;>$IxlYKXbX%>{k-PDL#%7*nykIj8sE4! znCkKKpj`gEHcc3?=2PDm0cFa9sMgW>kJGj_ojdd?^xUZap+xR+lAkX#eTSvl4zFgeyCEqiX2BfBREg*w7+q|+X4GPP1~mG~`?Q@y`pCmG&|mbdFa*$C~y<4%h6 zyWRfn?K9*I4i+!pOl;tAZ9IeVP1d7uTH~%_ys&%k!{79$WhIVJ)EH~YCouH9;a3)j zu2Vd4)phxtyp5cgQy0aGhz~0cLv0_H8sLn03B3r!-oiohy>+^QnRLb5?w>%F(aaEZ z-{)>0WnI0wKXq#u?cfKVL_XafU{c>D-_;(!OLmVH>J1R*l7D~{-hUEic#cjHeQU7` zmSVDhz&7)%6YJN5&GPM~Sv$BCHsgU;&oftrGMek~QNM4IW&%=Z78y^70FUE3!u$J# zpXo$R9&K`09P#D@K4XSA@&wgq5aIft`t=vaW-h-0q-CL?LKpPQsH>kg08XmSXPNGl z&Pd{qYR^%R#Y;9GzcI*z6K#FZE`&!ov2WKKV;X*KmTA@_J)rup`N~sQ<8p#hvRR9$ zV{6g^YFw#=l@5dcLmTlEl`;bFRe4d{WTrsH+12VK{lL#C27o_Fi` zX8SXYZwn#%`PnkS#d{^|sNfsZ79!Gu*5sJMGxTzf2S2*grqnJpXU{#?=kZMKLR^BB z2Nv?*>Niy17T~wkY>sF$uY{78ufbT~PYk}t(~w3ACiL!izE9^(jDtUJx7Eb_y%pgG zd-;5|q+fro9jEoSbV3HcmySkb z*c#tidBw~buHQ``lnD8TBa~noUi;>bTt7p(b+R$*McOPm+O~iGlF!zch`}f+bQPF+ zzd7D`GkTAEc)mOee}DUMv-kKE=2{9D(w{mb9$4DW9(#x#LVnreIJB03pLcs%3#Mk> z$}YMdz`SrKUNTyniJKA43E483dBwgye0u7X>8J;F%6oq*bZWbkViRfz-{*2UzI@q| zdWXH5=Dx`J+J79U>9F5_wB8l)z3k{Tf*~w;A^G_{;T=4dwjGWeG}2NkH9q>B)t3M2 zzj6A<|9nyAXHGcn76)QGK;KfbJQnJFq5E4bltTQQ%Cv(v9KJu*b?*xfI@fa;!t}n= zftVm_{{sKKCVoPuk!(<|@_N*2&h4fIE7MK8(t4YPTx14GR_-0x!l$yF z&E7mnOql6$BXg+IV}?|{_0J~m)__G{3HGbi>=6BdQ^$6u$}7AbZbRDb=0As5-O2Qx zhB9}y+4nb^Y@ZZ+!4~vE4f07z;e+BE6oRQ|P3G)q+v5-oeiN*LnmnH1BPzh)ise1M zl0B{pM$<7=U2g2(({st~nPpC+S!sCZ}YNKlpa=F#%HMu*-nJ&9@G&;DXB8C{DuHg_jMbqfMTH8X=cf zAMMO}Tv7r)Txscf4kFVBn4xS~qKbU)Q^zE=2*_g=s;wmCgt$0&7YgHZ1}6?~?ze5* z&Gwtq(kTNTrk5cgBHlwBsewyT+j*+@;Z{toBD#+e*Tm6Bf8X0opd&jqHml{4h_XMX z-$Okb2$oWU%{#pY-cC|D&Mi-x|&c+(Q+qy0e{gd(jL>1w#?Bo64 zK&`GU_UyPqT+?W1w@pvcd^1q2=}Hz~hY&8bWX^2dVGr!C2I1LQbu;xKW|c1+yfFtl z0$(wy*r{bH#$>X>+__-^XL)@bw$w3+1LKap*;(EbMOdCSY^^&%PstHuE)sXihLUFP z#srK8^SZ%hQJA*9^;f$q%8eF{$&Fh1`Fc5fa<)m>)8z9?8`{v`Q@!Q1b_@^?O}7HP=TD6+5TD^Q zo+q??=`V%~2^9;^a<92`Qi)@@nGi68qRPPD|4^;g4Ab`h=^2u35!1UXr17i3T8kSx>}-WI`P;{H?q6sS_*#icR~gGu6U2xm-(rI^POqg{O`9m?K`S98ydK@PYfL zxl#mOlYBtXLv}{4z;4yPkfOJn_BWj+G7N48d?uDXjZVoFi(!rqMGEd*Y{y z`J^uH)B3OW^`muurUyb1?_SPY+sTNsLSht!X*RnP`)0wU^E=(Hw5!~wf~hsoCx;e# zp*6twe?Ul+2PpJdxR-G+k_rs-5Xb}#k79|CssgyS0M1Z=rJfm8xH1kGSUU&Ae3x5(p=K3Ad#2l=wRDBgE zW<7whB^}?H0Ak6{jNozo=bPz^Wm#eMxDw{NH5!3GJ{>ed{#eOQRoTPs{=Z&;+egjx zAIwSNqftp|BF**Umh?BcuWw`XIeE}g2RvL&b+yU>zw&Q?gEd==0pU|Aq~r_`kfM+f z{)j|kBjihEB6p^ktwj1ESZY-y4|pgE5k$s`b;tO;-58C@Y`L9h90Ri3kNt}a1g(`^ zbU+K#3oW7>Wg-+?{V`ev==yLiO1Am0LIzVw^y|4M*J|0*1gta~@HyB4>}J{@(r*!g z3DUu*9hBm~t^6EFbhm#vE)tUEf7l?D3GPp&)l(QGW43RAk#*jtP}7vFJe{`4>e1G1qrx$cMn{93e#I9fvk{$l zTd1|(EH{sm@U{<|Coj!o1|J=dtRumXN-1y2Fs*JBWW|J=EL#d<&*p3??|VX!zXxot zvbORoF&j1U&?vjJ}#ren>C=e^sd8zrx>PtpilLxVw>?i@JXPu>hH z_5%~^{Bn@QVO9*8k4J{-9Q7Huy!4b>xy?uDM6ax78S zOh!FkuZi-c+c6E}ZI0PSwv;TnGkC4SKHg6!ByuxYM*D=sb|8gWU1RAu*_@Tss1I9 ziPBlCeozCI%zF*JR(c}u)+n9XzP*X9y4WkOoT9D*xq2TwsixvJhKJh-2Z_vNSji|XM! zTVBfqW}`rSf~8i%pc*~0@{|*;PI@1)r?lZd1|-G7shW_tJ3E{!Y=P=|dG=zJ8d@K{ z(iFU9A&EHDe$C_Tn0iqKDhuKIj=TUPznwI+l%!-kPyF8pf8v56R6uNW)i2h;3dj7n z%5S^Q(&wWZgR7F{R!hI43kB{1s810MgY9&|j3ZQ9ez}|nQ)R4lhJK?jLHcE2zNF<| z{}qQpf#mpU$c&H2maBJz_k4e6=4}Fk*Qj4~p@`Lv#aMiPqBu)uwY}L)3gDI4VP^EB zf^KrCaH3yzA2;;*+mYd6)JH`iFmO5m@}d)4ysrJ=8++4^L%h!CzyTH17qhBOQ)9I=az**7x>9eEPwK zLYXd1n@q=y-s+_Je3ewgUl7R+ywfiuF3R(YeT5u1O&>3J`xp4MD4_6H&}i6XiVd}R zRc6{NcL7ghwKK?GwD7|YvdIqqruoVCogkX%Wgr~C1SHm!TyFGUc^km5;`Lf7FEQvO zncDhiG(Q#0$8W`O-u$@ORV%kAQzqu5N;ZRYCnCTdAPdGvHioH{sTCfw?ovz%55~|oy;IjmTTL_SHb1xllKu1@DXLjFvBMHzJjEQ5AAcd-=K5A1 z_q4#~!Ay0m6{RUr9WF#rtkr=lTekSB^8q~?Q3?TM5OwFf;@8GfSk68YlBdZ6_=~A_-^K{B17Zw3my`P6B zs1I(4>BLV!NCV&=!c9h1d+SaOo3iM9vYcvlO58}FHq|KE0Y_}s=N`hiJo17LjoWk~ zmd-QSPoO)vzHl@G0&g6{Y`bg2v6ZY;8ps*Qvn1m5j!Y7~%%)ltVf<99&-GhFJQy($ zdv-9o+r_ix4wC~vNq4O;S{v87aBaFJE(~7lAj$m> zEn#Hxll@fMGc?bRlu|>G_}m|Nrj;_&^D)=1OW^+EXdo4*T=U5#Fl7;qS{3x1+XX38 zN>{+z@J`vsD7;p+N}1+JpF*M{s23+`Mw@Dn-Hqxf8@)Kh6YVrva`!w$3jXCqHg`8H z(g=y^D}_vl@o@5*h;m!)laMzC-5U?I&$#^g;nJtwJi@bVwou!Cj zeaJ-=Yt`jUG*nuv>LQ~j=IjdO^`oAHerxzeLOii@D>03dRDp#=MH5gJRjx!w9~Kx& zv`_n%J}Zohidr45cxTLMsoEiABr^FV#(KB=W72pzXoQ@i81>^?fZ33BunX94a#AQ zdPYJ<$Cgwy*lxoV-R1w0b)7oI!;0%J-`h6MPFUpsBN(c_1}psS?t5;F`#XF-63fRD zlqz7_-{B)>r)B@t^Wy;|oHKN_5l7IyshiK4buZ<#D?$@F9$1NcljLA>P(SbBb{-?z z5iZNVuiW!i&SEOff=NFgx%*TMot|q*Bp+eX$s5(&MJkPSixx}$rzo}Y)V1}MI^BFh zY#fOP8*noL0X%Hzhj>i0{BCCSTr!eG2NhOzGGF2wyhuw~Tk)DJn^V@aB35d&EEg&VNe3wirtzu;zcuCh7mEBHU|c zbZmR|FG5mOVKonESU`Z*mhFD1_|?$lIxZaNYGZlS0Br#VBqaM_h2d|RJzq9kCHKtR zgB(Zg)7~iK4_K@T?q$Jy?{%MIwQTXAM1Hs93C-_C1JG0pW#l9^SOK~;as2lxySB7() zdSu3C!swrNqQ^o{*~2rlYV+fEL>67Pqa{YOhn404#?Nl1md2C;88;MC%n6b2vTaiB z9?mShtB6mB3A{d$7c0RGTffLUS1h1fq|&5CcI8NLw7)v{hfG(&gE+H^hkR%XxynE_8 zVhM4>0}pUF{CJRl4&DmlPZgp0{$a7GlRFDCE2!_}62EoNXeugw!u;<(Mv&3Dels4V zlu_ZZJ0k3m7WneSO*Bj~nOr&yi&93-1{(^x9gg%!aB;c}b_RilQ)w_+KR}{tc@N!I zE&OdzRdfg^%1UEul+Bpff%xzY7C#ZmCJLRcjf5=Pb$-_}$>C`Unz06lqxe!uT=;!s zJ522g2qk=;3^xXbUJhXlR=pXV z;5ArG9uUNY{eSF%Mqb{)5RJ@A0Z)!HwMTQ>ij`*ueA?=R%9Qc$A4thB=Z9t?iUzAk zQVypRf8xL*Bq~WtbnKFmn0AH4G_@A!!U2Y`8cP!cM`EgzVjlHth(hQUCF};MqPqA@ ztm;nYdpd_io#j`1l#^Zp7RZ@pi9$3oB=6I(m^Q5e-`!?vJysVnB9lx65KSVcj1VQP zfCFT%cP`;;fk}dfQw*&(bReP;qYUx3T=3tY+C034SZR4+BHW)=jqQRa*1~_;cgRwd z#!V@Wu;W{0(4*e*u5gDpJ&vNnZE^Wp_j}|!L;n~uB;5D+sRVy|YXcIjI0~p*)7(z@ z*P9y(DT(@KZ%qP;2bv-Tt)31q6RcU)mBGK%sk=XCgG$DjA?Ndgg2W8t__V)>14?#& zIrAkqa)brYG_()RQd$xS!rz;9UuQ4dEo5HtmF=gRC7$GQyK}^UN6@W{S>tdVAm3)J z!gxy+(040(?j*XwJ(lY#ZIk@+!ryE_h#*gqHQ55!|ZmLFcDRg;aF^_h|II#~tC;Z2B~`8IeF(CL+Us|3uh- zo$-r#G9XOeEL1xf{ukVWeZ$P@UA11aXOA#B7IDI23G73KLaqI{;%n5;o(_f9xyE`- z#0it_;{}~?q;9t`Eb!3r5srEg^iv@YpS^+pzQ26y58!&>9XK5vHPg3(g(4o{h_ZNL zOWppmTxzH>cAm_`mwh27y0NBHPfE70Jhr=di zK}zB^685BOAzkAwgmz}--^jfu#_?)N16MC)X8 z#&>3c(C6|J|?cKy+# z`XQ`MC9VGz%pBN$!v5%X$$>o)HmLskt`%hvDZ|xi+>U+B9y!uudPBH|11)5%7d%)< zRJ)_C&<=%Y@}8u8f4V`_a%8IT@JZSQUFg#dtAlE=sj$>6-XJr2@Ou z30x^5fotZ~w}x<9+wu+5tHCkyob2D+wZH)CCo`_DYvSL#z9F7!XUc1eV&&9R=D(nx zi4cCMHNZrvAl5AEUr{LXJq|r}gX^R(&?xJq%3@8NP~5Gcq#X>vAZ~|P1j{kdBF`!J zjQ{16FMmPw{J#ZDPLT&^)kAnFh-r0a3b#RAq1M63OAn!q)Exk)Ub?A=Q~Oq+0#qKEZ@WM zq#lRBW>iiYfpH+`2xfr{=!eE{6JZ%UXf=1KdnwtJDVA*Z+q%@|;;fANYal!) ze$evQ7L8No;e+n?mYF=3Td}1j{V2JwI{m7Uq7h|42?;525QH@^T`gOytW%6Pts4q} z#E-#uC7To$7QgdYA^o|~i`9c{C(U;Yy$QG%6G;<)hem;fjCM`#hZYpER)v#g_dwdG&Y)+Of+xOd@gyIj%wewU zhmfQVuwe;(^_uCVsWO$$NlZ5~PnKPVTl$t4a{X|(vN`}>&8_#dFm(58h-dLdu)8$~ z=B6pnN#~7NKDacP0QvJ=pw)t5oyEk!1uv3Cm#L!x;FKaWU7Q3bt|6_c<{6w6vmcy% zB1a7SM`SD;uXY@pkP@J@s2tkck9rq#$~@AvQU`|~(l3?@dh>Fa@9`Omhzx?BSkFST zqvsrLy>N=JfhFmRb49-YThH1cqA(a{p~LI?&MR#Yne4#>UgBz&!&%oJ#ttFH-0$lHsf-w%O_HLZUh6SqFBzMC8hEe}i{OcmIy5KZ8IS&F3$BPcS zYrU9F7!?h+w>2@4DFMhnlghs-vq`UnQ%h^UR)QSQl zOq$o`1aR-rtam{!I zof0{;3GG-f7xKIr0``};xoQ{VVR0b}`S*3~JDDS$>ZIZjhr=g8#tVV-0e|X^x33nI z@6=ZKce=`K9Ll_$ocbh~5l&R60xU5EVSkdJ91exKlOZ+6oZF3%aaj-c<+jnL$MU@c zc_a6&z%E}F8O_Gy0oRrWP@rYgi6-DoCE($`xQ^;=Mq1{jO!O`yx?_qO+t|vOosTiI z2XGXl6ph{9n54{1l1d0D#F-DqA`?xu&~HBLcx%Fzd(C#z?vKFq59%9y)vRQwVok!4 z0jC4wJ@;l`fqag2-<=B3#f`4LHAi#6(H#Nj9;=dTMh~j?4*Oa4H6dwX0|n(sla%TZ za*B;Kg*G?eboLzXvXMR?AjqA5gdMtWpH|eBA#JRvhJ2Uy-fUodo$5Pm{D%jjUFILV z+`^>TSZcaiwp~F#YR-XLd1%V_X^ghLuDZKi{KD^k-b?r|z4@4wev{qNn1pgn+}?Cc z6nCe0pX*h#fjdNkJ35Q?xg!r~87wb}o7q3XUlE8q))Gm#mw(I)nR)SXsdo!Q@dUEe z|5Cxj)62r0M`sM|u#buGcaucHSmv~rlB+wanhOx2A%!hceaMJ()BX6EFGHW_VLz2^ z_C2MeLHIr0nD|M*o};k? zG8f=hxJZUXw5Gqa8F3#rvE}}DzaCw-(pCvfx-d`j7F^YG4J!rpcZ@{10{^qBtN&h; zGd6VQwrV)Xz2n?c+++#73v_g$^?(MERosv~vRW~8$MCb9LFDi2g4@{6?|#VBXs7O zs%x9CHqSb#3R>afDVEjKhjUKavkItmJ*k2hpSOBvzK+5DRTbq+VADl!f#+anSl-p~4PiWxhClr!Op}-CeVlLAxljH`xOPS-63*@Q~( z9RM{1DyESpoV0?s3_5IWEcnm0gz<;3U{n?I+;3|zb#P0;qSo7h0Y$&hdg?%gC!T)q|h~B<8H-#Z5 z{>6ph?CZcTCqx_!M^YMDn$?}H@k3}>qbNYJ0P1FFCR#SDDH-#xzfG1vc7hc5K^U(? zsx?~ZUK<@?m`vu`PD7zVxH2{UUK8VPb2{%U-8#)%SLI z5CrBUj$O3JQ{G9ewx+<9&Z0=%%f9a_C_5vz(R7((oe((UCs2BZp26Uo3xO^_QuArc~G_LWV|NqHgnUyhNKjO%xO-jcKV zZU51uJ2uK}z`ZfY#E%)9ygEV+#A)ni^kmPwdbbrjH93BFt2sln!>l-$wR-oSB!VHP z%dR|zoAUvlQ`rS=W!CVpR@_*h)S7`CwCa$5K^AlmmZa=(%S-{lakvM8IYl zN)P$NCI2c?PNz`~pe9g7tZX-i=w~xH? z0o{jaEk|-IKFQrhwU95h&186tDDMl27)RRCDVO9L4zdylFjtwK83K;rfB6j)&|+(AuOUjQURr>+Dto_LsEmnLCroV0FcMa}s^} z(%l(4V;UFwWJHNt;*jykFV|liqshg-XWoF#smke)xbVO}nI+txNkfD8np;yf_C=-< zr$5mRNos&cu7pzS)Xl-#>+o7+NEI7=n~01mVm-R0^4q-0ICtNg^M>m{cET<7E?@nB zl-G<|e#$$Sx;)zZfZ?$O<8^Z!Jc&ggJ18gT%$awAJviB|TklV6;GYQAC$4{Yhwb(! zQ?ut8l>c~#JzNbX`aYc_D|s<^%|ZVkq7$xrvRVkI+veP3yi3&2i7);oLRTx#X+1D} z!e3h9?pg_KUOMyM$?@~=G9@5fd4CuguM}i`!`d{UgSRWwxqftx28JR$ zZ%bp^v?y;wPi;wTig)>D$41hJ>x@j47bYa_)~!-BJ0I>sbEoPO9 zwFQD--WcyB3rSdMBH_9e3`dg{mEmFUU>;ItHuwkQkU@Fh-0a{BHeiYAZZMt(64>hc zX^DOD#kN1@-RK9)?-(zil>Od$`y`nWzC8esFXVoal zA@jlM(DW%C_Uq2XU<~)?@DG!4e;dt`D;3;AxIUlD@r1w_`(LM#-Z0>(3RMP+r*Pys zS6FJEQ3e)wj?}EmVTiEAN`C9}jf?$&sSZ9%jRl=Axhbc$8%@td2`fn;N4W(R#}A{H zDNmzdW|iGJZEbnZ2pf%)uo{4xpUX134F8$+6`f0L6~lpGR}VJ z5)M38ErgKPgxv{MuQ5pQY3G}8bDsLG%A_9u@~qhj!qs3p9Q)Xefh8r^I#s$xxuW-4 z?EPfn%+9mul(Sq2d4I3OY~X-IsDM2b4S|zqAU#JTW52Nw`jX;KS>D2nKoH410dIbm z;c*@@egcC9sgl7r$kOP-od^IPcL|LEQyjm{zIi+rzt8S#xRnd6PWtJCJI)V8{FX#V zZ}zDRN;V7~wzvM1kOWiv2Cd2G*(Wm<@P02XyvIM+PDv1YpRY?{EGf4?_##0q>1kQ9 zzj{@C)nl}dnifP*{PRv-wJy?hYe_JU&4e(?4c_1r()&t-+r7plgPT_T#X-f(m-8bz zveGR`T`3EZZ~-QBA!Y=HM|Pe6X9O9Cgag?wzjVSND0dlkpLjj%%O1c&E+sT*H5|nUCFBkjX(6TMCij<)-)w z$MczrGFnr%-BgA0eLggrilO$>U%+*wC=I-6Fs7ez2LDLd>G?(*PhBCcJCzz|k>9+w zJD-s1@GZgUe?y_QUSEmlV9l!T@$lQBvOp*1~z{+uPsux@dh}zcy^3| z*97l4kwCCe5^DO?5OZCr~rhHQU&Clk%>O3jI zry7k`ceU^dSkj#|KQ4#2o$9j%GL`Y5c0B3F(ogPX34v2HC=i40Cy&7M;sm$K(hmBv z#y94otze};S$p^#xN8jo*~hT?w~f~nKPF04?@n|g_co- z#+m!BmJRbyJcK#EEN-X(TW~us8j31R#W_%zoI#@!iX3ky6&N0Iyzove_zqAxv%H<5 zrHMUqN9vDk)$6lNI38;P2Mbe4?wRleL)+JI+RxVL!h(csI4GL8(~H+HdNdc0-1Jk+ zh$;{j)kws;cd=O;#z^ohylTjW{IV+E@Qjw13t0_fNh&kQg;+S*y5@Jp0xtcfo*uof zzxK`k_z_duzr^&3wapjmkZ9Nsvl-%VdSyWn4A1p7qgM^~Zr(QBE{;@K=v|SCC}d9L zisk8iN~GLRz+Y{p2#A{1us)k1}NH?s?v+W$(~otlrr^NrUva!kc(Sjg8-5 zMx+#DsSviZ7N}@~%tmk;%IZT$doAHzfqI{Ga74)mt{K3PC!b9gAUZBaor~*FF;}>o zq<)xLWq74(ZaQ&OvK2%<2vB-01Xc-N?2ZNoY@G7I)7ZKL2K13~s+*9SPM`9AjI?91 zB>q~3c#FVWMlD0iSc0lHeI16(kVT`OD-kIA#H-!<#aeA^q+3EJC%`;mmHqZ??m>~r zEW92rMZDerL)DLkBz7Cynhux|2~`1>XEYoFCm>bCU>KaGZ_@rbSN#gI6(xFb77Dy1Mi#k_Rw~7ui<$oPq%Ca-Z;ZlwTu$RDz@*k zRTQlG;vRTiYn|FY?SA;0d;8j|X5p=IIIyZQYTS2DWH4OH&&{q@u$T_mcsS>lOJJ=& z;7(!Q`HUZHLV|c)YaLeeOpvh;vs^jR9it={p5I=kQ*FHLvGT)q2vh6IrX-2+GHtbx zX9P?|PgA{u_kgY=SJsSZjnK`l8*h#3ikTfFRQ4(=X07b!er3hu)}#~U{zeKzuFZ5w zskE5Qsp$M#2s64Gr28m>H9X7ZO>`>{NByU>l0os7QQ|^~tt4-@dMrI`b!_WP%FG%z zq`cQ~`k}U;ju!A=Wih=oT(qlBI!uD;oK6Po){v>D*!hmq$_cwXB?jChcRzsR)xMg5`5VIgTDc<0&@#oUqmKiO$lT(&S};LyrEU$&24=rZG8-y>Eq za4LJsk21~FvN4H*?T?kArq+(t-54Cpy)-&;)Y7)Cm$;6;I8Q4qNrFo#DaifC6ikN0 zjK4m|B2SgTWkmj3U&A0K={`>$$#{!PLk0ElqMw_P zHkD}%o!*Me4~!s79-eRY^ruYHl8}w zq-+q*{th&K4L%ZVXR(wKNca?iOthSku~|6G)N3HI)i-^?>`Gm}>$JUKwcpgAz7@tF zfN!^2GPF)5Q&<=2yIMsM(k(QuW+OqS*$X*JSR2~0zFEJ4+uXO_x!TKjc;WeLVh$7) z8JT)BOmxn2=@gH#lh{lENyriyG9;urrg~~y^w87=U`W9-wim^ixtuW2Juw@la7;*a zW;DZi9(ak-{=n|z-)V*Lp|8%vl$k+tJ?{Qj_BF0_g?|RGfzhW6G`a{<$9Ym>?qOjl zW_7N{I2{wpo=4*AZ$c`i%sYTF`qk7<8-c~ox!ArDIR}leiFW9#FM%+iaSt>0#K;}l zT)ZQOZu^uKvf{7(F4>(sBLAWrzV5!}=TG}&+W@e;#l2?_d(SE_C%>R)_etBE!K2d% z?k>IJB=f;`$4$p2O4W9cFd`Kv9?^&ae6!bWYt<69h!|@ZvsMXROvJ~XTFS5Y`4W%% zCkb(z=9IIS@mPdNf-a4#B8GyIZD)Y zD{%sA$n{BI7R{^-ztJ3A4sp_glQXWmAbQNw8*)s;9<#sGAfM`p`6?Z9<2)&KNk4-1 zcE*6?NQR>@{(?173@A)^-kA;9Fia+~)=Uj#7o`wGt=0o^SZSJSssim_Uygy|s zeRQT~CyU3H*c*>CX2NNft-roZ(hr(cY#sP+l`~rM#l5?|L8Mw zV!;w+;^2r5eLG^c>Wdf*0!@p5{aqg?Y(mnZZWq5l#{1L31KJ15)r-#=m5fJs&qVR_ zgQkfMTs>r8+wEFb;-E705AhT2DTaq5*!SGz&2qr~VbL=LGw7F-+wsOUMpGg#d3M@^ za&zBX7|>FMiQJDqNzfMd<4DQ5QVo`ZxXG>XC^kBg+t)9`%75Hw()q;|6h)I&^DIlb37R)AM-7VlNTh_# zt{m8=V|r}0v)23vLz&0hd0*R23k^;>&K3$+X|G#29CF9kEp?rQfq~)W3t8goJYqeO zuMg$Mnqeup!m^ai8>kWz#vpW7OGP5W)n+fq9_J8BF2ASCYg;;_zq^>6-{;oXPdj|g zYbxX3=h)J87(gww&Xn)!%4d0UOFqjPS=j|z&a;E{#!GV6<~xkNE|0=7NA;EEAnVL9djYK{7ji&zQ9}Q-2mt3YJrcVIqOQ~XR!X(dp>!`80DqC;aU;0M^ zu+mA56gPu7zXr|YEia^tq2#HD(FYo{LP`UC&ILx;`Ugj`l@xQlcoe8u z#r(q@reWlhEJPVOtwEf{O-p#~>2zsFr$^39=1OTFY@Gx~BD4@mPr( zF*&{}{cGRXCPxDneV~Iu_undriB+2z6P|64N&c$4wrDX#H2Ed0t!9%|O( z<>A4YODR{T`n~XHFUS`{S`8#r6#wM-hM7{+w9%F|V5brvH0)k5qP0h(ejL{mz)^LN zzYvo)NJ`m5#br91v3dDO?JsMs(XOR7>4t{~;K(X5@yw_lY-taH&er(eibe5}-xx_S zheKtIBAhOw%v0iW>h-3gX9-)@R@_d*e_!_dM=9*~HoIr%u^tv2pwlL*jV)J)&{F^W z=_Oa-YU<)UKlbMh+T@GGf#=0r0B|Lj3Hyvlwctt34LqD_DPS%bw7(nwT~yWX2Mg-) z8#|P-fcf>U&{_R^T;Zl!_Ea_>Sifk5rlJv3ALfhUcW4rtjE~MXC-1xP|AX9Pck%kR zu>6UAQkk6)%yoIO|IGRd!rJO}2_QM(H=DMO1sx`No%WKaIJ0#JuJfX5@sF(PEWN$h z6BB~Zmb%o$25tu$om;F~4uW=hzD2llB24K=C&8T3JUvr)W4C?NrWML2#3q9djxP!& z{-|yoEVf7PMPIeypH>)1)q}V5BXR2O6kFq0F>O&4E+322ADe_l3U7g)Jh3Oc%UuuX z*-<|y%K9PlL`a*{Z%^&C2QDuR8mnpYL*8meRmI#L)5&CxY8iz?9D2=)DsD{f-DE87 zh)D}VoSv5k@a1D~I5?^2Uakz5s5a?-t^nqrfhr2BP^!w#Xlkb~v!Hgg!E<$2{nu{W}gO2yHVc05g&9^&>dxmci2B3)c zZ+U9A^aY@Byjrs$(jWd8`^U)s5Bn$fWf1zbEW*V=Vz@B+EQ7NU^UvAfb?0+NEEiCq zx)DZhV+_qUB23bjovRL&P4q?E6xhTpe!0XEZ={49mRP zd)a9UArC$`-OS*B7x2XbhmsqZnggl-{_9tE0vXq*<(iHlhR_@+>kS@t9o%%U##*tGupwQ}hOq!pe|t z0kfu|8Xb;!$GDo99=KroL4NnitWFOrnu~dRt|0BGK&GH^sO_e}j5sBMgkc)EsWDF* zklO1ZNFn$cOHP)%J=|3V&8yPTh@4H+lyZ}EDo57DxVxZU)z!p^^ zQNEBjDGB`iq!QF1`v&INf?t>$x4-hQd)EEH5@^h&h@*0B$uJtEywZaI*M%F7giB-g z|I2r~2rcyAk`AdC668OCy7z}S^?qma6~cckwV78{k9KfvMIDyPjTR5@lR2fJ#}a%63ykWK;d3~f8vp!7R9~Fu95dpzF<#qkTrJwF zNh>aYC1pAw_;u|&XG%kNRXc70S{V`v#`v#v=B@&4%9>c2Zuk>|?qc>oR^ooakou`b zJaHg zxINQ{2a09|$OPy@c_5{lM-4#x;^l!@WZpXAlc5ui$^<@I zVaT7ol$7Co&o4VqN@C-j(?D;k}T?EkQSI>p|p366Q3@1u%WBmASK z3wSFKNYf~RuR2Ckf z@Nu{5U1o9M-~~~t$y@Z3f_HJ~9{)L!8RZ1 zmAALnNy|p;gpsV{w;($iTQ%ORlHl+z7(&`D2n;KmwKj#`qYsyPA zrF$ES-sRN96OTEM)OJ9T6DP%5V(fpiK@TXQ1ZPjn^9l!sfdB(R8h}C@XC&~nbc-X> z)JnGe>cO=ozaj$`Yk4m6Rn4p7b!XE4oI!1I4aqU5hXYyKg1 z3D0nX@bKh7p96w~>@IH%b*HB19z`Dl(6@J$I)a_u28`0W&R|PJVKz>6VL|L$B*Rvr z4QR0(Gv@}tMo*V6|-=eY9X zm1ZZBjHnJ-&VNFTB+CJ==RjItV+>3oj`xe2=i^yTPmx7t=`5KXP&(3x_Cp1;y;qcS zC-c1m!2DpUfH2!t|z`u$lQ+2e~yREkiv>LM;>>7t+ZVom;8D3-`i?qA1QJJ zBN3toEkKh#&LPeNn=O>V5*9*6uK~q`5R1i3+>9%NDxm%$4UC{5NemJK3%YMix9R8- zkX5)gwESMj&zmPN_~Y44r!=+oS_c9sK9!BUrraxZz3<*PGMH5SLFM+!ssm zN@0ZHukG5N&7wNy6Ze;vl+XU|!Z5WL@9e%6xTfis=Ukc}+Fz=xORe>`94EN5rHceU z%kJ^cc+?LwHXCl4+taI%2bo)qVSR3JXK~gbDLDq;xyxhs9{)N%D9!c=n2(wDlA{7| zlK$q47q{}7Ub4YAV=5M>WTNdV&0~h8;H(%MS6lXe?K(=yJn-J_SiwMeOlL`cfT2gP zhT|sXRu<3}FJVS6OJjMg+it(~ymOMf2iSpwAu{rnPp;=mmCvI$O*fH>N(sfPLMTQi z)Jn~qD{Z59gd+zwkdQ=iaSDOAO7C5SGrZyA6txuq4RbA~5O7ml zZ5k&5WKtXn@$sV<8%{V)B%fL4YKf_XFGwrspY`c%!LCcanp{pKZc$cg^17nwUmvA^ zJWRABs4uK_wMNgKm1pJuzC|f;J1T5{KSJ`}bc-YjF)kN8T(+u#d@@?BUjo{>fvKsl zjC;<%a!`TY)axq?PbNUAT!oVmTI{_+9)-Ix(D$BaE%8OU>F13yhC%=y#^c_)%tsqc zAJ;cz^zCFe)JneA`=a=tG-6lsh-=m**w(OQ{cuMTNT8aF? zzi2h0zWjaLZ%>JEzB4|tu@czHUsmbtuhfqc&@&Ahy3Wf-7cVaOW2ukANbCB^8Kuno z-r{Vv*;@ruJtnM%waaJg4cc&{;ay(9G=g1G!#4bst;gQyvgy6|c4=`qAK*@AzEh_y zZP+}wqP?Ld-DO9&k1J1d(a^(d9e#{_Lo8-9R&0a#m*NDw`%z{fl8%U7jOk-pJEG4v zFK3T!DEXZSdvZvr_(=Bbf8f+YSgS+bJ~zVjnXVMSgA97Zr5*|V;p~JT(u7eqqiaj? zHYfT3S4O%ziYYbz*~pe21ReqSycmGCSsRBw3uXG;ptx5Yk#yuaYNRJwB~JQYSICiJ zyf&rl;XA37CC5E>h~u=PJNsEwcfRfba4V`HU5}R=9O%GrNtLgZ$UYT#YBU8#CQ)2o zp=*n1M=0VO^EpW&HTTQR+_ca{n)wqs=V9@CkrWUd1u1E!ZfaM#W_b?p;LqwUM z{GhY^Od)X(YtztsJi8K{0UjzfjE<5598O!pWxAZIbQIn(k@xdPF}gY%pSfxOCjPoE zQ2hQ?hoAIpLHnQY*#C9s*bXylPcm?(1-@=f9`K)pX+cB4o8tAV{Cs?)y9G{LDmuphV-NBm|7GD2u*h4zjOB}v%#vBW;3ko|o&M<>%$ zHg}k)?-3=#y5w6O0jjN6v6w+!nG03}CgXip4WwfVThQRZ1h?OlEb0-dlSX}8d4Q=y zqxv%{sUjy1!=w{bw+!giY27Z#W8*Xa%$QKp(7=HdN{2U93sNS;<9D!%TYK23 zhx#@@23#AcmnZ225=IQ%szPaj)Y2LM{W;8j{8y{;%h?5(4-_wae9@Rx#wq}c(Lit5 zaIm8zJV=?ac>0(c?Pru0yLCcb?$NpjB$PLl1E*blT+*Kub8cBPv@F73$IghU^kzP9 zA7D@&V42#wks;LNE6NqJr6QiMNXN2gr=9LrBxF0Np_~k+AK_~Qo$WllCf~{hJODz$ zvg@nNk#8KISsdJG_aeTp4R#_?wZFKuAJ$v(2X+X~mTx_4J0{4IYjX0RJ}y|YO?%3( z9J*Ec9?Vo;RLW%(emB|*nG#XW53=n}W*H2HG-NcJb`j&G%k8fv5(3GPOavTsvFyaM~3kYJhLG5E^7 zx9r{owsFK@WH9hHrmUkkG2P@At2)gXd?CNW9=sVUqRG+3n`Fzm!t>~|xS@P_btL=- z&}V(cp`bxweU@bZ14+>SH$!i-CWcfJnoiyYo7oqN@dbyw%Nc&|i5x8Fk~@^*QlFKL z4>v&++m8NRa=f|pe3AC{jX5?&FikF{Spf&vr}3w^+%LoSj{}ocz0ZKlZ2aEDF5i5g zYUUa>cD@Fd!?mO31}_+126MxkUy@%-&~u$-ytv~gI_qP#ka%1vPE*#!*ToZ_`q%1( zk^;Ca+0=J^b7hpqn<{uF8#2kJvV(m1nU~%Yz}pDEl7Q0~6bH1gm9uY(ROLF;SMZ)m z#?6hLjQxPsPkKyrX&8NdJA8bXpW~GH4LtDNQUyi*XxYe9nY?b{(VYSql=aU<#T3t@^Q82REzmj47Uk|jF`(SWA`~b%{b$y zTgg188;WZnRDxw3SG|cVbwI0eR#WkUPW!m?|03)xgW~GCHcdi+-~@Mf4epTO?(W*S zyC=B2yGt5(*Wm8%(zv@$KhOJpGgDJDHC6rNRNJmT`}A3BU-!M&;sCRa|jR3Q}^j?ir+-;TxWs}ACYaCr&~sX@(DTLk%^d1M>1{mB=KN8@%m<*c;c z=ym7_Y<-u$EjP6b@e0XyzO2h@zT!VYS{uHgVo!QqlV~?ZzM!A*eOx||HK`^tzJuF- z;@sR$s(pY7uHBQVP=8S*W2(tLt1T?v*>b~HJm=GoI_d-Zz+3@D7$-aVlatSF`FK6Q z+*LAwqCWX}xNNy67d35F^yHbJ(`Z|=2X?+|l4}z3yZYEs4rKNvILO(Xp5sju*RsxIXoT^r9|v+dM|v6)G*+d4g#47s;qKG=7>TX8ApP~VdKnHFr8{_U8opt&}SCSd`E zF2FcJvHitADIkRf{=SZDVZTK|pL`e5wlJBhR0zAme-Sk!hkeAiSg>FfCbxmy(PUUB z`Q1ASe>mWnpOU3m5p&ut9EO<23-H}p^p>B2-dB->^Kfz~SwJEf+tv4-=0u(<;RI6% z1bYECxPRq`Y22MEeSZ7m1K+z6gj==>y?c3Ua_^`M$;||kF;0n zhq3fPD|%H#ktdVbd~0v*S58WSgf3a#0FJwBEibWatYtL2!_ec*hF6B$+zwr7ePf+g>JH!LUXmzQmc_d2Zqq6Q}&In zVp8`#>kS;ov}tLk*pjcgt~q&;M`|$g%9dp>Yk$gr#9Z;)uki#EzStQeW{kBu2y^qB zkYFkMZS8IdqMv4*yy0KS+WifKq~SZh0eIehqCZcaSnO*Fx_Qal&6Nl4&-!n zRqkor+^L^hWDt9W=yH{R*zTbgcVWqyI2tZ=tCg^i`&g$ky$`JgsK-+Hr8iVSFR_wL z)|>yz`OM<0+1CrHeuiaa#WS5z%;MQ=cG{eNm$E`DZJ7-V->)7VnWJ9!?#x2@BMoQ# z0OOm;D!Fq*#_W%WEs`E8-RrAe@DrtTp@0oFli{@LU=s6Rg$d#B+ z-Q_s1vA0`(^3Mainon;W0-lxj&02=i6qfVWm=|48zxE%e2K2& zFlR$7z^rE6791{v14o!3aD@4JPFZ5af^&6sU!ryvjL+-)1=t@b2HambbDqfNj^e(; zwYj!Mq36z@wOslmzrVyxad*toJK7hbO$&)0Chv^pXRsgk9BGFt^iXzlWIM9;+@g&C zWC;JQYZVe797QBYpWTN56Ca-b5m;Ga=gLXAvOH7v`xJ7T^1&s8rJUe3D~3KGNtD` zVO@5H#@0z~SIey_a3k#~0+I63uSvgG@}28b8zV6L8;9*TOI5Gl&<~``@j226jwtj? z2~eY**cz!wlIUjZYmhUd=ZaNjsmPy@2BUQtm*lgY!d+J7XIC*58-bj|1qaSAs_bdbw%?Swn-REc9PSr&U{^9v{%Y|m&99DBg;8ldfr})Mz#(k) zRR(ykk1d~h4cuT&wpP9KQRIDHI12%HTwGHn*)WjaX zuPnLy4nFO-295*2RUBJ>dVxafwyuNWl*nvE)e zF^iJ&pXrvvYri!Ir8hTWW>JvQ{$45(PY!U&SFGIJ)iC#W)hEy zx7x1%+`s!wKHmCZAw5PjSs}tuzua)1)9J}t1pYPOk9?=K!XW1cK4iK{TyTH@)?lw* zKqdaPuet-Kp4{fdG5YZ0;~{T^OZ;zgPN>h&34=85TBb^>47W*$EMWDR(>#}$x*?r-mC63t=3Lxy3#Ok@|Uo3St{-*LC4;S&4@)J z5$6tZQ{FO+aWZgNF#vOj<^^t+2Y0D`k9hbY(QH~}J8DrIrOBJ53#Z?c-v6@xpP38| zSn=&qVjtT0P2>9av+r7=uj#%NZdUFBCQp z7_gM<>@tXwS*c+t=Wc}3bfh$Sv=;Cp`2}rbnaAz>ReQIX%|1S^Yzzp`c}+{qy97ai zsLk>BX^sZ<0?^CKn1~DqozHCToxq4k0-T`c7(e{=!C%tuH?h!xg>P(_mSmOt^rZ6> z#rV2R#Dg?*rb=HM#szr@XiAoAd@#7iAHT@b#7P-}^G5yW?>j z8ucGA+Z?np~OqTP&{T<8=X=^ zm4=QVnlgxpe(6IVoGY4l5he<<#CX6Wx3JElPL|mT?9Fi+{9z5BznC)srSl_y9dis_ zZA)OCL?)*<;D>F{D`53{+$yP8y6fp6Ulu9u@fX{TJfG8|lo_Q6hB&r}Om+Hb%N0r1 z`FhHqwai4rec8WS&XuHl>XAYE3v|NmNYcLdqoKNT+2p|T$|1g`;ficSVIa@#Fqm>N zQ1N=_DUN^oG`0I&g<`GlI9$7F+PS&Gf4VFEN9)7F=btFq9HdL0{_f4d>OX746LNh< z%=v0GrRuT~??Nm!nzVfW+ra}7I>5kkSK?862j)8Z>~}(w!u~j`A(BZH?Q$9JLb_Q> z*B+7`R)KYgIQ+=Mz5Nh&%9pbj0LX#6I#M`B*o^yv1s{Kx&g)P_*Oor{x4c z_>eZL$0JKYAxo7KO`ll$t4toxpqck&nu@E_7kg}&wsFI-MXlhFoc?k|(_VEZEbaSk zJxp1b^u^omRR>%;-le8tTr+%CcFl%Hyce@b(|wGW2!JZ|wNzEI#EXEbba@nC)xGzv zujze9L#aItjAVeCF=bzVo^KzD4xQS59sBy}J}o;J@6Y)pmzMT@{$fA3o4B*^u(klF z064DH7<)3LNWJmW!q#zQnvZp%oOHQ_r*=`k8~u-XeWLF~bn@y6p8K{=d=pX@p8=8+ z2S)y3jr-2jle+uRxz^tT)n6}kTwskYWz9y^v-_Smh^4i3+A~SKSyi!Qb}S2MxsnKa zYNN;?6xv>Pm8@8|Nt|%r?~XqHWPwS-3%GtX5wWV3d<~peUv5ljk+D*1mBqv;dnlch)FC@X++`-S&8_?^kiCC?>ZIZ^TVytis)`dlR_aVz!)x;0#(M}#<=ImFonyAo9c zr%9mqtLpBkOcV5OFW-_Vh;n)Y11aC|2bEr?n?7ia)UrrruXEKNvgcY{Qo&qFGJQED zTXgj9WTYM33m5+mN)Z4X;bp_q^sqe>4T(%3HAnFB8;$=N?50s%_7xCDE zE-mrP$NzQWVb#%TJJ&x?B%+a$tQD%(Z@~*0p&lYq;FCM9vcy>12Vcv^f=^YFE73<5@Kq7Ig^83`*5W- zMdsn(iC2(`r10*;;^oe7y>>U1a(t$*@?ccP58ayW^z}jsIuX~|GPP-&fn+Osm|K45{2*x{o5`XFv^J)r`)_l**$`14Jp26cjl8DGx_ zdsCfP`C>zE>Tn7ptd~3j>5jh*%sCZrN^Wd*1U(xnL_@3a;9ggN{=M+cDGx?r}AkKWXMxM!O{G+x0gHF0Q#7B0aQvD*zTqKqr zoRE_{QH>iBzJ~c7)uWLw3TtttS%93FpD$jHk1rt_>QNHR_1)OS7mGP_-K=T)rT)<$ zugS17OWp;!2xydJqW-J_%IqU441KIQHv5zFYUIZF+LRr`$=dgRP5?8rOs6Pp>Cz8= zPUjs53q+#xwBFmxC>fR1=BGZ;*t6=ICiT}|eX{vVJX%ce7I87ptFR`cU*&ih*fF`uqT z_1%{l^C)U`&fzgR;S!eZ+;IvD-i$l(bI0vtmrUmg9oexR0JMfF8XW956Igk+sD)2J zRi;|)r8+I#ipLrEpYG}48yhT{es)c|ac^p(g)_-S44C9bzmTSe(<}$;5oy z;E|){c)$RxT8=H)2ZtA4{DV^*94gULDwIY|HROulA_p@3~5g> zq-Py8j*O_pM7G-&N|UGIM_Rmpn~g|OCi}YkA0c||t*m{|P~AHsuxDBQK8&k`Ah)!q zqmwx&5w8I!UcRh|W+2r_jXiHDX5Yz^iNeL&~PnlkcavS|o{;XD-+T4`*U zHgB-Q=76y(%xY-GsJPhnlHHAcg<)gU6rDX-5~$EQEhv=v`Ihnvg%>S?3P zVImUgXfN>QGvJ6jcv;=n>B53+Z74KMa&(vCHdzbX{u`5_+4O>Z`}C4nj6`u4B1+J^ z6;yYmcF-f-5q`3Y!|97y!;*^M>uzNzZZsfk+NAfp6@;JM>^DAjX%0cZfq2AT&qR=Y zC0g=5!C{*im|VH1lK$CcyfTt-B}jn?gHoCYR?yC9yS0FP`LwD<|3y*ea60e@433j4 zUr7@^Sck<+g{ZT`ej7wMC^Dd`-nXZ72Fd{ZML>PM({yUQg_P{Z*!P!=V!Y!y?-7Cb*AW(vsHYSXep>QS&-ibQ0^U+NcS> z-gr|}?a;(b!6caCj&|j1kKYeIt(pu61n&aA1O2p*6J3ODATwV}SK8r~0h9SwEiapu z0+Wb^32sNvs;l?92Xajh0VN}WiVNF>DjCsa0$Zcoh|@j#fBO2Kh4ZU*xr2KT`wN@w zq!&R3BQcN+|HWlvV=4Z_YLiSZ2wlGGQpFTqjnB*YRxtVKW%Sk-dbTs)o3>~6VKX-p zEFju3H|y&^#~^}Y%z|0-quzEZ{QwXbZVr1T4qF0>ZJ z&J8W_p~vH@XGN{G^WDP1%*DeNS(8#t`n$oJ;;d`4R_Yt`p;J~~q)$?vM8}!ml!#R^ z%Xp|s3I$^*r2Nsv9Bk#oI*u@D?}?hb{-XM|K=o3~sA{-0zp?sVMtNWnw!jTEg3q@e z>6<45}cLeH}X6? zyw@5u<o)=v+m3LQKayOc ziF~_8Pay<}F-tN+G%g+BQtb90q)vUK|GE|Bb)j-=hMZk)H*Yt z)^?7_pHbe-X?BVEdx91;7sh~-zbTpyXW0+dkJO%lphe-CEmi}L3$4z5x0XHbsM^9F z;6B6vlJ9TaR=%AGz7pxT-!Wbv3YT1qFWeg$8=+`0N)S*2Rh^a3(z3&Dj3p6PB-9} z30ks8h;i&1)?Jy*nX|ctc4R7ir)NmAlFd=TDDmw;`bEkbJbIM|uiZIjl1zlp` z6mbVbTDdv241EUzfk+6;P{L&%?^&sr?Mb2Yf*A0&pV~bq40qQ)sd!4@8!M{jNox~? za1s!Y!^vF5W|JII~666pJeyAAx5Axw-2E(4f$O<6O1FQ{8rjMUM!G;{z}P=yrBwsn`&M^|Wl z-OEzV1?RayVH)KiTwwF&&WH^K^%}W%Y&oURI4yJT1xEJF;6D_8;h>}h8z&(kIM4=Y9h6OtEOU2?5PdFsoxqoQ2`2%YFoBhL-% zt4;zF-J*tu(D6~DZ$@2UGt|kn=I2*V7(n)pvq%%T6x}kn6Z0YhTRZPviM3wvAu|+f z+P8V?K}ra#si&W+RQDrGLeR3$dBAklz4?Uti6dY~Y8JfJ7;Hg!<(HJLM%GL7fxH@T zl@#)A6t{>n1GPiFYTc^>==H&~4ETN)1XoD(7+v~Wp19(iEW7(M^)?S%K7VVv@awpL zW!v2erViQ_l%zP?a@VUez~;c{=xgT98@V5W+(u@T-e1dl-^mM<4EuG@QoBHC9R5TnoFx{Z?mR~Gp$;8eBKG0Nyuz;Frb z{`!yF!njnGR|eo1-$c4D*7rZ3kLEgPvXN={8ECqQJ_EPlo3r(MFH_p4YA(Ey_7C)b zm@1c@D6G7@V>=yEA258R!{${6B0Ps*?vHAG_?ppvaW)u<_qYXGRwd95dvVMBu~~{pf}DzOE6p=8lA~n4RL+5W>ZTJMl`&q<(Q=yk zPU#f0bdlJ=HE+1|%dENHQ?|x%pi9VNY=GWEan-iw;-=~86@*`oi&(rSzB#6dnzW7V ztMff)y#|_`JHrW&tXZ*xn_V^NtYOYdXk5F5%i?wO&8m*(vwLSY8J652*7AYqP1Atl zL$|qS`z`9^y=RDW-mH7wl|Jk;)K7OU@al@W`k%r7o^yfQi62?X#i_J9)PJT;!i5p1 zs;7m@>a(1wbK$4`@`!>m^DE?XWmd&@cKAIw#;>|ZwISsYZkpP>U%a2QH zU;a*A&|o@V9%ro^6n9rc^LjLu@9KM*CkDzF2Icp0`$gAQ8seoNzqf6pNpd)CKhngL z+uQNRb|QLD8B8MG<1U1u1Vz5DDaxH(_>BTi*LyeG;GE}y7;8B0IjkjIX0fqAR|9+A z5C0!hsvNQ1{;MJ~MSp}%M!23++D=Rqp%-n}9VEx6;8%uoTeHnp>`?rgF zcoW$qkiRecX6x@rLnfyUm9|YdluK;@bi;h4;*5gfr8mHpme7;OoIO>B(nM0W zrBfMur#b<-gb5^KwC&xr28UR7b|RSoOQ+Fg%a6BEY2*n{^yfZnaKLoeTq&GHN!AvB z8ppH@*PuW45+=+8)rHQ}k*bl+sgzJInavzO0)`dvL)I|sGf=h9nKeE0V~6iyWOlw# zuVqyyjP8`Gu93Fl50%rF*GtQsMOO_6OMuz2>KjqECQ9H36SysD%={*pXb^G?I~Rf#W*Y zf9Y%nNRrFp3nog%R8IMlF0sb3Z7l08A^7*#B9Oce&G{T4Pa?)znnW1#H=BOf1B=0G z($Jr{gylF}WcTusAFbnhe{)A%EHDnnf4^DymF1il33MbEiCbRjMHujOHDyM702F}J z)IUe~Mw90SI z$P2*6!5r`1bsKU)%+s!-wBJEvTpdgGZHpW{Ts*oR)FIt*jH9gwMY&dr1m#ePFGkkb zM2g642>Z7@R;P^i+qnZloc$97EUwJLmGYzdX40N}FY2Z(BNI7yV;67qyk@HxV%`-$ zQC~Ktpb7cM0IF(R{gar?6HdsxA&m6cQZkk7k-EkNkDeo+NtG%7xcbJ8q;OoOiN@)x zBkn`;N$@+p{N6vti!lSe*tE3Rqg7&!!3)=@}Nwo3;weXhAS&C+(K- zHx0X#3zb0ouDu_5mW^V3**+riuNcfVQs7>*T-*mD`h?raKO0i+a_aQfy*oMEcxRXT zV1@cf1JNu+(1eb&O`c?9)iG3o7bG6-UL^M&+;g^{Vj);@(u&K zeI|N}me5_oGrlStY~mlc=1%@P((UiVj>N+z8jOVzjXTn4D;26G*6JB=d=!*M0k(_y zC@$itaEBgOWbCvF`oriSNQuYKhNTN;x$S}(unmKd_j)XXTZ`lUmv7*fg^%}rb)wHc z8BdkUkk5%emjd7{)#U}IJz+iPoryEgHBIUZXaKFli$UB9Iaj)6W|5>?|LCQlFbI*} z_uajiDn7OAR9KS;F|1_f*G`|ChpE1ac>=ww+*{o3tAS{ON2W&|zq`+4NV&H#OtEvm zpWgUjhS4X5oR{=6ybzZ6EIapk(Sb~y$uI_@y6ebqPDMXHuL+sI_EWZEamSF}5$8ne zPPB;ArzoU9*xfHoK0c2hjjeNiuDC+#qJN9zf4WJdXBs!T$vrNmKxAaZ{bPsNXEJ^O zN(<0*tgQZ#(BMuu271L6;QgK!rf&ntCq;hS_ZfSCMd21jz2z~(iVO|`P0FNx*M?g) zb2RUsf>QPvmKHnP3u+w4ZAAJs+L9FcehD)JQCG)0(IOVUvGGqhw%PEcl}R0a?z!jL z80nRk@mya!=t62 z(5m5M>;ah;9h_84idEtSkYE9svDB#$F*C-}4)Lscyno;703TT_x5Eq?w5pf?&cOI7 zm8rzM(t=&9@4D#WAJ%``B*OT5$R%lq{`8v+t%0>EcxG60f~-)<5lVqR^3M$0VR_R$ z1`Pu1`XX;slJQfo!bCG*2zmVv<$EyuZYnDDzp&;vb69z#BBii`1f&FJWH8=Sf!2*D z&r_kDHYZS^?N0|gWS78+hW-HMXZmf}d$Vo|Fp5M+J^7z5+5hTe`_nyAr23!Nz;yTj zVY{vHgVjuS5QEkn_%t=bk@;yE;6-a!gY}Pp)(^hswhv8?VU-%FsH|7e1D{3@yQX3( zgGoobJkV2)kFR2Rz9Q01!G}|>2Gp>UE^natD)j9i96HQ_XBchy=h*r2*ts-G%JJrU zs&szon#p8;O($#?k1MSLR5?$nmWcJP_>YsdZ?+@6oqkk<95ucgz`(cB}ggIhmwfCOCTFS~7BWk@k3? zXZf#r^ucd1YvMQrCC6rrmMo~z z`*;J!4PWIfMf#U54@Y$*(AmZLXM_K%HP!gSNw^JpiMf_CRTgT0k1!p~PU~XmUkqgi z+Dmw*Xr=yxGl#%96MS{xk45S4zBNL3`kFz+4Pz$GzuffvjpK83z zhcg1DD%59jOPJ?;F&p?p@?c@RYT1DVSyjQ~6@#U?+3N2~F5vyDn2-N}6w5^#a7EW% zp+_ggZyNhkw~nEL|Gt*&t>-L(0$FHK#?YBS9DR83={UhOla;TKsT^<%jaI7^P?r#0 zo2Q%hs>V?z|JG?wlt9=~BaxGKn@Xq@JRlh&FF_~PZnb-U_=La6X1sPgUH_2GT-1-2 za~`g*VACp-9rV#2=Iuz1q1~W2_P(Vmlxb3(&aCC!3^QN69(p`S^MPZ>7)1&+*XNqg zvwHA6)%V0CLk1h96{i_8+T6;2!8POvwiX(b8zTSZOTRPn{($C6+dX}udp%-vC68j7 zG<|$A8+Js>yV1V5wv7R{cak(#mmM2;B3t@XN>1kJ=P^nX{>QD#xD%CTHWbHK%Ib}2 zhuAq%9i*80Yvkv-jy`j&g%<*H*;T+OW7xbU!{hqid}3aL__Y$0*_d0*&G?}H^(+BA z1STl`*4vE5$I>&GxyaG=ZE$SOrRdS>5dsXUF8XO?PP9{495EMNCyW;koc8tvFCI_b z1@Km)iDAf|t-x4qI7Osb;MtC`?(XH(3rLcB%C2x2bJcI<#AT_Ni|iY8gyuChCmoRJ z6Qm0-V0yMJ12M*Ucf@Xrzy9a2JAI&ty)Lzbsxp^A^hFpYup|Ync0lgW`;4XeBpjN& z1oeA+2$m>iC`^dMrp3I^UC@p;kH%WCl|RT}Mnu;f#c&aXprbzMQLIz?^nP zuN{m4BYx|+;A2WLFpyhk`3w>n!VOwB{A?%*E^UqYi?gU>^JzCsZhPx&Fi z-!)NVR!At4oM|!h=M!OV+T$bJXGkF0LMsBVD<|iAkM)9jL6oHQ|Q`!)=g0u%lInYHytr@hiy2z$e;cYidd68gI{_FMuARod84)zjtZMcDBC)`mAa z>!!8%6DWA*rrV;kQ}6Xuz{YddV$jZ7UP-W8S1pRjbWMpn!^Ezwsf+h{uPqfKp^~KP zu59Ek$x0GuW@KYfilA|HRipI?!80R}g)ZGiFuPh`I=f!LTge>7?}qn zlOZG`<%A z#=`Q>hFhx|D1Vu9a+Y7??D!eeN3(<*3CduGHBdHUMm>-8Olp0w(Jc zJi4r)>IX!S#@xqG1kdMvm+wPP#(hKT?{SUf1C3Yi{z88jSK9-esqIGR3s^E z^`8;Np3zXRjH5B#_Q=bk8+V{nlu=(5GM#oZpZQ&nSSO(-DDSQO!Af)y&TP3Bgh@&K zFr;-q^;<4UR-!g+EC-X>xZh!>e9PU8D8vpL1zqFupoW%i%0ceSCKHhyVo;`^9wK74 zUD(y2-6OyBZlQ<>wf5<5$-?n7aQkUWOG&}D!I5!I zQljI6JE9mNiNDH;bcS$6>z%6?N)_U#C5|MA16Vth(Razl?sO1iNuq8 z2bBLkF>+u64gW3}Y<8xLTlFZ0k&9L@8E|Bm&2RN0?)g3MPSbZ(xGr$R(_cHh7%xP->wnh{E3N917nJ|3) zP!cI7)=JRV5ZNn`WWt(al#m-lcW(MRJ;Dq%N%7j8_SeyFx>%H`_53j^L3D*=7%;)$ zk+o4WPBpAwM?1LYl20L=37ZqD9w23Am){$cJ4krT_AijlseOy~ zj}5ssJmbV4zaI2ot)}%F)A1`~fL2!}3QTJEgeZvEJI~DO3<`-VpMj@(EHMmVJ6x1{ zX#0V5iW%LNvIzdi;D(R5xFY*Ik>BJ3c^e7hjD%3gl$UroTFDopPVf@z%ACQ9W$5 z6ae37lg@KY6Z$pfv`9Li>ZS zQ`=D38zm;zs|(CuY4oZ-VYFjdn?0alI>3vf$&B+fQ0r>^Zj00LEJRLXpADoLf0B}y zowN*hD1Eh<&c)}E5y2Ob3+9jaq1sVo>tVA|qRCNO3T;7+5Z#qe*e0{sx!X%;5O1mU zZsG>_uENt`J~#DOg*({g<<6|@%3yD^E@neJ5DzHVis)JY1%AjOHd?i%a=5UkV8Axu z_vJB(vl&m;=8jT)|!u4nEh zI_uz)`K5~|R6`o}cf;dZ4t+S(ZuHw&_6)4WnF?mM>pfiE{CWScZo#yVn})#@=^6*u zb7D{D^Wjo(F_1SyPARDgtvdZK{7Ec9{q8*on^zLdDnap@l#HF1L>uYwC|;O_I+~pg z;|D9}>4G{fi7V^z5D1pdV~qJ8Rj?tFlNdA%)vju^Ht#(S=}PP%@Ct&d&{S3DIPVqj(q-uyLXh!4~)=+C2ehiXxptNUJg4*f@+ zG0f}-b8q9D6sM%{UHJZz(<8c8!x#A>#`yS&`IBh{AlJS&$3SVVIM zqqV;z&>*~3%)0WR>h;;0yH8Hc9!(E!9s>Fgvi88S+mC6SPf(k|sk?kJB2zl0@^W!FKf) zQJq$SwcS4YFhV@+!v9mv=xSU#Zr*WRR6xo>E!&bXU^RNvW|*`5Fl zl;~_yP-Nlp_Hn;y5#fFb{^mkwhf#1Qhc8L^{)qeM@>ze$)pv%KLBqw9FW&9yt+}pH z-0JJhQFWo&1D(nu&XBsX-axwpt}%y+o1@<=b75S3UP)+1Zv}e}i;NFsg0#3Z&O&(x zs)??HR`f^WyYFDCpf4`WIbR2@s?tnztu2x|VY}O@{&2FKzCu-DcmeaghuenCdde1a$FpUt;S)ex+`fJKiQTP8N@$*go9v~Xj5OHYcWa)Ys166Z^s zRw}y%H3Qya|Kegwr(HvTN$iVpW4x^2{^w~mGmSW-341=hs0I9%hO$Tt+!f@Zam{*H5&9m2bY73!pofmd6o-2 zz<24@7MS~6+yOcU3H^Se`}#Om)>i1<)119sa}1a5V>j9ghQU%V*RtQ;!UajG=G9lh zB)!NPFCXvIYjp-w5QIZg%Hi_`uYVhy*gt78NxrmZi`4}B8r`kTPf*HMBzd*@8N4;T zLW&PuoIKviZR8{gV+bi;uKep;tw?SU;W1fYAEse2^J(z%IU)&bOJLs6s-UD0w_HJ+VKy6n6`HJC}R>fGE0U>Il;)Ie27WETCo@W zJ_+aBR~ThJ<#A3Ez@RlKU5$q(^g}matMNWUz^;9fBU?s{;fKF3XyZy%3c(bJGS~Zn zL9)vkNUh&MuvJePA?bWDgwW({vqLZZ2zWkCr4N=Z0agz>mxkBY6URp>nM- zo@W9Pt$z8#oMX6NjMjcHcc){h| z{si)SGaE5~8`-sbQHkdA{Wze5>sXJ`1d8Q3ErkkpB(yj+qQ(Sj&+&kpLkGL$^wzo| z#u5$=U*dST30@F*c;L{yNO`UFi`Fpg=yeX}I+ELOwLK+fTtXRkG)O$iSDUPO=6?#I zjh!+eqMgeg4bDbhthRUPF%VeEYKtDF1#Pr0_7kPm8j~hqRTyTDNZm5Arg_e_M7D}J z_`g~*+v*0KI{rN~xxNh2eCw=zNPgR(@FBVZrEI@3k1iQZ8A=O?Kh`sGnBS^rBEgc zz|2NIvM4jMF2g66>^;;wJc0*@w5r^9K|Q;E)o1QvC149hi#=zL-1GEkp~+I2dIP;y zMRF+Jbl?Y0SRv}9E?PE`Wdm+o$Cgjg`(hY~6wV)l)SCYRJqZ`jl5wr>Tf+sZS>@W_ zaf3Ze9op$(*Q+gy?Uwx6HsVpH`+!oScyidRjs1T|6_gJILwOw71l=788R*#G{(+>> za_HHt;ICKbgjAEJtflXY&6AEN2RpHOIwDG!QNMgbF+9;9B>9|tndyby3}*LVsF-g9 z{!?0mmAv=AifsJfk9dYYr2mSE@26p+D_s*&hyD90zo4($lPw`6qmNrZTY7qWG;bL7 z+P{WQ{i`KRvVLpKHm;dg)FgrL9vzLibq3Y#Mv9T`kEXz6=1G641Vzw~8kN&9FC-Fw z{Y?C9&EMn*&A`OO)ZX6iI4&Ic!I;4bke^lXmjv#4prsh$!5+!+}C|aey}jB(Z!v4IlgV3Hl36U zGuuGWMf%!vn=`N=o?@Ie7OUo64Pf2Q=u2Bui6}=Ze#>Og92NAg(C0`Q=KuFrI)i$B zj~r-+1PXc2(_B>|XPBodC-HXTa$0#Ym2GNeGkDyYbnn};Pc9i%fBnwo#h+mZ+3MSv zT0L(VSfMo(X~+OO!vk7w$aBtWin{~$W!|WV)I1N;)YG00xtt;;3Cap+_9YXED@kW? zK5Fpeez$xAG=fv3lShV5hSX{!_Jm^^`OIV@AjM4(qGpT|Iu!UQhcOsjctlW&jZ8oo zN%p(lokcV=NtoGFlaHq-5BRM4SvwiUmT0@iQmbXt5+WHX!7COxhLxK-Z){-yl9Bz+ z-qtRfvZ$BeuKoVIC*7hK04Zwr_mDtlobiI0#s>SsM)p;DzY*{4(rInGT2m;lKJoU3 zn30yyKDbbgY@_&mzY6E|3P&j_ub$vV;KHqQgcn#)-4?8%^prK}HAW^h%>Bpryo;!i z67q06N2;CwV(2B@q|ospTV*J+FWtDcQUo44`cR})YkLH(m|t!p9UpwNc2W?F@^Ft7P$6VVlN^`MQ=e&m8H5w~aHVp9daP~kgEyF8IC=-j>-|Ru0FQp&3*De_?)G#wMGd0jKI?T+> z%;1KpVP=lQ4ClV^-AUhjC;5-HWLcJXcXsBP-!s!m3qUqbNvpJVPKfpLpP-evf5PGf zx6wL}{uRM{EA_(Xi4MtG9Ea_xAKbKnQ9*O*v#9np-ZKAtm;?&grlzEdaG}QO(wge->rU)vTh9hx{f!=_ ztavEATqqM)N3R&0NUJA!KKE0}#r=^+yp7*+B^cWAF3=BIlB?b~xU+Rg6B2L1MK_Wg zbXIV=k`Hx_9m1icx_s9MrK%=wIPsiQ2f9HE#yy6fK$6NQAAo+s%2Yc1`#}cl%dcY z8y}j41aXJbjvPQNr0;HI7O1Ked!v=H}!1y{P~dV(~*kJblz>dKEF#@b(BYy=4owjG@|no<)) z_r-Ecu%x6&4kV$ItyD|(^6>7)_b*j#2!uTdw0~=D?fh=Z2$Z>I5(2d$!M!(@C2e9M z{YU;CIuYgt!{T_*D>X}bN!!b6X_KPC?#I&cJZ@Njt}R{_({E%mn%EZoXnbjYHdQh* zg}0O#Zu`aAXU0$V7b|DTkESmr-GqH-9_kT|L}Mul&j}o6sHj|@otLYl%6$`vB|fCe zS$U@J1@!QHPmi@d{QV(2m41mFl3$H8aTTo{To^k=%{TGnWw$JOY9m<@{!t^V2BQ9K zW8jN1h~^&|fg9sf5&HZ2ZudTC@y14?WMSW{X0o$yWNsNGqed5bbuW(Q7*afFz4!~R zNwtOx_;GEYIdzyRH0pB&zepr-w05GGfF}lWW*-hH)Zq2xO8+jGzT@jd`~amroqvQR zuj2+`B`{gFb>keT)(T~t(~b@9o@2+BE_=b+sCr$wwcRl2dSBUF3i&KipMLb`T>)1K zz@p>v4qjn~IZt++tPvg!o_%K*g3^rT zPVb?u<(wr0ef0?V4|Z4Oqxz;~vs5`r#UIocw|gNZcmaFJUojt2b^p_h@jZaG=?m)0 z13wm{p5zkHmX>G<5PoAm(@=TS$qUwVa5#g?CfML}1OPP;o1QivNr}*Q>u?GR;2zz} zTrwfUi=cg|uNa$Bhq<}PJu{WS{YQas3&h`K4jTYb)h#}a0(DxI>le_uA8Z*6oGbj0kUIeR7@)8cC>I*MwwqO12Ym2mUt3N%%J z&qiKvkHXiH_KU0!VDtZx1C097gtoC3?zFLO?po2eMQg9@a)SeF-w}`$=*x^Tl9gT{ zT$BQ~W{aPXxjy9EFo;Gz!^T_)$?@eztW3P~>z3+?z$ldY$kMMTiIJB(KPP;u9Cv)k zm(x$C+6P^&E~ZFI{N}o%ppT_!sPxk0@w%wV!TA>;qW)dpEv2rqXZ1{{U1XM@2_BKq zW%XIDTro&U!D7kbPRsIrFjZ-$6Kr*l^!?SacGWj&G&6pjNs!SSGeS|L8WX4up*1ei z1`=Ugc_}n7gc0Jd%=WDHiq1D;zSuX?=xh@s=;D&9*{%9J@K^cmlNpk(9M>%@W3E9* zZG!FB>It_3WrS$;nLnn`wusM-iqYK zZ!>YCE|ddreDX6s(4e@uYqKl zCzN{xGNTK&FekVPw7w|MUd?Ks8|##HGM%e?WlnCM@+I7=Z3sDe!Bl(1+(J zMsl)-j8VJQSv_Ip6CutUJlp{JX=8ZAAf*&$$oO_*DEos}2*I&DfGF>8R#&n-?|qr{ z(f~F&mhwUr3%l#7_cV;1Lg%^TPvs|)=q45b%UK1#{$@~Nw zELnsjRv1ze%d?~&3xt#P(W3RqEySr(IlK~1zOZV%57hZgikT!mT2b1IB-ZJ4cGL-> z%zGlwLT3KDnIq)Mb$z)5W^H`|Pjk!Jas0Jfbw*<|mK%8p6dX&T2u+9ed4~gKSWHYf z;}RYAwT@~yLqH!n4dKH8agcWJ`1)j+rTq=>>2tCK{jr1lqN1NDRHYN%$5SWq4f$a0 zS1^S3AJzFs$iM1*ePc)O&u))ih{OQa9+BLJ1&7&s9u5J4tgLL5htQ5QnINNT6k}2l zg%r;E7eU|e;QQq;Q|ryYq5zF>FGu-1DW$XE$ED)&@v&ysj@YzUcNiS=A=P9k2|P33 zOw=QR_fqB~i$hUKi7^M>)%7(Q6&2N@Q~!-lq@i zS_U8gRYe9rmb{pxS}g?~o!Jef*&SaN2r2%y9_fZf$58T8;s#~J_vU}fm82LhCO3_U zz4oLYW5Yeb_BQbpQ>x` z9`hJF^W$c&(VM%Kk#xHy>~@cC<$WCgXmXaEUt`_~`WtNJ%V_B0vAA8F{)l=0bxw6C zNcgW<_u~t4pXoD*YV!sRFv~w!VgJ1VP z{4H>OYBCFK8=d|`Ku|_}rGxMP0@mWqzr{DIz+dm3bzb;1 zFW#ptB$*(2jlk2GXpQ6OL?5|UO9Fz`3i{^fVKUaGm9)?-4>+WgX~JuIm|=tHH$0pe z4-njV85g;9(|dqxd9IgApX!k!MI+72v>FNZvvci%*UKQs)=t)BW;k^q$*QC@hartx zq}y&Fi#*}TT0*kGzDuHaBrUlq0X{9JG*V)d!wa{^4Sc)Q#vpFicBKv-l1T(8~jz?5U?fZ3tXGIiqOOH8h)B3umq zq;ERi-NCL~b+Erc+;|s;+kh+7_(?TtfkER$r{$*Cq%`V#unH;^^#R{ zJ$XX2#vf%81|sdoc@~Pt{UA85f@ub{GP#@7T|d_gXr-qyT* zc}7`wd>%2;oP2H}JHE6QjQ{GbLjFx^U+a;fxH@uc+Zd>;PdITKQ1f$qDDZeiulsEW zKZZi!?ZB{21x_e)Isva+t8mZRCR^}#k~v%3q6J4!lufW=QrCxw-NUj#CEQKTKC!>E zQz>NG&5VFh^F;CP-?z3H&3G4_j&T#)j0~T@FV^zB^{e%Q=?Ys7-L6-Prbdw)KyKwj zXFAoSL!W=qf*D;@SO8sI8d|H>PjL4*^1wuWy9_xuRbT2A3KDGOPi2$Uu*HH5f}B3$ zz+l=D9U8^FqCbnZNA({+kgb-`JKXs#qtoVs+rjt%GrqH&m?wC;Q&jKhdm{_$elB}h zZO`ub<_SPI4wFl+J1(Cs(v^#nMuY1l>6>FFPrA z_}8m#e>SR)v=^%=aauQXqE`ur`zPtbe1OI?5CW6Bre!u}ial zT>pT`yA?+8{r4Tk5H-&OxDD-YL@@K!uH)9%7s9rPaQx}cZ9!-~|Ka+)5$3Gh)J;AN zoP(u81isDF{NJ<{g4cNFo=wR_O%Wv<3WD`9$^zm(ty(;*nI3J;YG&<`?WnUHnRo+X zS;cr5WblGJt*XN7uBXR4iew}^iO_dtUNv)uf)hT`*#A*F5hxaC$*2U=dox*%>nxd* z=PT2GiflD8c#<$lBJsjzb2ZKqeC{sYT5=y>vEN_QA(m=(Y*%Bc0#=Vm%chTBg9`&g9cqJ) zo*hfo_@;!o)^gsv^NR&P*NB!v657aqBAjVrBaT(~P{l>bl8@MT+sM6^r1-r~@7$lH zP^oh)pYF9$e+Z?Ply=cPh&xN`@rRX8M_QXPF(`cQ`Q4DGU zK4$b^us);{#mJhjf2zeW58Ll<7|*SD`U1eo)5Id4@zb5Hd{OyN=Op3*=Gv>KVw~6u z5qW-z$0&IDjl_7#-J?GA!X8~@`DeDAgF~80MV5yxkUv z+MJasUSH~nHX0okbK(wN^$_=t5C@ZI*`2+T59}>Wt@thaG2EsWRDIPE!6^cbw3BDy zG)~H(X7q@Ibo0)ZSW0DN=kplCaE^BYPs|PYAHcRA3?&$_<>36EYDvO78zvL?Q(u2l z0)%Mzr;*U4Qz#oXI%iFIXlx$aIVA09DI;}u;!i!2ySuwU7ubl|86H{@n9&#g$p28g zY8n^`m#bEBb$k1xY7UTQR^sE(q_cW^|1boRTCKO`74xZ$VhFbpVy|D%qXJV>n(V5N z%}NGM>NwY;^Xh)II^ZW{#ZT%#o&gId>obCEYr%5rN!%weq|3%UogZx8u|Eukz9pF< z(MW;__J#Fl+#kv0OEL|&w}fs_H&Y(`x`v`B5{~&F$+?Dv+C*K&CB21oxy<~s?XLvNEE`xY47aN(9|`r-brAQb0(>5 zwKs^@P@uN3oa4^5-JPjeKXSkvmolj&a01^9Lki9+u4a>hw3UFA4#xEWNj^JfQmfmf zs2;~&pZY5P{75Bt%JH3&GWuw@q#jb!t1cq3N$1ea?814=VkIS{WdW5)y|$+bFi>(c zv!fsWXWW}f@SosQHn!x-ZSMm(`>fO92W(SF`Nr3+6Y@g+V;T*5dx*S$fjzelj9XIg zK6$-x%KmclW^V9n7daZ6xb*7x;$mafRAOmwGp;k=DpnXiGTS}4i zq<;m7I-W*mYybB{kf})*ww`$0SvUpNekrO<&yS_Q4>&E45LbZYTsJ67YzlMR2D4o) z585r7mGEQXkgFT+^|EqBhgQ{G$Dn=%0f~Rf4>2><*={q&SDTSRu`im99<0Vz*kp|$ zcOT)C5kpRDiC5C(H@_S$q>5Z90z7}bsPw+SX1Ku$L?eqYpgnvpilLCVGcB%l8kvNZ zVV~qbIV&zH9cJY3*$SXg3R=rTlcSk7+BK~k+5tbFs1dPHrh(CcN9xmvxz`}iaZk!{ z%3bP)H~GF0Nig^k*gj#fA55q7VkW@;^{`u$Y@k9YTD01EN2D(hw3&Aq?ulr*n7$yx zar@p%OuFMw2yU>l?DTY*e{*9LJ1=j0sj+x_Wu>41+y-0M?Ko@|GX~FFeXp5!rXRs1 zR)C_DBAvg8@Rmg3!nR)%bm?59`IY+(>T{Vm8=IU?k26e#lz6<8dt8b<;hsiBFnKAZ z{X<1i%_OeCsQX8-=S@$c{sh^PF*%Q~z6h;S=t0w(A_e1beM=FTszA;YNK{_F5SbQv zXPVJ~-AlTDdGp*OnVIfYdK`#~I?|fOO9!UXIU@&d0|)M0Whe{U%~q-G$$MTf+u~8w7`x_t9~~lkSpSyB*|VKAG*4agQM-a~0Uf9sJ)cfS>bYXOesMF_Rae zS_S6+iSn?9?$%GmS0W!wlrXzkICBY#ad^D z>0&$&K#8r*=@_|u(4#o|bZYwIu1u|rri;G?rivLiRWccbmVpy~_JFV(tz(swHhD{*J}o^92*z#bf$L!x`VQzSK}(d@!2^P)Cr5o^2|a=zDP z5;TemywFEZpa{_J;%Yw>YH{pY4`=Nz#3kwZOMnuz7Gw;sd6KY|)yUNpV1RB<0>^ufE6yoE}R;B&6 zm2pRTk@QmIgF#k6T166-YKigx>(qTTaYvJ@RY>TZ=93@rf`RxD61jBB!FvqRV(MAwK zujIjFBszvEwu#o( zv-n(J7G(>Cp*AZeJnab&GJwlb2d3|`eg${tNeZYL z^Ex^L87V^sPD|>}8ez0Y3?KzX%J{t*vz#gm{!0M!T3;Mr_-eS+$?0K@InE0Fob$x^ zqI~@o2JZ$pSyodL6tJGwXj5PJ1v-e+AvAY{YOs+Gvn z2B@nINB?Y1g;|i<4aSp1=HA(pW`j3Y@eqU68yYZ_Hp$Y)1Yc!-{G*DD+ z+CnAu%!?rc3#crv`p@VJy`2Nr^loGwf0juiGzHFmzHByjJz_m!Z*>9&CCuM*d4-``UdW$pk#Z4CAt!orLu#JaqT-a)>dnW8>Z-rjG$nCZi}t+TrJ-4 zIqvhs>-I0dBkJE8`Kg&Q6bGkKKD59x8*A$z4p<#~{fg$>oUQz+nrcN=isH{y;P6(g zS_vuBGpw67LIhf@O}Bz7sAFk@DshiTuYz*>l(PP&Cy8OqBvo$d{2rUl;uB3o;JO|4 zfQ?AYpG8B)^a2o<_wJ8#eesJqS2&QxMib_8vORD!(GT=S;4=|KHbB*Gq}w*v>RP-X+knqL_C7 za=jgb{Uh{@-y~K)cW8s@1cnx-n}CMC%rYwd=S_mi%$7iB+m?5rFk5r>4bB`L;?_%= z?&ncW`AsZYjk-oI#^~N+@`)5Ht{u` zn=k(?M-#15`Il<3&3vD241ps4M~~H?WjFTXiSvDp9%T%C-{M~_FP%(XK2m}JwyCqS zg&DTF>^~dbys4tjgY9F)z-?LbPivKHMGXH3z&s-3V$C zpV(CiC-s?aqELH)MZ~>AxIU@7JzWmcBgb;=o;3*J@V+j+;Zvy5$Q{wl z1+d;M3Zy=8zFB(+xO!9NEOhqVn{(N$rQWS{`cmKUc^>j%4QOxHy3XU2S6ursMHG@> zW>KE4Jz?6eH%tZmgWb0M!~YQ_>zs*xwgO9C22gb{9aslO%`_gkoEm3ZN&s68o9;q- zKMS2V8r@>5c`Nr^9Sg$0R^c~}uuMO!rDW;^-Mn!r(`QRlA+PENH7GhyI0_=}YZePj zt8Gn0rigCO7SfQ3Z764{@{nvL2m0HXV`fK031s3OJXDGdF~OElIpHKMMUqPf- z@j4oXn(A6lg70MXr?2prk|azUPfd(xpzz}j-AW#7)QtNJ+1@;mrY|FCv3)v2)RSB& zY970OK+Vj65f7WMznrr~vObWGJqEpEfJg6^63>fQm_v0pyAzdoc-Ht>^VHIcvuONj zok{;1$ru0Gbo;sb(z51~KMn8|so7<-x)bM{1uL5=TokUQ$saf6yY7PcsIRr<={#(<{-F{ zwYP_esFz1W=We$awRchFeAK9=q>SF`bA@BR+r{fX_IoeU#baEoT(;pkQHgeKg*_Gt z`;5ouWV*BX{o@peQ9Z_)R1C7|lY+Z#wWrn%tDkTdBu=SP2^Swpfr{wKBW>w8_R@#B zS4@$d4xjgzNEy!+*Rsf~cu_FyuNvYvRS()Ms#d4<8h?p5l}TyHHO58g;R_%(yU(Kw z2U9;`3jTZwE9Cp*xAsG+izdxgk6Epd1Sx@7RTY5~8X4~Gi@6R}hG14aZYrdiLh=h> z9q^D+0NE(McJ3b5P7rz5XF^*_yKZI8<95pjQI-_SX1|}(=r3EULP5x>ZymM2NSO)7 z&L>+(W4EzsZnTu6e*I6hkb8_RG@{06vfSnjr|Ct*7w!)BJKzv>kffEvrq4FsydF1e_B7U-b> z1VaX1m>V7>q16yoER#Eg=k@a>wGb9+!U-8Nd!rMzq zG*XCEI$Ob=(dTK`K^tTb_nm~krs*7k@uB*4+}TFv4A`ZMFDhMaz z_?ZG(>kPW}N}5qEH3^S@++66U4$b@W+0R4bYCPk58-epm zmk^0sjZj@^lsN~$C)yu;U+Sn22KJb z8`M{nTP5N5M}I4IsX%$7N*8OP6qjC#SP6m`Dclq(O4kTk43L&s$%^ouSt-_ zO3zfk>R6hvGomkjp~F$ifF`i0DckV;$j;?NC?u(!NcLQph;Wh$XnX2UJ$m2ja1;|- z0aKw#W^jbo+`Wh+T8s%E0{KQh_SF0m=295C|uJ-I6P``UXbBV-wp*039ZS^GK&~MonOKOcK-|t>jTX?V>Bo!{T5~0WDB?|N9U;icB+be28 zJFzd03x#sWPJ?!)JK$i%OZn018#z~}rpqugQ%FrsZW#sraxx9}XC~RKDa?=vJNc?m zFmmEA_RD;dn30$LxRQft#DfX#hK$C?FnfrRI2FtO)@;beAKRNA|Kn_=ib1mTD|zAJY0c_;1(nn+zu&;i-zxVnoq09k5}c|so!6Af zKCfUiUg|n#Aspr=cNzK(T<8`2PdSWsr`W-Iq6ATSvv+S6RW~FlR67L>SMk0pKfe$f zzdu?Mv?|xKmVqgst?God8Sx4G24KT&Y6F(Eao6ZIGkB2N&X?pnH5RW{XWI=Fc|j@^ zq0u~qy{41uOl=J1-hE$>^K+&nu4qH=Ks=r7?*BM%Y2fta7bVndpeV%QkOY{pra6hE zCA|p$eoC4|LH>zU%!OjT(U6O_^=OJzGtwYZfpYu6PZoyHO> z<^3AAztVd1@ZvUjL&)xi7jt#$n#qG`UpLfAK)vT@Fu}!7w951F>0eXJ8 zo6mQbVtsgyaRbG#^eRmU#;-VjF)46rxms;!$Ip9R<6aLWNJ1JSbDPhXL#W}6)1Me{ z4R$I%TN8UjiHKimsa)L+`;RM~2&VqS!?;q1($#>EW=l3P0`M4VLX0?n*CGdQ)X@y% z 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 N6qEZ_E~4-MKLAd;$AJI< literal 0 HcmV?d00001 diff --git a/screenshot_gallery/user_info.png b/screenshot_gallery/user_info.png new file mode 100644 index 0000000000000000000000000000000000000000..7623b0e06bc5e781049c352aeabdcadc864486af GIT binary patch literal 65570 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

Qf%t}p0v1?V>KPAJ*YA!`oZX9#g75(o6; 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 @^Z^ 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 f#{Z}PF!A=GE=*k*6k$jS1ssy1aw3)Ab$ Gobattle.io server", value: "> [Link](https://discord.gg/gobattle-io-official-380588354934276097)", inline: true}, + {name: "> Official Gobattle.io Guild", value: "> [Link](https://discord.gg/gobattle-io-official-380588354934276097)", inline: true}, {name: "> Application Owner", value: `> ${client.application.owner}`, inline: true}, - {name: "> Approximate guild count", value: `> ${typeof approximate_guild_count == "number" ? format_score_with_commas(approximate_guild_count) : approximate_guild_count}`, inline: true} + {name: "> Approximate Guild count", value: `> ${typeof approximate_guild_count == "number" ? format_score_with_commas(approximate_guild_count) : approximate_guild_count}`, inline: true} ); embed.addFields( - {name: "> Terms of service", value: `> [Link](${tos})`, inline: true}, - {name: "> Privacy policy", value: `> [Link](${pp})`, inline: true}, + {name: "> Terms of Service", value: `> [Link](${tos})`, inline: true}, + {name: "> Privacy Policy", value: `> [Link](${pp})`, inline: true}, {name: "> Project initiator", value: `> <@${project_initiator_user_id}>`, inline: true} ); From 3e0b0be9f277005462dfcf5f2f502bf45751c3bd Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Wed, 7 Aug 2024 01:59:54 +0200 Subject: [PATCH 41/54] add screenshot gallery in readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index b168eb7..14bf337 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,12 @@ Brutal method to stop all containers: ```bash sudo docker-compose down ``` + +## Screenshot Gallery +

+ + + + + +

From c4c65da057935c0835ef54f017f2a5b51b16ead0 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Wed, 7 Aug 2024 23:13:24 +0200 Subject: [PATCH 42/54] ranking emoji correction --- src/commands/ranking.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/ranking.js b/src/commands/ranking.js index 79d1618..2a4b41d 100644 --- a/src/commands/ranking.js +++ b/src/commands/ranking.js @@ -176,8 +176,8 @@ async function get_general(interaction, client, type){ const max_items_by_pages = 7; const pages = new Array(Math.ceil(data.length / max_items_by_pages)); const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; - const kills_emoji = application_emoji_cache.get("compass_item97") || "šŸ—”ļø"; - const deaths_emoji = application_emoji_cache.get("compass_item96") || "šŸ’€"; + const kills_emoji = application_emoji_cache.get("compass_item96") || "šŸ—”ļø"; + const deaths_emoji = application_emoji_cache.get("compass_item97") || "šŸ’€"; const coins_emoji = application_emoji_cache.get("coin") || "šŸŖ™"; const experience_emoji = application_emoji_cache.get("fuego") || "šŸ”„"; const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ”°"; From df9622aaf46988e19170f4ab5a5bcbd0c0cd857b Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Thu, 8 Aug 2024 18:31:21 +0200 Subject: [PATCH 43/54] add the skill improvement buttons on the user profile. + fixed a bug with the logout command. --- src/commands/user.js | 368 +++++++++++++++++++++++----------- src/database/database.js | 28 +-- src/images/mr_strong_head.png | Bin 624 -> 0 bytes src/index.js | 2 +- src/utils.js | 2 +- 5 files changed, 265 insertions(+), 135 deletions(-) delete mode 100644 src/images/mr_strong_head.png diff --git a/src/commands/user.js b/src/commands/user.js index 5ac7803..adf4573 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -1,4 +1,4 @@ -const {EmbedBuilder, SlashCommandBuilder, ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle} = require("discord.js"); +const {EmbedBuilder, SlashCommandBuilder, ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ButtonBuilder, ButtonStyle, ComponentType} = require("discord.js"); const {is_my_developer, restrict_text, format_score, format_score_with_commas, get_level, get_level_to_emojis, send_embed_layout, application_emoji_cache} = require("../utils.js"); const database = require("../database/database.js"); const head_list = require("../head_list.json"); @@ -400,9 +400,9 @@ async function get_login(interaction, _client){ } async function get_logout(interaction, _client){ - const succes = database.remove_gobattle_accesse_by_discord_user(interaction.user); + const success = database.remove_gobattle_access_by_discord_user(interaction.user); - if (succes){ + if (success){ await interaction.reply({content: "Your session has ended and has been successfully deleted. You can reconnect at any time with `/user login`.", ephemeral: true}); }else{ await interaction.reply({content: "You do not have a registered session.", ephemeral: true}); @@ -450,124 +450,254 @@ async function get_info(interaction, client){ const response = await fetch(`https://gobattle.io/api.php/bootstrap/${server_version}/${gobattle_token}?platform=${platform}&ud=`); const data = await response.json(); - if (data?.error == "Invalid token"){ - database.remove_gobattle_accesse_by_gobattle_user_id(user_id); - await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); - return; - } - if (!response.ok){ + if (data?.error == "Invalid token"){ + database.remove_gobattle_access_by_gobattle_user_id(user_id); + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + await interaction.editReply(`Unable to retrieve user data. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); return; } - const embed = new EmbedBuilder(); + function get_embed_user(data_user){ + const embed = new EmbedBuilder(); - const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; - const streamer_emoji = application_emoji_cache.get("compass_item122") || "šŸ”“"; - const head_data = heads_map.get(data.user.skin_head); - const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; + const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; + const streamer_emoji = application_emoji_cache.get("compass_item122") || "šŸ”“"; + const head_data = heads_map.get(data_user.skin_head); + const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; - embed.setTitle(`${head_emoji} ${data.user.streamer ? `${streamer_emoji} ` : ""}**${restrict_text(data.user.nick, 60)}**#${data.user.id}`); - if (typeof head_emoji != "string"){ - embed.setThumbnail(head_emoji.imageURL()); + embed.setTitle(`${head_emoji} ${data_user.streamer ? `${streamer_emoji} ` : ""}**${restrict_text(data_user.nick, 60)}**#${data_user.id}`); + if (typeof head_emoji != "string"){ + embed.setThumbnail(head_emoji.imageURL()); + } + + const level = get_level(data_user.experience); + embed.setDescription(`${get_level_to_emojis(Math.min(level, 2000))}\n${restrict_text("_This user has no description..._", 250)}`); + embed.setColor(0x500000); + + const id_emoji = application_emoji_cache.get("item_348") || "šŸ·ļø"; + const coins_emoji = application_emoji_cache.get("coin") || "šŸŖ™"; + const diamonds_emoji = "šŸ’Ž"; + const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ’Ŗ"; + const experience_emoji = application_emoji_cache.get("fuego") || "šŸ”„"; + const role_emoji = application_emoji_cache.get("item_36") || "šŸŽ–"; + + embed.addFields( + {name: `> ${id_emoji} __Identifier__`, value: `> ${data_user.id}`, inline: true}, + {name: `> ${coins_emoji} __Coins__`, value: `> ${format_score_with_commas(data_user.coins)}`, inline: true}, + {name: `> ${diamonds_emoji} __Diamonds__`, value: `> ***${format_score_with_commas(data_user.diamonds)}***`, inline: true} + ); + + embed.addFields( + {name: `> ${level_emoji} __Level__`, value: `> ${format_score_with_commas(level)}`, inline: true}, + {name: `> ${experience_emoji} __Experience__`, value: `> ${format_score_with_commas(data_user.experience)}`, inline: true}, + {name: `> ${role_emoji} __Role__`, value: `> ${data_user.streamer ? `${streamer_emoji} Streamer` : "Player"}`, inline: true} + ); + + const equipped_attack = data_user.attack - data_user.base_attack; + const equipped_defense = data_user.defense - data_user.base_defense; + const equipped_luck = data_user.luck - data_user.base_luck; + const equipped_hp = data_user.hp - data_user.base_hp; + const equipped_regeneration = data_user.regeneration - data_user.base_regeneration; + const equipped_speed = data_user.speed - data_user.base_speed; + + const formatted_attack = data_user.attack > 0 ? "+" + data_user.attack : data_user.attack; + const formatted_defense = data_user.defense > 0 ? "+" + data_user.defense : data_user.defense; + const formatted_luck = data_user.luck > 0 ? "+" + data_user.luck : data_user.luck; + const formatted_hp = data_user.hp > 0 ? "+" + data_user.hp : data_user.hp; + const formatted_regeneration = data_user.regeneration > 0 ? "+" + data_user.regeneration : data_user.regeneration; + const formatted_speed = data_user.speed > 0 ? "+" + data_user.speed : data_user.speed; + + const formatted_base_attack = data_user.base_attack > 0 ? "+" + data_user.base_attack : data_user.base_attack; + const formatted_base_defense = data_user.base_defense > 0 ? "+" + data_user.base_defense : data_user.base_defense; + const formatted_base_luck = data_user.base_luck > 0 ? "+" + data_user.base_luck : data_user.base_luck; + const formatted_base_hp = data_user.base_hp > 0 ? "+" + data_user.base_hp : data_user.base_hp; + const formatted_base_regeneration = data_user.base_regeneration > 0 ? "+" + data_user.base_regeneration : data_user.base_regeneration; + const formatted_base_speed = data_user.base_speed > 0 ? "+" + data_user.base_speed : data_user.base_speed; + + const formatted_equipped_attack = equipped_attack > 0 ? "+" + equipped_attack : equipped_attack; + const formatted_equipped_defense = equipped_defense > 0 ? "+" + equipped_defense : equipped_defense; + const formatted_equipped_luck = equipped_luck > 0 ? "+" + equipped_luck : equipped_luck; + const formatted_equipped_hp = equipped_hp > 0 ? "+" + equipped_hp : equipped_hp; + const formatted_equipped_regeneration = equipped_regeneration > 0 ? "+" + equipped_regeneration : equipped_regeneration; + const formatted_equipped_speed = equipped_speed > 0 ? "+" + equipped_speed : equipped_speed; + + const attack_emoji = application_emoji_cache.get("compass_item95") || "āš”ļø"; + const defense_emoji = application_emoji_cache.get("item_119") || "šŸ›”ļø"; + const luck_emoji = application_emoji_cache.get("item_478") || "šŸ€"; + const hp_emoji = application_emoji_cache.get("heart") || "ā¤ļø"; + const regeneration_emoji = application_emoji_cache.get("item_622") || "ā¤ļøā€šŸ©¹"; + const speed_emoji = application_emoji_cache.get("item_122") || "⚔"; + + embed.addFields( + {name: `> ${attack_emoji} __ATT__`, value: `> **${formatted_attack}** _(Base: ${formatted_base_attack}, Equipped: ${formatted_equipped_attack})_`, inline: true}, + {name: `> ${defense_emoji} __DEF__`, value: `> **${formatted_defense}** _(Base: ${formatted_base_defense}, Equipped: ${formatted_equipped_defense})_`, inline: true}, + {name: `> ${luck_emoji} __LCK__`, value: `> **${formatted_luck}** _(Base: ${formatted_base_luck}, Equipped: ${formatted_equipped_luck})_`, inline: true} + ); + + embed.addFields( + {name: `> ${hp_emoji} __MHP__`, value: `> **${formatted_hp}** _(Base: ${formatted_base_hp}, Equipped: ${formatted_equipped_hp})_`, inline: true}, + {name: `> ${regeneration_emoji} __RGN__`, value: `> **${formatted_regeneration}** _(Base: ${formatted_base_regeneration}, Equipped: ${formatted_equipped_regeneration})_`, inline: true}, + {name: `> ${speed_emoji} __SPD__`, value: `> **${formatted_speed}** _(Base: ${formatted_base_speed}, Equipped: ${formatted_equipped_speed})_`, inline: true} + ); + + /* + embed.addFields( + {name: "> šŸ’Æ __ADV Score__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> šŸ’Ŗ __ADV LVL__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> 🤠 __ADV Rank__", value: `> ${"_Unknown?_"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸ”” __Status__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> 🌐 __Server__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> šŸ”± __REP__", value: `> ${"_Unknown?_"}`, inline: true} + ); + + embed.addFields( + {name: "> šŸ”‡ __Is Muted__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> 🚫 __Is Banned__", value: `> ${"_Unknown?_"}`, inline: true}, + {name: "> šŸ‘‘ __Is King__", value: `> ${"_Unknown?_"}`, inline: true} + ); + */ + + embed.setTimestamp(); + + return embed; } - const level = get_level(data.user.experience); - embed.setDescription(`${get_level_to_emojis(Math.min(level, 2000))}\n${restrict_text("_This user has no description..._", 250)}`); - embed.setColor(0x500000); - - const id_emoji = application_emoji_cache.get("item_348") || "šŸ·ļø"; - const coins_emoji = application_emoji_cache.get("coin") || "šŸŖ™"; - const diamonds_emoji = "šŸ’Ž"; - const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ’Ŗ"; - const experience_emoji = application_emoji_cache.get("fuego") || "šŸ”„"; - const role_emoji = application_emoji_cache.get("item_36") || "šŸŽ–"; - - embed.addFields( - {name: `> ${id_emoji} __Identifier__`, value: `> ${data.user.id}`, inline: true}, - {name: `> ${coins_emoji} __Coins__`, value: `> ${format_score_with_commas(data.user.coins)}`, inline: true}, - {name: `> ${diamonds_emoji} __Diamonds__`, value: `> ***${format_score_with_commas(data.user.diamonds)}***`, inline: true} - ); - - embed.addFields( - {name: `> ${level_emoji} __Level__`, value: `> ${format_score_with_commas(level)}`, inline: true}, - {name: `> ${experience_emoji} __Experience__`, value: `> ${format_score_with_commas(data.user.experience)}`, inline: true}, - {name: `> ${role_emoji} __Role__`, value: `> ${data.user.streamer ? `${streamer_emoji} Streamer` : "Player"}`, inline: true} - ); - - const equipped_attack = data.user.attack - data.user.base_attack; - const equipped_defense = data.user.defense - data.user.base_defense; - const equipped_luck = data.user.luck - data.user.base_luck; - const equipped_hp = data.user.hp - data.user.base_hp; - const equipped_regeneration = data.user.regeneration - data.user.base_regeneration; - const equipped_speed = data.user.speed - data.user.base_speed; - - const formatted_attack = data.user.attack > 0 ? "+" + data.user.attack : data.user.attack; - const formatted_defense = data.user.defense > 0 ? "+" + data.user.defense : data.user.defense; - const formatted_luck = data.user.luck > 0 ? "+" + data.user.luck : data.user.luck; - const formatted_hp = data.user.hp > 0 ? "+" + data.user.hp : data.user.hp; - const formatted_regeneration = data.user.regeneration > 0 ? "+" + data.user.regeneration : data.user.regeneration; - const formatted_speed = data.user.speed > 0 ? "+" + data.user.speed : data.user.speed; - - const formatted_base_attack = data.user.base_attack > 0 ? "+" + data.user.base_attack : data.user.base_attack; - const formatted_base_defense = data.user.base_defense > 0 ? "+" + data.user.base_defense : data.user.base_defense; - const formatted_base_luck = data.user.base_luck > 0 ? "+" + data.user.base_luck : data.user.base_luck; - const formatted_base_hp = data.user.base_hp > 0 ? "+" + data.user.base_hp : data.user.base_hp; - const formatted_base_regeneration = data.user.base_regeneration > 0 ? "+" + data.user.base_regeneration : data.user.base_regeneration; - const formatted_base_speed = data.user.base_speed > 0 ? "+" + data.user.base_speed : data.user.base_speed; - - const formatted_equipped_attack = equipped_attack > 0 ? "+" + equipped_attack : equipped_attack; - const formatted_equipped_defense = equipped_defense > 0 ? "+" + equipped_defense : equipped_defense; - const formatted_equipped_luck = equipped_luck > 0 ? "+" + equipped_luck : equipped_luck; - const formatted_equipped_hp = equipped_hp > 0 ? "+" + equipped_hp : equipped_hp; - const formatted_equipped_regeneration = equipped_regeneration > 0 ? "+" + equipped_regeneration : equipped_regeneration; - const formatted_equipped_speed = equipped_speed > 0 ? "+" + equipped_speed : equipped_speed; - - const attack_emoji = application_emoji_cache.get("compass_item95") || "āš”ļø"; - const defense_emoji = application_emoji_cache.get("item_119") || "šŸ›”ļø"; - const luck_emoji = application_emoji_cache.get("item_478") || "šŸ€"; - const hp_emoji = application_emoji_cache.get("heart") || "ā¤ļø"; - const regeneration_emoji = application_emoji_cache.get("item_622") || "ā¤ļøā€šŸ©¹"; - const speed_emoji = application_emoji_cache.get("item_122") || "⚔"; - - embed.addFields( - {name: `> ${attack_emoji} __ATT__`, value: `> **${formatted_attack}** _(Base: ${formatted_base_attack}, Equipped: ${formatted_equipped_attack})_`, inline: true}, - {name: `> ${defense_emoji} __DEF__`, value: `> **${formatted_defense}** _(Base: ${formatted_base_defense}, Equipped: ${formatted_equipped_defense})_`, inline: true}, - {name: `> ${luck_emoji} __LCK__`, value: `> **${formatted_luck}** _(Base: ${formatted_base_luck}, Equipped: ${formatted_equipped_luck})_`, inline: true} - ); - - embed.addFields( - {name: `> ${hp_emoji} __MHP__`, value: `> **${formatted_hp}** _(Base: ${formatted_base_hp}, Equipped: ${formatted_equipped_hp})_`, inline: true}, - {name: `> ${regeneration_emoji} __RGN__`, value: `> **${formatted_regeneration}** _(Base: ${formatted_base_regeneration}, Equipped: ${formatted_equipped_regeneration})_`, inline: true}, - {name: `> ${speed_emoji} __SPD__`, value: `> **${formatted_speed}** _(Base: ${formatted_base_speed}, Equipped: ${formatted_equipped_speed})_`, inline: true} - ); - - /* - embed.addFields( - {name: "> šŸ’Æ __ADV Score__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> šŸ’Ŗ __ADV LVL__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> 🤠 __ADV Rank__", value: `> ${"_Unknown?_"}`, inline: true} - ); - - embed.addFields( - {name: "> šŸ”” __Status__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> 🌐 __Server__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> šŸ”± __REP__", value: `> ${"_Unknown?_"}`, inline: true} - ); - - embed.addFields( - {name: "> šŸ”‡ __Is Muted__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> 🚫 __Is Banned__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> šŸ‘‘ __Is King__", value: `> ${"_Unknown?_"}`, inline: true} - ); - */ - - embed.setTimestamp(); - - await interaction.editReply({ - embeds: [embed] + function get_buypoint_button_user(data_user){ + const diamonds_emoji = "šŸ’Ž"; + const max_skill_points = 20; + + const price_attack = 5 + 10 * data_user.base_attack; + const price_defense = 5 + 10 * data_user.base_defense; + const price_luck = 5 + 10 * data_user.base_luck; + const price_mhp = 5 + 10 * data_user.base_hp; + const price_regeneration = 5 + 10 * data_user.base_regeneration; + const price_speed = 5 + 10 * data_user.base_speed; + + const attack_button = new ButtonBuilder(); + attack_button.setCustomId("attack"); + attack_button.setEmoji(diamonds_emoji); + attack_button.setLabel(data_user.base_attack < max_skill_points ? `Buy 1 ATT point for ${price_attack}` : "ATT points is at MAX"); + attack_button.setStyle(ButtonStyle.Primary); + attack_button.setDisabled(data_user.base_attack >= max_skill_points || price_attack > data_user.diamonds); + + const defense_button = new ButtonBuilder(); + defense_button.setCustomId("defense"); + defense_button.setEmoji(diamonds_emoji); + defense_button.setLabel(data_user.base_defense < max_skill_points ? `Buy 1 DEF point for ${price_defense}` : "DEF points is at MAX"); + defense_button.setStyle(ButtonStyle.Primary); + defense_button.setDisabled(data_user.base_defense >= max_skill_points || price_defense > data_user.diamonds); + + const luck_button = new ButtonBuilder(); + luck_button.setCustomId("luck"); + luck_button.setEmoji(diamonds_emoji); + luck_button.setLabel(data_user.base_luck < max_skill_points ? `Buy 1 LCK point for ${price_luck}` : "LCK points is at MAX"); + luck_button.setStyle(ButtonStyle.Primary); + luck_button.setDisabled(data_user.base_luck >= max_skill_points || price_luck > data_user.diamonds); + + const mhp_button = new ButtonBuilder(); + mhp_button.setCustomId("hp"); + mhp_button.setEmoji(diamonds_emoji); + mhp_button.setLabel(data_user.base_hp < max_skill_points ? `Buy 1 MHP point for ${price_mhp}` : "RGN points is at MAX"); + mhp_button.setStyle(ButtonStyle.Primary); + mhp_button.setDisabled(data_user.base_hp >= max_skill_points || price_mhp > data_user.diamonds); + + const regeneration_button = new ButtonBuilder(); + regeneration_button.setCustomId("regeneration"); + regeneration_button.setEmoji(diamonds_emoji); + regeneration_button.setLabel(data_user.base_regeneration < max_skill_points ? `Buy 1 RGN point for ${price_regeneration}` : "RGN points is at MAX"); + regeneration_button.setStyle(ButtonStyle.Primary); + regeneration_button.setDisabled(data_user.base_regeneration >= max_skill_points || price_regeneration > data_user.diamonds); + + const speed_button = new ButtonBuilder(); + speed_button.setCustomId("speed"); + speed_button.setEmoji(diamonds_emoji); + speed_button.setLabel(data_user.base_speed < max_skill_points ? `Buy 1 SPD point for ${price_speed}` : "SPD points is at MAX"); + speed_button.setStyle(ButtonStyle.Primary); + speed_button.setDisabled(data_user.base_speed >= max_skill_points || price_speed > data_user.diamonds); + + const row_1 = new ActionRowBuilder(); + row_1.addComponents(attack_button, defense_button, luck_button); + + const row_2 = new ActionRowBuilder(); + row_2.addComponents(mhp_button, regeneration_button, speed_button); + + return [row_1, row_2]; + } + + const embed = get_embed_user(data.user); + + const gobattle_user_id_in_database = database.discord_user_to_gobattle_user_id(interaction.user); + const components_visible = data.user.id == gobattle_user_id_in_database || is_my_developer(client, interaction.user); + + const response_interaction = await interaction.editReply({ + embeds: [embed], + components: components_visible ? get_buypoint_button_user(data.user) : undefined }); + + function collector_filter(m){ + const result = m.user.id == interaction.user.id || database.discord_user_to_gobattle_user_id(m.user) == gobattle_user_id_in_database || is_my_developer(m.user.client, m.user); + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + async function button_interaction_logic(response_interaction){ + try{ + const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + await confirmation.deferReply({ephemeral: true}); + + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/buypoint/${confirmation.customId}/${gobattle_token}?platform=${platform}&ud=`, request_info); + const data = await response.json(); + + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + database.remove_gobattle_access_by_gobattle_user_id(user_id); + await confirmation.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + case "Invalid item or no enough money": + await confirmation.editReply("Unable to pay skill point. Invalid item or no enough money!"); + return; + case "Invalid category": + await confirmation.editReply(`Unable to pay skill point. Invalid category!\nContact ${client.application.owner} to resolve this issue.`); + return; + default: + await confirmation.editReply(`Unable to pay skill point. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; + } + + await confirmation.editReply("The skill point has been paid."); + + const embed = get_embed_user(data.user); + const rows = get_buypoint_button_user(data.user); + + response_interaction = await confirmation.update({embeds: [embed], components: rows}); + await button_interaction_logic(response_interaction); + }catch (_error){ + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + } + } + + if (components_visible){ + await button_interaction_logic(response_interaction); + } }catch(error){ await interaction.editReply(`Unable to retrieve information on this user.\nContact ${client.application.owner} to resolve this issue.`); console.error(error); @@ -638,7 +768,7 @@ async function get_friend_pending_count(interaction, client){ const data = await response.json(); if (data?.error == "Invalid token"){ - database.remove_gobattle_accesse_by_gobattle_user_id(user_id); + database.remove_gobattle_access_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -674,7 +804,7 @@ async function get_friend_pending_requests(interaction, client, type){ const data = await response.json(); if (data?.error == "Invalid token"){ - database.remove_gobattle_accesse_by_gobattle_user_id(user_id); + database.remove_gobattle_access_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -735,7 +865,7 @@ async function get_friend_list(interaction, client){ const data = await response.json(); if (data?.error == "Invalid token"){ - database.remove_gobattle_accesse_by_gobattle_user_id(user_id); + database.remove_gobattle_access_by_gobattle_user_id(user_id); await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); return; } @@ -814,7 +944,7 @@ async function get_friend_add(interaction, client){ const data = await response.json(); switch (data?.error){ case "Invalid token": - database.remove_gobattle_accesse_by_gobattle_user_id(my_gobattle_user_id); + database.remove_gobattle_access_by_gobattle_user_id(my_gobattle_user_id); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; case "Account is already a friend": @@ -870,7 +1000,7 @@ async function get_friend_delete(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": - database.remove_gobattle_accesse_by_discord_user(interaction.user); + database.remove_gobattle_access_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; default: @@ -913,7 +1043,7 @@ async function get_friend_cancel(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": - database.remove_gobattle_accesse_by_discord_user(interaction.user); + database.remove_gobattle_access_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; default: @@ -956,7 +1086,7 @@ async function get_friend_accept(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": - database.remove_gobattle_accesse_by_discord_user(interaction.user); + database.remove_gobattle_access_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; case "Error accepting request": @@ -1002,7 +1132,7 @@ async function get_friend_ignore(interaction, client){ if (!response.ok){ switch (data?.error){ case "Invalid token": - database.remove_gobattle_accesse_by_discord_user(interaction.user); + database.remove_gobattle_access_by_discord_user(interaction.user); await interaction.editReply("Your user session is unknown to me or has expired. You must log in to your account with `/user login`."); return; default: diff --git a/src/database/database.js b/src/database/database.js index 66a813c..69bfbdb 100644 --- a/src/database/database.js +++ b/src/database/database.js @@ -5,8 +5,8 @@ const fs = require("fs"); const database = new sqlite.DatabaseSync("database.sqlite"); -let add_gobattle_accesse_statement; -let remove_gobattle_accesse_statement; +let add_gobattle_access_statement; +let remove_gobattle_access_statement; let get_gobattle_token_by_gobattle_user_id_statement; let get_gobattle_token_by_discord_user_id_statement; let discord_user_id_to_gobattle_user_id_statement; @@ -21,7 +21,7 @@ function init(){ database.exec(data); - add_gobattle_accesse_statement = database.prepare(` + add_gobattle_access_statement = database.prepare(` INSERT INTO user_session (discord_user_id, gobattle_user_id, gobattle_token) VALUES ($discord_user_id, $gobattle_user_id, $gobattle_token) ON CONFLICT (discord_user_id) @@ -36,7 +36,7 @@ function init(){ session_date = CURRENT_TIMESTAMP; `); - remove_gobattle_accesse_statement = database.prepare(` + remove_gobattle_access_statement = database.prepare(` DELETE FROM user_session WHERE gobattle_user_id = $gobattle_user_id; `); @@ -59,13 +59,13 @@ function init(){ }); } -function add_gobattle_accesse(discord_user, gobattle_user_id, gobattle_token){ +function add_gobattle_access(discord_user, gobattle_user_id, gobattle_token){ const discord_user_prime = gobattle_user_id_to_discord_user_id(gobattle_user_id); if (discord_user_prime?.toString() == discord_user.id){ return false; } - add_gobattle_accesse_statement.run({ + add_gobattle_access_statement.run({ $discord_user_id: BigInt(discord_user.id), $gobattle_user_id: gobattle_user_id, $gobattle_token: gobattle_token @@ -74,26 +74,26 @@ function add_gobattle_accesse(discord_user, gobattle_user_id, gobattle_token){ return true; } -function remove_gobattle_accesse_by_discord_user(discord_user){ +function remove_gobattle_access_by_discord_user(discord_user){ const gobattle_user_id = discord_user_to_gobattle_user_id(discord_user); if (!gobattle_user_id){ return false; } - remove_gobattle_accesse_statement.run({ - $discord_user_id: gobattle_user_id, + remove_gobattle_access_statement.run({ + $gobattle_user_id: gobattle_user_id, }); return true; } -function remove_gobattle_accesse_by_gobattle_user_id(gobattle_user_id){ +function remove_gobattle_access_by_gobattle_user_id(gobattle_user_id){ const discord_user_id = gobattle_user_id_to_discord_user_id(gobattle_user_id); if (!discord_user_id){ return false; } - remove_gobattle_accesse_statement.run({ + remove_gobattle_access_statement.run({ $gobattle_user_id: gobattle_user_id, }); @@ -133,9 +133,9 @@ function gobattle_user_id_to_discord_user_id(gobattle_user_id){ } exports.init = init; -exports.add_gobattle_accesse = add_gobattle_accesse; -exports.remove_gobattle_accesse_by_discord_user = remove_gobattle_accesse_by_discord_user; -exports.remove_gobattle_accesse_by_gobattle_user_id = remove_gobattle_accesse_by_gobattle_user_id; +exports.add_gobattle_access = add_gobattle_access; +exports.remove_gobattle_access_by_discord_user = remove_gobattle_access_by_discord_user; +exports.remove_gobattle_access_by_gobattle_user_id = remove_gobattle_access_by_gobattle_user_id; exports.get_gobattle_token_by_gobattle_id = get_gobattle_token_by_gobattle_user_id; exports.get_gobattle_token_by_discord_user = get_gobattle_token_by_discord_user; exports.discord_user_to_gobattle_user_id = discord_user_to_gobattle_user_id diff --git a/src/images/mr_strong_head.png b/src/images/mr_strong_head.png deleted file mode 100644 index d72b3f8daef10c35d159c493889c3ea5e22f6607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 624 zcmV-$0+0QPP)Px%DM>^@R9HvVn9WMVKoo@&L9|6HRQxGLbkTyi6G0HcolCcFL|>ur;m%i3+~^~? z6Gae2S1L3MrIh-E1*^1*pyGrRoSV6wNiCV!O-bf9bIx~9Ceyf%=95mPzJ5wZCJVY! zY5eilla8CMmaDpe^5pNr*acw1NOs#0(vb)euqMEC;;2~wNsdH7;NJ+)WrHKsH3D51 zV2XY=Kd7Ahy!xU%4-(WlEp`(xfI9E*?GYv>h6f}wduP?Q-NAJzF)aaW0!$}{?OfK+ z$kxHNM544jEskq-KP% { return; } - const success = database.add_gobattle_accesse(interaction.user, data.id, data.token); + const success = database.add_gobattle_access(interaction.user, data.id, data.token); if (success){ interaction.editReply("Your account has been successfully registered. You can log out at any time with `/user logout`."); }else{ diff --git a/src/utils.js b/src/utils.js index 7f6c188..dfec123 100644 --- a/src/utils.js +++ b/src/utils.js @@ -225,7 +225,7 @@ async function send_embed_layout(interaction, embed, pages, header_description = }); function collector_filter(m){ - const result = m.user.id == interaction.user.id; + const result = m.user.id == interaction.user.id || is_my_developer(m.user.client, m.user); if (!result){ m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { From e258d22fe6f866ebfc356e62d2f5d117dadc07ec Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Thu, 8 Aug 2024 18:38:06 +0200 Subject: [PATCH 44/54] aaaaaaaaaaa --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 14bf337..95d3975 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ sudo docker-compose down ## Screenshot Gallery

+ From ab3c5cebdfc346f8b5c3e41c4eb340505200bbc1 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Fri, 9 Aug 2024 00:28:37 +0200 Subject: [PATCH 45/54] Fixed bug when updating user profile after purchasing skill points. --- src/commands/user.js | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/commands/user.js b/src/commands/user.js index adf4573..7173328 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -544,26 +544,6 @@ async function get_info(interaction, client){ {name: `> ${speed_emoji} __SPD__`, value: `> **${formatted_speed}** _(Base: ${formatted_base_speed}, Equipped: ${formatted_equipped_speed})_`, inline: true} ); - /* - embed.addFields( - {name: "> šŸ’Æ __ADV Score__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> šŸ’Ŗ __ADV LVL__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> 🤠 __ADV Rank__", value: `> ${"_Unknown?_"}`, inline: true} - ); - - embed.addFields( - {name: "> šŸ”” __Status__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> 🌐 __Server__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> šŸ”± __REP__", value: `> ${"_Unknown?_"}`, inline: true} - ); - - embed.addFields( - {name: "> šŸ”‡ __Is Muted__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> 🚫 __Is Banned__", value: `> ${"_Unknown?_"}`, inline: true}, - {name: "> šŸ‘‘ __Is King__", value: `> ${"_Unknown?_"}`, inline: true} - ); - */ - embed.setTimestamp(); return embed; @@ -657,7 +637,7 @@ async function get_info(interaction, client){ try{ const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); await confirmation.deferReply({ephemeral: true}); - + const platform = "Web"; const request_info = { method: "POST" @@ -665,7 +645,7 @@ async function get_info(interaction, client){ const response = await fetch(`https://gobattle.io/api.php/buypoint/${confirmation.customId}/${gobattle_token}?platform=${platform}&ud=`, request_info); const data = await response.json(); - if (!response.ok){ + if (response.ok){ switch (data?.error){ case "Invalid token": database.remove_gobattle_access_by_gobattle_user_id(user_id); @@ -684,13 +664,14 @@ async function get_info(interaction, client){ } await confirmation.editReply("The skill point has been paid."); - + const embed = get_embed_user(data.user); const rows = get_buypoint_button_user(data.user); - response_interaction = await confirmation.update({embeds: [embed], components: rows}); + await response_interaction.edit({embeds: [embed], components: rows}); await button_interaction_logic(response_interaction); }catch (_error){ + console.error(_error); await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); } } From 0d045e059887514f7abb0a2edbc39642752962fc Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Fri, 9 Aug 2024 00:45:12 +0200 Subject: [PATCH 46/54] Fixed bug when updating user profile after purchasing skill points. --- src/commands/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/user.js b/src/commands/user.js index 7173328..7cd9c1a 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -645,7 +645,7 @@ async function get_info(interaction, client){ const response = await fetch(`https://gobattle.io/api.php/buypoint/${confirmation.customId}/${gobattle_token}?platform=${platform}&ud=`, request_info); const data = await response.json(); - if (response.ok){ + if (!response.ok){ switch (data?.error){ case "Invalid token": database.remove_gobattle_access_by_gobattle_user_id(user_id); From fae302847b77fd3751c05d6fd8e0de9692680357 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Sat, 10 Aug 2024 01:28:29 +0200 Subject: [PATCH 47/54] minor modification of integrations. --- src/commands/user.js | 5 +++-- src/utils.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/commands/user.js b/src/commands/user.js index 7cd9c1a..91cf598 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -475,7 +475,8 @@ async function get_info(interaction, client){ } const level = get_level(data_user.experience); - embed.setDescription(`${get_level_to_emojis(Math.min(level, 2000))}\n${restrict_text("_This user has no description..._", 250)}`); + const description = data_user.admin ? "This user is an **administrator**." : "_This user has no description..._"; + embed.setDescription(`${get_level_to_emojis(Math.min(level, 2000))}\n${restrict_text(description, 250)}`); embed.setColor(0x500000); const id_emoji = application_emoji_cache.get("item_348") || "šŸ·ļø"; @@ -672,7 +673,7 @@ async function get_info(interaction, client){ await button_interaction_logic(response_interaction); }catch (_error){ console.error(_error); - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to pay skill points.", components: []}); } } diff --git a/src/utils.js b/src/utils.js index dfec123..f0c24c7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -254,7 +254,7 @@ async function send_embed_layout(interaction, embed, pages, header_description = response_interaction = await confirmation.update({embeds: [embed], components: [row]}); await button_interaction_logic(response_interaction); }catch (_error){ - await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to navigate the list.", components: []}); + await interaction.editReply({content: "-# ā“˜ This interaction has timed out, please use the command again to be able to navigate from page to page.", components: []}); } } From 85c0f57eb021e5d4bc5ecf3a53b658f6f458bded Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Sat, 10 Aug 2024 20:51:02 +0200 Subject: [PATCH 48/54] del bug --- src/commands/user.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/user.js b/src/commands/user.js index 91cf598..e7cdfa0 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -466,7 +466,7 @@ async function get_info(interaction, client){ const unknown_head_emoji = application_emoji_cache.get("heads_item_0") || "šŸ‘¤"; const streamer_emoji = application_emoji_cache.get("compass_item122") || "šŸ”“"; - const head_data = heads_map.get(data_user.skin_head); + const head_data = heads_map.get(data_user?.skin_head); const head_emoji = application_emoji_cache.get(head_data?.emoji) || unknown_head_emoji; embed.setTitle(`${head_emoji} ${data_user.streamer ? `${streamer_emoji} ` : ""}**${restrict_text(data_user.nick, 60)}**#${data_user.id}`); @@ -666,8 +666,8 @@ async function get_info(interaction, client){ await confirmation.editReply("The skill point has been paid."); - const embed = get_embed_user(data.user); - const rows = get_buypoint_button_user(data.user); + const embed = get_embed_user(data); + const rows = get_buypoint_button_user(data); await response_interaction.edit({embeds: [embed], components: rows}); await button_interaction_logic(response_interaction); From 495c5af4e1ff4c1f8394bb84c4ba615660d3ea73 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Sat, 17 Aug 2024 21:07:07 +0200 Subject: [PATCH 49/54] Add a skill point purchase confirmation button --- src/commands/user.js | 163 +++++++++++++++++++++++++++------------ src/database/database.js | 8 +- src/index.js | 38 ++++++++- src/utils.js | 33 ++++++-- 4 files changed, 181 insertions(+), 61 deletions(-) diff --git a/src/commands/user.js b/src/commands/user.js index e7cdfa0..d17b9f0 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -448,7 +448,7 @@ async function get_info(interaction, client){ const server_version = 115; const platform = "Web"; const response = await fetch(`https://gobattle.io/api.php/bootstrap/${server_version}/${gobattle_token}?platform=${platform}&ud=`); - const data = await response.json(); + let data = await response.json(); if (!response.ok){ if (data?.error == "Invalid token"){ @@ -550,58 +550,64 @@ async function get_info(interaction, client){ return embed; } + function get_price_skills(data_user){ + return { + attack: 5 + 10 * data_user.base_attack, + defense: 5 + 10 * data_user.base_defense, + luck: 5 + 10 * data_user.base_luck, + hp: 5 + 10 * data_user.base_hp, + regeneration: 5 + 10 * data_user.base_regeneration, + speed: 5 + 10 * data_user.base_speed + }; + } + function get_buypoint_button_user(data_user){ const diamonds_emoji = "šŸ’Ž"; const max_skill_points = 20; - const price_attack = 5 + 10 * data_user.base_attack; - const price_defense = 5 + 10 * data_user.base_defense; - const price_luck = 5 + 10 * data_user.base_luck; - const price_mhp = 5 + 10 * data_user.base_hp; - const price_regeneration = 5 + 10 * data_user.base_regeneration; - const price_speed = 5 + 10 * data_user.base_speed; + const price_skills = get_price_skills(data_user); const attack_button = new ButtonBuilder(); attack_button.setCustomId("attack"); attack_button.setEmoji(diamonds_emoji); - attack_button.setLabel(data_user.base_attack < max_skill_points ? `Buy 1 ATT point for ${price_attack}` : "ATT points is at MAX"); + attack_button.setLabel(data_user.base_attack < max_skill_points ? `Buy 1 ATT point for ${price_skills.attack}` : "ATT points is at MAX"); attack_button.setStyle(ButtonStyle.Primary); - attack_button.setDisabled(data_user.base_attack >= max_skill_points || price_attack > data_user.diamonds); + attack_button.setDisabled(data_user.base_attack >= max_skill_points || price_skills.attack > data_user.diamonds); const defense_button = new ButtonBuilder(); defense_button.setCustomId("defense"); defense_button.setEmoji(diamonds_emoji); - defense_button.setLabel(data_user.base_defense < max_skill_points ? `Buy 1 DEF point for ${price_defense}` : "DEF points is at MAX"); + defense_button.setLabel(data_user.base_defense < max_skill_points ? `Buy 1 DEF point for ${price_skills.defense}` : "DEF points is at MAX"); defense_button.setStyle(ButtonStyle.Primary); - defense_button.setDisabled(data_user.base_defense >= max_skill_points || price_defense > data_user.diamonds); + defense_button.setDisabled(data_user.base_defense >= max_skill_points || price_skills.defense > data_user.diamonds); const luck_button = new ButtonBuilder(); luck_button.setCustomId("luck"); luck_button.setEmoji(diamonds_emoji); - luck_button.setLabel(data_user.base_luck < max_skill_points ? `Buy 1 LCK point for ${price_luck}` : "LCK points is at MAX"); + luck_button.setLabel(data_user.base_luck < max_skill_points ? `Buy 1 LCK point for ${price_skills.luck}` : "LCK points is at MAX"); luck_button.setStyle(ButtonStyle.Primary); - luck_button.setDisabled(data_user.base_luck >= max_skill_points || price_luck > data_user.diamonds); + luck_button.setDisabled(data_user.base_luck >= max_skill_points || price_skills.luck > data_user.diamonds); const mhp_button = new ButtonBuilder(); mhp_button.setCustomId("hp"); mhp_button.setEmoji(diamonds_emoji); - mhp_button.setLabel(data_user.base_hp < max_skill_points ? `Buy 1 MHP point for ${price_mhp}` : "RGN points is at MAX"); + mhp_button.setLabel(data_user.base_hp < max_skill_points ? `Buy 1 MHP point for ${price_skills.hp}` : "RGN points is at MAX"); mhp_button.setStyle(ButtonStyle.Primary); - mhp_button.setDisabled(data_user.base_hp >= max_skill_points || price_mhp > data_user.diamonds); + mhp_button.setDisabled(data_user.base_hp >= max_skill_points || price_skills.hp > data_user.diamonds); const regeneration_button = new ButtonBuilder(); regeneration_button.setCustomId("regeneration"); regeneration_button.setEmoji(diamonds_emoji); - regeneration_button.setLabel(data_user.base_regeneration < max_skill_points ? `Buy 1 RGN point for ${price_regeneration}` : "RGN points is at MAX"); + regeneration_button.setLabel(data_user.base_regeneration < max_skill_points ? `Buy 1 RGN point for ${price_skills.regeneration}` : "RGN points is at MAX"); regeneration_button.setStyle(ButtonStyle.Primary); - regeneration_button.setDisabled(data_user.base_regeneration >= max_skill_points || price_regeneration > data_user.diamonds); + regeneration_button.setDisabled(data_user.base_regeneration >= max_skill_points || price_skills.regeneration > data_user.diamonds); const speed_button = new ButtonBuilder(); speed_button.setCustomId("speed"); speed_button.setEmoji(diamonds_emoji); - speed_button.setLabel(data_user.base_speed < max_skill_points ? `Buy 1 SPD point for ${price_speed}` : "SPD points is at MAX"); + speed_button.setLabel(data_user.base_speed < max_skill_points ? `Buy 1 SPD point for ${price_skills.speed}` : "SPD points is at MAX"); speed_button.setStyle(ButtonStyle.Primary); - speed_button.setDisabled(data_user.base_speed >= max_skill_points || price_speed > data_user.diamonds); + speed_button.setDisabled(data_user.base_speed >= max_skill_points || price_skills.speed > data_user.diamonds); const row_1 = new ActionRowBuilder(); row_1.addComponents(attack_button, defense_button, luck_button); @@ -635,45 +641,49 @@ async function get_info(interaction, client){ } async function button_interaction_logic(response_interaction){ + let confirmation_message; + try{ - const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - await confirmation.deferReply({ephemeral: true}); + const skill_interaction = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - const platform = "Web"; - const request_info = { - method: "POST" - }; - const response = await fetch(`https://gobattle.io/api.php/buypoint/${confirmation.customId}/${gobattle_token}?platform=${platform}&ud=`, request_info); - const data = await response.json(); - - if (!response.ok){ - switch (data?.error){ - case "Invalid token": - database.remove_gobattle_access_by_gobattle_user_id(user_id); - await confirmation.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); - return; - case "Invalid item or no enough money": - await confirmation.editReply("Unable to pay skill point. Invalid item or no enough money!"); - return; - case "Invalid category": - await confirmation.editReply(`Unable to pay skill point. Invalid category!\nContact ${client.application.owner} to resolve this issue.`); - return; - default: - await confirmation.editReply(`Unable to pay skill point. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); - } - return; - } - - await confirmation.editReply("The skill point has been paid."); + const confirm_button = new ButtonBuilder(); + confirm_button.setCustomId("confirm"); + confirm_button.setLabel("Confirm"); + confirm_button.setStyle(ButtonStyle.Success); + + const cancel_button = new ButtonBuilder(); + cancel_button.setCustomId("cancel"); + cancel_button.setLabel("Cancel"); + cancel_button.setStyle(ButtonStyle.Danger); + + const row = new ActionRowBuilder(); + row.addComponents(confirm_button, cancel_button); + + await skill_interaction.deferReply(); + confirmation_message = await skill_interaction.followUp({ + content: `Do you want to buy 1 **${skill_interaction.customId.toLocaleUpperCase()}** point for **${get_price_skills(data.user)[skill_interaction.customId]}** diamonds?`, + components: [row] + }); + + await response_interaction.edit({components: []}); - const embed = get_embed_user(data); - const rows = get_buypoint_button_user(data); + const new_data_user = await get_purchase_confirmation_skill(confirmation_message, client, skill_interaction.user.id, user_id, gobattle_token, skill_interaction.customId); + if (new_data_user){ + data.user = new_data_user; + } + + const embed = get_embed_user(data.user); + const rows = get_buypoint_button_user(data.user); + await response_interaction.edit({embeds: [embed], components: rows}); await button_interaction_logic(response_interaction); }catch (_error){ - console.error(_error); await interaction.editReply({content: "-# ā“˜ This interaction has expired, please use the command again to be able to pay skill points.", components: []}); + + if (confirmation_message){ + await confirmation_message.edit({content: "Payment for skill point has been canceled.", components: []}); + } } } @@ -686,6 +696,59 @@ async function get_info(interaction, client){ } } +async function get_purchase_confirmation_skill(confirmation_message, client, discord_user_id, gobattle_user_id, gobattle_token, skill_type){ + function collector_filter(m){ + const result = m.user.id == discord_user_id || is_my_developer(m.user.client, m.user); + + if (!result){ + m.reply({content: "You cannot interact with a command that you did not initiate yourself.", ephemeral: true}).catch((error) => { + console.error(error); + }); + } + + return result; + } + + try{ + const confirmation = await confirmation_message.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); + if (confirmation.customId == "cancel"){ + await confirmation.update({content: "Payment for skill point has been canceled.", components: []}); + return; + } + + const platform = "Web"; + const request_info = { + method: "POST" + }; + const response = await fetch(`https://gobattle.io/api.php/buypoint/${skill_type}/${gobattle_token}?platform=${platform}&ud=`, request_info); + const data = await response.json(); + + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + database.remove_gobattle_access_by_gobattle_user_id(gobattle_user_id); + await confirmation.update({content: `User _#${gobattle_user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`, components: []}); + return; + case "Invalid item or no enough money": + await confirmation.update({content: "Unable to pay skill point. Invalid item or no enough money!", components: []}); + return; + case "Invalid category": + await confirmation.update({content: `Unable to pay skill point. Invalid category!\nContact ${client.application.owner} to resolve this issue.`, components: []}); + return; + default: + await confirmation.update({content: `Unable to pay skill point. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`, components: []}); + } + return; + } + + await confirmation.update({content: "The skill point has been paid.", components: []}); + + return data; + }catch(error){ + await confirmation_message.editReply({content: "Payment for skill point has been canceled.", components: []}); + } +} + async function get_king(interaction, _client){ await interaction.reply("This subcommand is currently under development. You cannot use it at the moment."); } diff --git a/src/database/database.js b/src/database/database.js index 69bfbdb..350887a 100644 --- a/src/database/database.js +++ b/src/database/database.js @@ -105,7 +105,7 @@ function get_gobattle_token_by_gobattle_user_id(gobattle_user_id){ $gobattle_user_id: gobattle_user_id }); - return data.gobattle_token; + return data?.gobattle_token; } function get_gobattle_token_by_discord_user(discord_user){ @@ -113,7 +113,7 @@ function get_gobattle_token_by_discord_user(discord_user){ $discord_user_id: discord_user.id }); - return data.gobattle_token; + return data?.gobattle_token; } function discord_user_to_gobattle_user_id(discord_user){ @@ -121,7 +121,7 @@ function discord_user_to_gobattle_user_id(discord_user){ $discord_user_id: BigInt(discord_user.id) }); - return data.gobattle_user_id; + return data?.gobattle_user_id; } function gobattle_user_id_to_discord_user_id(gobattle_user_id){ @@ -129,7 +129,7 @@ function gobattle_user_id_to_discord_user_id(gobattle_user_id){ $gobattle_user_id: gobattle_user_id }); - return data.discord_user_id; + return data?.discord_user_id; } exports.init = init; diff --git a/src/index.js b/src/index.js index db5a6e7..7bc2f3e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -const {Client, Events, Partials, IntentsBitField, Routes, ActivityType} = require("discord.js"); +const {Client, Events, Partials, IntentsBitField, Routes, ActivityType, ButtonBuilder, ButtonStyle, ActionRowBuilder, ComponentType} = require("discord.js"); const {get_first_chat_channel, is_my_developer, update_application_emoji_cache, get_level_to_emojis, application_emoji_cache, get_info_application} = require("./utils.js"); const database = require("./database/database.js"); @@ -287,7 +287,7 @@ client.on(Events.MessageCreate, async (msg) => { if (msg.author.bot){ return; } - + try{ const command_info = msg.content.trim().split(" "); const command = command_info[0].toLowerCase(); @@ -355,6 +355,40 @@ client.on(Events.MessageCreate, async (msg) => { const emoji = application_emoji_cache.get(command_info[1]) || "_Unknown?_"; await msg.reply(`The emoji is: ${emoji}`); + break; + case "!gb_confirm": + if (!is_my_developer(client, msg.author)){ + await msg.reply("You do not have permission to use this command."); + return; + } + + const confirm_button = new ButtonBuilder(); + confirm_button.setCustomId("confirm"); + confirm_button.setLabel("Confirm"); + confirm_button.setStyle(ButtonStyle.Success); + + const cancel_button = new ButtonBuilder(); + cancel_button.setCustomId("cancel"); + cancel_button.setLabel("Cancel"); + cancel_button.setStyle(ButtonStyle.Danger); + + const row = new ActionRowBuilder(); + row.addComponents(confirm_button, cancel_button); + + const response_interaction = await msg.reply({ + content: "Hello dad, here is a confirmation message:", + components: [row] + }); + + const confirmation = await response_interaction.awaitMessageComponent({filter: () => {return true}, componentType: ComponentType.Button, time: 60_000}); + + if (confirmation.customId == "confirm"){ + await confirmation.update({content: "Confirm.", components: []}); + return; + } + + await confirmation.update({content: "Cancel.", components: []}); + break; } }catch (error){ diff --git a/src/utils.js b/src/utils.js index f0c24c7..3029adf 100644 --- a/src/utils.js +++ b/src/utils.js @@ -203,6 +203,12 @@ async function send_embed_layout(interaction, embed, pages, header_description = embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + const first_button = new ButtonBuilder(); + first_button.setCustomId("first"); + first_button.setEmoji("āŖ"); + first_button.setStyle(ButtonStyle.Primary); + first_button.setDisabled(current_page == 1); + const previous_button = new ButtonBuilder(); previous_button.setCustomId("previous"); previous_button.setEmoji("ā—€ļø"); @@ -215,8 +221,14 @@ async function send_embed_layout(interaction, embed, pages, header_description = next_button.setStyle(ButtonStyle.Primary); next_button.setDisabled(current_page == nb_pages); + const last_button = new ButtonBuilder(); + last_button.setCustomId("last"); + last_button.setEmoji("ā©"); + last_button.setStyle(ButtonStyle.Primary); + last_button.setDisabled(current_page == nb_pages); + const row = new ActionRowBuilder(); - row.addComponents(previous_button, next_button); + row.addComponents(first_button, previous_button, next_button, last_button); const response_interaction = await interaction.editReply({ content: content_message, @@ -240,16 +252,27 @@ async function send_embed_layout(interaction, embed, pages, header_description = try{ const confirmation = await response_interaction.awaitMessageComponent({filter: collector_filter, componentType: ComponentType.Button, time: 60_000}); - if (confirmation.customId === "previous"){ - current_page--; - } else if (confirmation.customId === "next"){ - current_page++; + switch (confirmation.customId){ + case "first": + current_page = 1; + break; + case "previous": + current_page--; + break; + case "next": + current_page++; + break; + case "last": + current_page = nb_pages; + break; } embed.setDescription(header_description + pages[current_page - 1]); embed.setFooter({text: `Page ${current_page}/${nb_pages}`}); + first_button.setDisabled(current_page == 1); previous_button.setDisabled(current_page == 1); next_button.setDisabled(current_page == nb_pages); + last_button.setDisabled(current_page == nb_pages); response_interaction = await confirmation.update({embeds: [embed], components: [row]}); await button_interaction_logic(response_interaction); From 00c0e8633f398b3759e8ca16524b5849ff53e910 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Mon, 19 Aug 2024 02:40:20 +0200 Subject: [PATCH 50/54] add some details in the readme file. --- README.md | 7 +++ src/commands/dungeon.js | 116 ++++++++++++++++++++++++++++++++++++++-- src/commands/item.js | 2 +- src/commands/user.js | 3 +- src/index.js | 2 +- 5 files changed, 124 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 95d3975..4d8ab37 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,13 @@ The door is open to all potential contributors. The project is now managed by Sh ## To start the project, open a terminal at the root of the project and use the following commands: (Make sure you have set the environment variables correctly in the `.env` file.) +The .env file must be created manually at the root of the project and should look like this: + +```env +TOKEN = "token_discord_app" +# The GUILD_ADMIN_ID variable gives certain administrator privileges to the specified guilds. +GUILD_ADMIN_ID = ["380588354934276097"] +``` Assume you are on a Linux environment: diff --git a/src/commands/dungeon.js b/src/commands/dungeon.js index 3b2f17d..3af6ab8 100644 --- a/src/commands/dungeon.js +++ b/src/commands/dungeon.js @@ -1,6 +1,14 @@ const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); -const {restrict_text, send_embed_layout} = require("../utils.js"); +const {restrict_text, send_embed_layout, is_my_developer, application_emoji_cache} = require("../utils.js"); const {dungeon_list} = require("../dungeon_list.json"); +const {ultra_list} = require("../ultralist.json"); + +const items_map = new Map(); +for (const item_data of ultra_list){ + if (typeof item_data.id == "number"){ + items_map.set(item_data.id, item_data); + } +} const dungeon_command = new SlashCommandBuilder(); dungeon_command.setName("dungeon"); @@ -41,8 +49,110 @@ async function get_dungeon(interaction, client){ } } -async function get_info(interaction, _client){ - await interaction.reply("This subcommand is in development and cannot be used at this time."); +async function get_info(interaction, client){ + if (!is_my_developer(client, interaction.user)){ + await interaction.reply("This subcommand is in development and cannot be used at this time."); + return; + } + + await interaction.deferReply(); + + try{ + const response = await fetch("https://gobattle.io/api.php/v1/stats/ultrarare/drops"); + + if (!response.ok){ + await interaction.editReply("Unable to obtain general information on the general frequency of obtaining ultrarares in Gobattle. There is a problem with the Gobattle API."); + return; + } + + const data = { + name: "Frostbite Dungeon", + id: 57, + level: null, + ultras: [ + { + id: 2, + rl: 500, + probability: 2.92 + }, + { + id: 3, + rl: 500, + probability: 2.92 + }, + { + id: 4, + rl: 3000, + probability: 17.54 + }, + { + id: 6, + rl: 4000, + probability: 23.39 + }, + { + id: 36, + rl: 5000, + probability: 29.24 + }, + { + id: 42, + rl: 3000, + probability: 17.54 + }, + { + id: 116, + rl: 100, + probability: 0.58 + }, + { + id: 168, + rl: 10000, + probability: 5.85 + } + ] + }; + + const embed = new EmbedBuilder(); + + const dungeon_emoji = "šŸ°"; + embed.setTitle(`${dungeon_emoji} ${data.name}#${data.id} ${dungeon_emoji}`); + + const header_description = "Here is the list of Ultrarares that you could find in this dungeon:"; + const max_items_by_pages = 8; + const pages = new Array(Math.ceil(data.ultras.length / max_items_by_pages)); + const unknown_item_emoji = application_emoji_cache.get("item_91") || "šŸ“¦"; + const id_emoji = application_emoji_cache.get("item_348") || "šŸ·ļø"; + const level_emoji = application_emoji_cache.get("compass_item121") || "šŸ’Ŗ"; + const attendance_emoji = "šŸ‘„"; + + let current_page = -1; + for (let i = 0; i < data.ultras.length; i++){ + if (Math.floor(i / max_items_by_pages) != current_page){ + current_page++; + pages[current_page] = ""; + } + + const ultra_data = data.ultras[i]; + const ultra_info = items_map.get(parseInt(ultra_data.id, 10)); + const item_emoji = application_emoji_cache.get(ultra_info?.emoji) || unknown_item_emoji; + pages[current_page] += `* ${item_emoji} **${restrict_text(ultra_info?.name || "*Unknow?*", 45)}**#${ultra_data.id}: \`RL: ${ultra_data.rl}\` \`PB: ${ultra_data.probability}%\`\n`; + } + + embed.addFields( + {name: `> ${id_emoji} __Identifier__`, value: `> ${data.id}`, inline: true}, + {name: `> ${level_emoji} __Recommended level__`, value: `> ${"*Unknow?*"}`, inline: true}, + {name: `> ${attendance_emoji} __Attendance__`, value: `> ${"*Unknow?*"}`, inline: true} + ); + + embed.setTimestamp(); + + const message_content = "Dev testing..."; + await send_embed_layout(interaction, embed, pages, header_description, message_content); + }catch(error){ + await interaction.editReply(`Unable to generate template.\nContact ${client.application.owner} to resolve this issue.`); + console.error(error); + } } async function get_list(interaction, _client){ diff --git a/src/commands/item.js b/src/commands/item.js index e9cf86e..ed9a4fb 100644 --- a/src/commands/item.js +++ b/src/commands/item.js @@ -74,7 +74,7 @@ async function get_info(interaction, client){ const item_emoji = application_emoji_cache.get(item_info?.emoji) || unknown_item_emoji; const embed = new EmbedBuilder(); - embed.setTitle(`${item_emoji} ${restrict_text(item_info.name, 60)}`); + embed.setTitle(`${item_emoji} ${restrict_text(item_info.name, 60)}#${item_id}`); if (typeof item_emoji != "string"){ embed.setThumbnail(item_emoji.imageURL()); } diff --git a/src/commands/user.js b/src/commands/user.js index d17b9f0..b928c9a 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -662,7 +662,8 @@ async function get_info(interaction, client){ await skill_interaction.deferReply(); confirmation_message = await skill_interaction.followUp({ content: `Do you want to buy 1 **${skill_interaction.customId.toLocaleUpperCase()}** point for **${get_price_skills(data.user)[skill_interaction.customId]}** diamonds?`, - components: [row] + components: [row], + ephemeral: true }); await response_interaction.edit({components: []}); diff --git a/src/index.js b/src/index.js index 7bc2f3e..0976482 100644 --- a/src/index.js +++ b/src/index.js @@ -287,7 +287,7 @@ client.on(Events.MessageCreate, async (msg) => { if (msg.author.bot){ return; } - + try{ const command_info = msg.content.trim().split(" "); const command = command_info[0].toLowerCase(); From df52810f009565f66214a414636477d9401fe561 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Fri, 23 Aug 2024 23:21:53 +0200 Subject: [PATCH 51/54] new command + fix errors --- README.md | 2 +- src/commands/date_new_king.js | 4 +- src/commands/user.js | 89 +++++++++++++++++++++++++++++++++-- src/utils.js | 4 +- 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4d8ab37..a8bdd64 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The door is open to all potential contributors. The project is now managed by Sh ## To start the project, open a terminal at the root of the project and use the following commands: (Make sure you have set the environment variables correctly in the `.env` file.) -The .env file must be created manually at the root of the project and should look like this: +The `.env` file must be created manually at the root of the project and should look like this: ```env TOKEN = "token_discord_app" diff --git a/src/commands/date_new_king.js b/src/commands/date_new_king.js index d52416f..01f27ff 100644 --- a/src/commands/date_new_king.js +++ b/src/commands/date_new_king.js @@ -1,12 +1,12 @@ const {SlashCommandBuilder} = require("discord.js"); -const {get_utf_time_next_king} = require("../utils.js"); +const {get_utc_time_next_king} = require("../utils.js"); const date_new_king_command = new SlashCommandBuilder(); date_new_king_command.setName("get_date_new_king"); date_new_king_command.setDescription("Get the date and time remaining before the next king arrives."); async function get_date_new_king(interaction, client){ - const utc = Math.floor(get_utf_time_next_king() / 1000); + const utc = Math.floor(get_utc_time_next_king() / 1000); await interaction.reply(`New king on ().`); } diff --git a/src/commands/user.js b/src/commands/user.js index b928c9a..0d0b6b6 100644 --- a/src/commands/user.js +++ b/src/commands/user.js @@ -128,6 +128,29 @@ user_command.addSubcommand((subcommand) => { return subcommand; }); +user_command.addSubcommand((subcommand) => { + subcommand.setName("change_nickname"); + subcommand.setDescription("Change a GoBattle.io user's nickname."); + subcommand.addIntegerOption((option) => { + option.setName("user_id"); + option.setDescription("The user identifier."); + option.setMinValue(1); + option.setRequired(true); + + return option; + }); + subcommand.addStringOption((option) => { + option.setName("nick"); + option.setDescription("New nickname."); + option.setMaxLength(1); + option.setMaxLength(20); + option.setRequired(true); + + return option; + }); + + return subcommand; +}); user_command.addSubcommandGroup((friend_command) => { friend_command.setName("friend"); friend_command.setDescription("Command relating to friends in the game."); @@ -297,6 +320,9 @@ async function get_user(interaction, client){ case "discord_to_gobattle": await get_discord_to_gobattle(interaction, client); break; + case "change_nickname": + await get_change_nickname(interaction, client); + break; default: await interaction.reply(`Invalid subcommand.\nContact ${client.application.owner} to resolve this issue.`); } @@ -356,7 +382,8 @@ async function get_create(interaction, _client){ const password_input = new TextInputBuilder(); password_input.setCustomId("password"); - password_input.setLabel("Password (Don't give Discord password!)"); + password_input.setLabel("Password"); + password_input.setPlaceholder("Don't give Discord password!"); password_input.setStyle(TextInputStyle.Short); email_input.setRequired(true); @@ -381,15 +408,17 @@ async function get_login(interaction, _client){ const email_input = new TextInputBuilder(); email_input.setCustomId("email"); - email_input.setLabel("Email (Used on GoBattle.io)"); + email_input.setLabel("Email"); + email_input.setPlaceholder("Email used on GoBattle.io") email_input.setStyle(TextInputStyle.Short); email_input.setRequired(true); const password_input = new TextInputBuilder(); password_input.setCustomId("password"); - password_input.setLabel("Password (Don't give Discord password!)"); + password_input.setLabel("Password"); + password_input.setPlaceholder("Don't give Discord password!"); password_input.setStyle(TextInputStyle.Short); - email_input.setRequired(true); + password_input.setRequired(true); const email_action_row = new ActionRowBuilder().addComponents(email_input); const password_action_row = new ActionRowBuilder().addComponents(password_input); @@ -797,6 +826,58 @@ async function get_discord_to_gobattle(interaction, _client){ await interaction.editReply(`The Discord account ${discord_user} belongs to the GoBattle account _#${gobattle_user_id}_.`); } +async function get_change_nickname(interaction, client){ + await interaction.deferReply({ephemeral: true}); + + const user_id = interaction.options.get("user_id")?.value; + const nick = interaction.options.get("nick")?.value; + + const can_change = database.discord_user_to_gobattle_user_id(interaction.user) == user_id || is_my_developer(client, interaction.user); + if (!can_change){ + await interaction.editReply("You cannot change the nickname of a GoBattle account that does not belong to you."); + return; + } + + const gobattle_token = database.get_gobattle_token_by_gobattle_id(user_id); + if (!gobattle_token){ + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + } + + const platform = "Web"; + const request_info = { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + "nick": nick + }) + }; + const response = await fetch(`https://gobattle.io/api.php/nick/${gobattle_token}?platform=${platform}&ud=`, request_info); + const data = await response.json(); + + if (!response.ok){ + switch (data?.error){ + case "Invalid token": + database.remove_gobattle_access_by_gobattle_user_id(user_id); + await interaction.editReply(`User _#${user_id}_ session is unknown to me or has expired. The user must log in to their account with \`/user login\`.`); + return; + case "Nick already in use": + await interaction.editReply(`The nickname **${nick}** is already in use.`); + return; + case "Invalid nick": + await interaction.editReply(`The \`${nick}\` nickname is invalid, please use a valid nickname.`); + return; + default: + await interaction.editReply(`Unable to change nickname. There is a problem with the Gobattle API.\nContact ${client.application.owner} to resolve this issue.`); + } + return; + } + + await interaction.editReply(`The nickname has been changed to **${data?.nick}**.`); +} + async function get_friend_pending_count(interaction, client){ await interaction.deferReply(); diff --git a/src/utils.js b/src/utils.js index 3029adf..d4f585f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -92,7 +92,7 @@ function get_utc_date(){ return new Date(today.getTime() + (today.getTimezoneOffset() * 60000)); } -function get_utf_time_next_king(){ +function get_utc_time_next_king(){ const utc = get_utc_date(); const current_day = utc.getDay(); @@ -292,7 +292,7 @@ exports.get_level = get_level; exports.get_level_adventurer = get_level_adventurer; exports.get_level_to_emojis = get_level_to_emojis; exports.get_utc_date = get_utc_date; -exports.get_utf_time_next_king = get_utf_time_next_king; +exports.get_utc_time_next_king = get_utc_time_next_king; exports.sum = sum; exports.send_echo = send_echo; exports.get_first_chat_channel = get_first_chat_channel; From 10fa321353ef8d02563f7bfa9069f242e37c216b Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Fri, 23 Aug 2024 23:38:10 +0200 Subject: [PATCH 52/54] add user install in /info --- src/commands/info.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/commands/info.js b/src/commands/info.js index ebecd63..5a0a795 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,5 +1,5 @@ const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); -const {get_info_application, format_score_with_commas} = require("./../utils.js"); +const {get_info_application, format_score_with_commas, format_score} = require("./../utils.js"); const info_command = new SlashCommandBuilder(); info_command.setName("info"); @@ -16,7 +16,9 @@ async function get_info(interaction, client){ embed.setThumbnail(client.user.avatarURL()); const info = await get_info_application(client.application); + const approximate_guild_count = info?.approximate_guild_count || "_Unknown?_"; + const approximate_user_install_count = info?.approximate_user_install_count || "_Unknown?_"; const tos = info?.terms_of_service_url || "https://gobattle.io/tos.html"; const pp = info?.privacy_policy_url || "https://www.iubenda.com/privacy-policy/8108614"; const project_initiator_user_id = "461495109855215616"; // Please be courteous and do not change this value. @@ -24,7 +26,7 @@ async function get_info(interaction, client){ embed.addFields( {name: "> Official Gobattle.io Guild", value: "> [Link](https://discord.gg/gobattle-io-official-380588354934276097)", inline: true}, {name: "> Application Owner", value: `> ${client.application.owner}`, inline: true}, - {name: "> Approximate Guild count", value: `> ${typeof approximate_guild_count == "number" ? format_score_with_commas(approximate_guild_count) : approximate_guild_count}`, inline: true} + {name: "> Install Count", value: `> Servers: **${typeof approximate_guild_count == "number" ? format_score(approximate_guild_count) : approximate_guild_count}**\n> Individual Users: **${typeof approximate_user_install_count == "number" ? format_score(approximate_user_install_count) : approximate_user_install_count}**`, inline: true} ); embed.addFields( From b7dc475c1320d5d0d81b01b3e0aad59b26db82ee Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Fri, 23 Aug 2024 23:53:21 +0200 Subject: [PATCH 53/54] add new command (/install_link) --- src/commands/info.js | 2 +- src/commands/install_link.js | 13 +++++++++++++ src/index.js | 7 ++++++- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/commands/install_link.js diff --git a/src/commands/info.js b/src/commands/info.js index 5a0a795..ab4fd29 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,5 +1,5 @@ const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); -const {get_info_application, format_score_with_commas, format_score} = require("./../utils.js"); +const {get_info_application, format_score, format_score} = require("./../utils.js"); const info_command = new SlashCommandBuilder(); info_command.setName("info"); diff --git a/src/commands/install_link.js b/src/commands/install_link.js new file mode 100644 index 0000000..9a03522 --- /dev/null +++ b/src/commands/install_link.js @@ -0,0 +1,13 @@ +const {SlashCommandBuilder} = require("discord.js"); + +const install_link_command = new SlashCommandBuilder(); +install_link_command.setName("install_link"); +install_link_command.setDescription("Get the installation link of this application."); + +async function get_install_link(interaction, client){ + const install_url = client.application.customInstallURL || `https://discord.com/api/oauth2/authorize?client_id=${client.application.id}&permissions=18685255743552&scope=bot+applications.commands`; + await interaction.reply(`# Install me to get utility features related to the MMORPG GoBattle.io ā¤ļø: [Click here to install](${install_url}).`); +} + +exports.get_install_link = get_install_link; +exports.install_link_command = install_link_command; diff --git a/src/index.js b/src/index.js index 0976482..56e4225 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,7 @@ const {get_leave, leave_command} = require("./commands/leave.js"); const {get_setting, setting_command} = require("./commands/setting.js"); const {get_echo, echo_command} = require("./commands/echo.js"); const {get_ultrarare_drop_chance, ultrarare_drop_chance_command} = require("./commands/ultrarare_drop_chance.js"); +const {get_install_link, install_link_command} = require("./commands/install_link.js"); const global_commands = [ ranking_command, @@ -29,7 +30,8 @@ const global_commands = [ date_new_king_command, ping_command, leave_command, - ultrarare_drop_chance_command + ultrarare_drop_chance_command, + install_link_command ]; const private_commands = [ @@ -141,6 +143,9 @@ client.on(Events.InteractionCreate, async (interaction) => { case "get_ultrarare_drop_chance": await get_ultrarare_drop_chance(interaction, client); break; + case "install_link": + await get_install_link(interaction, client); + break; default: await interaction.reply({content: "This command no longer exists.", ephemeral: true}); } From ed7da2765bed869eb08a39cc60f737b3cd151073 Mon Sep 17 00:00:00 2001 From: Marzin-bot Date: Fri, 23 Aug 2024 23:55:54 +0200 Subject: [PATCH 54/54] fix error --- src/commands/info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/info.js b/src/commands/info.js index ab4fd29..1fd29d9 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,5 +1,5 @@ const {SlashCommandBuilder, EmbedBuilder} = require("discord.js"); -const {get_info_application, format_score, format_score} = require("./../utils.js"); +const {get_info_application, format_score} = require("./../utils.js"); const info_command = new SlashCommandBuilder(); info_command.setName("info");