ocr, translate + fixes

This commit is contained in:
artie 2025-02-09 19:09:01 +01:00
parent 82bfb87ae6
commit 4f5b2e54a7
22 changed files with 653 additions and 106 deletions

View File

@ -7,7 +7,9 @@
"@discordjs/core": "^2.0.1", "@discordjs/core": "^2.0.1",
"@sapphire/discord.js-utilities": "^7.3.2", "@sapphire/discord.js-utilities": "^7.3.2",
"cheerio": "^1.0.0", "cheerio": "^1.0.0",
"deepl-node": "^1.16.0",
"discord.js": "^14.17.3", "discord.js": "^14.17.3",
"execa": "^9.5.2",
"file-type": "^20.1.0", "file-type": "^20.1.0",
"ky": "^1.7.4", "ky": "^1.7.4",
"lru-cache": "^11.0.2", "lru-cache": "^11.0.2",
@ -133,6 +135,8 @@
"@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
"@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="],
"@toil/translate": ["@toil/translate@1.0.2", "", { "peerDependencies": { "typescript": "^5.6.2" } }, "sha512-awxtAuyhNpogagl20Ic/BV1XY68voGWU0w/5kzxlBN5sxUPrMtSqluDSPm4XvHRfji4v45r1p1NAxLISCQfhpA=="], "@toil/translate": ["@toil/translate@1.0.2", "", { "peerDependencies": { "typescript": "^5.6.2" } }, "sha512-awxtAuyhNpogagl20Ic/BV1XY68voGWU0w/5kzxlBN5sxUPrMtSqluDSPm4XvHRfji4v45r1p1NAxLISCQfhpA=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.6", "", { "dependencies": { "debug": "^4.3.7", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-SdR/i05U7Xhnsq36iyIq/ZiGGw4PKzw4ww3bOq80Pjj4wyXpqyTcgrgdDdGlcatnlvzNJx8CQw3hp6QZvkUwhA=="], "@tokenizer/inflate": ["@tokenizer/inflate@0.2.6", "", { "dependencies": { "debug": "^4.3.7", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-SdR/i05U7Xhnsq36iyIq/ZiGGw4PKzw4ww3bOq80Pjj4wyXpqyTcgrgdDdGlcatnlvzNJx8CQw3hp6QZvkUwhA=="],
@ -181,6 +185,10 @@
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"axios": ["axios@1.7.9", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
@ -211,6 +219,8 @@
"colorspace": ["colorspace@1.1.4", "", { "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w=="], "colorspace": ["colorspace@1.1.4", "", { "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
@ -223,6 +233,10 @@
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"deepl-node": ["deepl-node@1.16.0", "", { "dependencies": { "@types/node": ">=12.0", "axios": "^1.7.4", "form-data": "^3.0.0", "loglevel": ">=1.6.2" } }, "sha512-sYrh8UngVHKBrgffLPl+ng065mlC+Ep3LBdSN3xqDBd4KdBZzgvdxHiHd9Pm9xQCI5pwyuCHyq0cDUW1T3vQUg=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
"discord-api-types": ["discord-api-types@0.37.119", "", {}, "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg=="], "discord-api-types": ["discord-api-types@0.37.119", "", {}, "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg=="],
@ -261,7 +275,7 @@
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], "execa": ["execa@9.5.2", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.0", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" } }, "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
@ -277,6 +291,8 @@
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="], "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"file-type": ["file-type@20.1.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-XoxU+lETfCf+bYK3SXkxFusAvmtYQl1u/ZC4zw1DBLEsHUvh339uwYucgQnnSMz1mRCWYJrCzsbJJ95hsQbZ8A=="], "file-type": ["file-type@20.1.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-XoxU+lETfCf+bYK3SXkxFusAvmtYQl1u/ZC4zw1DBLEsHUvh339uwYucgQnnSMz1mRCWYJrCzsbJJ95hsQbZ8A=="],
@ -291,6 +307,10 @@
"fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="], "fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"form-data": ["form-data@3.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ=="],
"get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
@ -303,7 +323,7 @@
"htmlparser2": ["htmlparser2@9.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="], "htmlparser2": ["htmlparser2@9.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="],
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], "human-signals": ["human-signals@8.0.0", "", {}, "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
@ -329,7 +349,11 @@
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
"is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
"is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
@ -363,6 +387,8 @@
"logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="], "logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="],
"loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="],
"lru-cache": ["lru-cache@11.0.2", "", {}, "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA=="], "lru-cache": ["lru-cache@11.0.2", "", {}, "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA=="],
"magic-bytes.js": ["magic-bytes.js@1.10.0", "", {}, "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="], "magic-bytes.js": ["magic-bytes.js@1.10.0", "", {}, "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="],
@ -373,6 +399,10 @@
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
@ -383,7 +413,7 @@
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
@ -399,6 +429,8 @@
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
"parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="], "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
@ -415,6 +447,10 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"pretty-ms": ["pretty-ms@9.2.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
@ -449,7 +485,7 @@
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
@ -485,6 +521,8 @@
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
"unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
@ -507,6 +545,8 @@
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"yoctocolors": ["yoctocolors@2.1.1", "", {}, "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="],
"zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="], "zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
"@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
@ -521,32 +561,44 @@
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"axios/form-data": ["form-data@4.0.1", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="],
"clipboardy/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
"color-string/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "color-string/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"colorspace/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="], "colorspace/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="],
"discord.js/@discordjs/ws": ["@discordjs/ws@1.2.0", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.4.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.37.114", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-QH5CAFe3wHDiedbO+EI3OOiyipwWd+Q6BdoFZUw/Wf2fw5Cv2fgU/9UEtJRmJa9RecI+TAhdGPadMaEIur5yJg=="], "discord.js/@discordjs/ws": ["@discordjs/ws@1.2.0", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.4.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.37.114", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-QH5CAFe3wHDiedbO+EI3OOiyipwWd+Q6BdoFZUw/Wf2fw5Cv2fgU/9UEtJRmJa9RecI+TAhdGPadMaEIur5yJg=="],
"execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
"execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"get-stream/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"winston/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"ya-ocr/file-type": ["file-type@19.6.0", "", { "dependencies": { "get-stream": "^9.0.1", "strtok3": "^9.0.1", "token-types": "^6.0.0", "uint8array-extras": "^1.3.0" } }, "sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ=="], "ya-ocr/file-type": ["file-type@19.6.0", "", { "dependencies": { "get-stream": "^9.0.1", "strtok3": "^9.0.1", "token-types": "^6.0.0", "uint8array-extras": "^1.3.0" } }, "sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"clipboardy/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
"clipboardy/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
"clipboardy/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
"clipboardy/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
"clipboardy/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
"colorspace/color/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], "colorspace/color/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"discord.js/@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], "discord.js/@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
"ya-ocr/file-type/strtok3": ["strtok3@9.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.3.1" } }, "sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw=="], "ya-ocr/file-type/strtok3": ["strtok3@9.1.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.3.1" } }, "sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw=="],
"clipboardy/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], "colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"ya-ocr/file-type/strtok3/peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="], "ya-ocr/file-type/strtok3/peek-readable": ["peek-readable@5.4.2", "", {}, "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg=="],

View File

@ -13,7 +13,9 @@
"@discordjs/core": "^2.0.1", "@discordjs/core": "^2.0.1",
"@sapphire/discord.js-utilities": "^7.3.2", "@sapphire/discord.js-utilities": "^7.3.2",
"cheerio": "^1.0.0", "cheerio": "^1.0.0",
"deepl-node": "^1.16.0",
"discord.js": "^14.17.3", "discord.js": "^14.17.3",
"execa": "^9.5.2",
"file-type": "^20.1.0", "file-type": "^20.1.0",
"ky": "^1.7.4", "ky": "^1.7.4",
"lru-cache": "^11.0.2", "lru-cache": "^11.0.2",

View File

@ -1,5 +1,5 @@
import type { Command } from "../types/command"; import type { Command, CommandBuilder } from "../types/command";
export function defineCommand(command: Command) { export function defineCommand<B extends CommandBuilder>(command: Command<B>) {
return command; return command;
} }

View File

@ -0,0 +1,130 @@
import {
Attachment,
AutocompleteInteraction,
SlashCommandBuilder,
type InteractionEditReplyOptions,
} from "discord.js";
import { defineCommand } from "..";
import {
getSourceLanguages,
getTargetLanguages,
isSourceLanguageSupported,
isTargetLanguageSupported,
languageCodeToName,
translate,
} from "../../utils/deepl";
import { abort } from "../../utils/error";
export async function translateAutocompleteImpl(
interaction: AutocompleteInteraction
) {
const option = interaction.options.getFocused(true);
const languages =
option.name === "source"
? await getSourceLanguages()
: await getTargetLanguages();
const choices = languages
.filter((language) =>
language.name.toLowerCase().includes(option.value.toLowerCase())
)
.map((language) => ({
name: language.name,
value: language.code,
}))
.slice(0, 25);
await interaction.respond(choices);
}
export async function translateImpl(
text: string,
source: string | null,
target: string,
attachment?: Attachment
): Promise<InteractionEditReplyOptions> {
const {
text: translatedText,
detectedSourceLang,
billedCharacters,
} = await translate({
text,
source,
target,
});
const displaySource = await languageCodeToName(detectedSourceLang);
const displayTarget = await languageCodeToName(target);
if (translatedText.length > 4096) {
return {
files: [
{
name: `${displaySource}-${displayTarget}.txt`,
attachment: `--- From ${displaySource} to ${displayTarget} ---\n${translatedText}`,
},
...(attachment ? [attachment] : []),
],
};
}
return {
embeds: [
{
title: `From ${displaySource} to ${displayTarget}`,
description: translatedText,
color: 0x0f2b46,
author: {
name: "DeepL",
icon_url: "https://www.google.com/s2/favicons?domain=deepl.com&sz=64",
},
footer: {
text: `Billed characters: ${billedCharacters}`,
},
},
],
files: attachment ? [attachment] : [],
};
}
export default defineCommand({
data: new SlashCommandBuilder()
.setName("translate")
.setDescription("Translates text using DeepL")
.addStringOption((option) =>
option
.setName("text")
.setDescription("The text to translate")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("source")
.setDescription("Source language of the text")
.setAutocomplete(true)
)
.addStringOption((option) =>
option
.setName("target")
.setDescription("Target language of the text")
.setAutocomplete(true)
),
autocomplete: translateAutocompleteImpl,
async execute(interaction) {
const text = interaction.options.getString("text", true);
const source = interaction.options.getString("source") ?? null;
const target = interaction.options.getString("target") ?? "en-US";
await interaction.deferReply();
if (source && !(await isSourceLanguageSupported(source))) {
abort("Source language not supported");
}
if (target && !(await isTargetLanguageSupported(target))) {
abort("Target language not supported");
}
const payload = await translateImpl(text, source, target);
await interaction.editReply(payload);
},
});

View File

@ -0,0 +1,19 @@
import { ApplicationCommandType, ContextMenuCommandBuilder } from "discord.js";
import { defineCommand } from "..";
import { translateImpl } from "./translate";
export default defineCommand({
data: new ContextMenuCommandBuilder()
.setName("Translate to English")
.setType(ApplicationCommandType.Message),
async execute(interaction) {
if (!interaction.isMessageContextMenuCommand()) return;
const text = interaction.targetMessage.content;
await interaction.deferReply();
const payload = await translateImpl(text, null, "en-US");
await interaction.editReply(payload);
},
});

View File

@ -50,6 +50,8 @@ export default defineCommand({
async execute(interaction) { async execute(interaction) {
let term = interaction.options.getString("term", true); let term = interaction.options.getString("term", true);
await interaction.deferReply();
// autocomplete value vs user value // autocomplete value vs user value
if (term.startsWith(":")) { if (term.startsWith(":")) {
term = term.slice(1); term = term.slice(1);

87
src/commands/ocr/ocr.ts Normal file
View File

@ -0,0 +1,87 @@
import {
Attachment,
codeBlock,
inlineCode,
SlashCommandBuilder,
type InteractionEditReplyOptions,
} from "discord.js";
import { defineCommand } from "..";
import { downloadFile } from "../../utils/http";
import { abort } from "../../utils/error";
import { yandexOcr } from "../../utils/ocr";
import sharp from "sharp";
export function buildOcrPayload(
text: string,
detected_lang: string,
attachment?: Attachment
): InteractionEditReplyOptions {
const languageName =
new Intl.DisplayNames(["en"], { type: "language" }).of(detected_lang) ??
"unknown";
const content = `Detected language: ${inlineCode(languageName)}\n${codeBlock(
text
)}`;
if (content.length > 2000) {
return {
content: `Detected language: ${inlineCode(languageName)}`,
files: [
{
name: "ocr.txt",
attachment: text,
},
...(attachment ? [attachment] : []),
],
};
}
return {
content,
files: attachment ? [attachment] : [],
};
}
export async function ocrImpl(attachment: Attachment) {
const { data, type } = await downloadFile(attachment.url);
if (!type?.mime.startsWith("image/")) {
abort("The file must be an image!");
}
const compressed = await sharp(data)
.resize(1000)
.jpeg({ quality: 90 })
.toBuffer();
return yandexOcr(compressed, type.mime);
}
export default defineCommand({
data: new SlashCommandBuilder()
.setName("ocr")
.setDescription("OCR an image using Yandex")
.addAttachmentOption((option) =>
option
.setName("image")
.setDescription("The image to OCR")
.setRequired(true)
),
async execute(interaction) {
const attachment = interaction.options.getAttachment("image", true);
if (!attachment.contentType?.startsWith("image/")) {
abort("The file must be an image!");
}
await interaction.deferReply();
const result = await ocrImpl(attachment);
const payload = buildOcrPayload(
result.text,
result.detected_lang,
attachment
);
await interaction.editReply(payload);
},
});

View File

@ -0,0 +1,28 @@
import { ApplicationCommandType, ContextMenuCommandBuilder } from "discord.js";
import { defineCommand } from "..";
import { abort } from "../../utils/error";
import { buildOcrPayload, ocrImpl } from "./ocr";
export default defineCommand({
data: new ContextMenuCommandBuilder()
.setName("OCR")
.setType(ApplicationCommandType.Message),
async execute(interaction) {
if (!interaction.isMessageContextMenuCommand()) return;
const attachment = interaction.targetMessage.attachments.first();
if (!attachment) {
abort("No attachment found");
}
if (!attachment.contentType?.startsWith("image/")) {
abort("The file must be an image!");
}
await interaction.deferReply();
const result = await ocrImpl(attachment);
const payload = buildOcrPayload(result.text, result.detected_lang);
await interaction.editReply(payload);
},
});

View File

@ -0,0 +1,63 @@
import { SlashCommandBuilder } from "discord.js";
import { defineCommand } from "..";
import { abort } from "../../utils/error";
import {
isSourceLanguageSupported,
isTargetLanguageSupported,
} from "../../utils/deepl";
import {
translateAutocompleteImpl,
translateImpl,
} from "../language/translate";
import { ocrImpl } from "./ocr";
export default defineCommand({
data: new SlashCommandBuilder()
.setName("ocrtranslate")
.setDescription(
"OCR an image using Yandex and translate the result using DeepL"
)
.addAttachmentOption((option) =>
option
.setName("image")
.setDescription("The image to OCR")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("source")
.setDescription("Source language of the text")
.setAutocomplete(true)
)
.addStringOption((option) =>
option
.setName("target")
.setDescription("Target language of the text")
.setAutocomplete(true)
),
autocomplete: translateAutocompleteImpl,
async execute(interaction) {
const attachment = interaction.options.getAttachment("image", true);
const source = interaction.options.getString("source") ?? null;
const target = interaction.options.getString("target") ?? "en-US";
if (!attachment.contentType?.startsWith("image/")) {
abort("The file must be an image!");
}
await interaction.deferReply();
if (source && !(await isSourceLanguageSupported(source))) {
abort("Source language not supported");
}
if (target && !(await isTargetLanguageSupported(target))) {
abort("Target language not supported");
}
const { text } = await ocrImpl(attachment);
const payload = await translateImpl(text, source, target, attachment);
await interaction.editReply(payload);
},
});

View File

@ -0,0 +1,29 @@
import { ApplicationCommandType, ContextMenuCommandBuilder } from "discord.js";
import { defineCommand } from "..";
import { abort } from "../../utils/error";
import { translateImpl } from "../language/translate";
import { ocrImpl } from "./ocr";
export default defineCommand({
data: new ContextMenuCommandBuilder()
.setName("OCR and translate to English")
.setType(ApplicationCommandType.Message),
async execute(interaction) {
if (!interaction.isMessageContextMenuCommand()) return;
const attachment = interaction.targetMessage.attachments.first();
if (!attachment) {
abort("No attachment found");
}
if (!attachment.contentType?.startsWith("image/")) {
abort("The file must be an image!");
}
await interaction.deferReply();
const { text } = await ocrImpl(attachment);
const payload = await translateImpl(text, null, "en-US");
await interaction.editReply(payload);
},
});

View File

@ -14,6 +14,6 @@ export default defineCommand({
flags: MessageFlags.Ephemeral, flags: MessageFlags.Ephemeral,
}); });
await restart(interaction.token); await restart({ token: interaction.token });
}, },
}); });

View File

@ -0,0 +1,65 @@
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
codeBlock,
ComponentType,
SlashCommandBuilder,
} from "discord.js";
import { defineCommand } from "..";
import { restart } from "../../utils/restart";
import { shell } from "../../utils/functions";
export default defineCommand({
data: new SlashCommandBuilder()
.setName("update")
.setDescription("Updates the bot"),
isOwnerOnly: true,
async execute(interaction) {
const response = await interaction.deferReply({ withResponse: true });
const result = await shell`git pull`;
const output = result.stdout + result.stderr;
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("restart")
.setLabel("Restart")
.setStyle(ButtonStyle.Success)
);
const isUpToDate = output.trim() === "Already up to date.";
await interaction.editReply({
components: isUpToDate || result.failed ? [] : [row],
embeds: [
{
description: codeBlock(output),
color: isUpToDate ? 0x00ff00 : 0xff0000,
},
],
});
if (!isUpToDate && !result.failed) {
response.resource?.message
?.awaitMessageComponent({
componentType: ComponentType.Button,
time: 30000,
filter: (i) => i.user.id === interaction.user.id,
dispose: true,
})
.then(async (interaction) => {
await interaction.update({
components: [],
});
await interaction.message.react("🔄");
await restart({
message: {
id: interaction.message.id,
channelId: interaction.message.channelId,
},
});
});
}
},
});

View File

@ -1,66 +0,0 @@
import { codeBlock, inlineCode, SlashCommandBuilder } from "discord.js";
import { defineCommand } from "..";
import { downloadFile } from "../../utils/http";
import { abort } from "../../utils/error";
import { yandexOcr } from "../../utils/ocr";
import sharp from "sharp";
export default defineCommand({
data: new SlashCommandBuilder()
.setName("ocr")
.setDescription("OCR an image using Yandex")
.addAttachmentOption((option) =>
option
.setName("image")
.setDescription("The image to OCR")
.setRequired(true)
),
async execute(interaction) {
const attachment = interaction.options.getAttachment("image", true);
if (!attachment.contentType?.startsWith("image/")) {
abort("The file must be an image!");
}
await interaction.deferReply();
const { data, type } = await downloadFile(attachment.url);
if (!type?.mime.startsWith("image/")) {
abort("The file must be an image!");
}
const compressed = await sharp(data)
.resize(1000)
.jpeg({ quality: 90 })
.toBuffer();
const { text, detected_lang } = await yandexOcr(compressed, type.mime);
const languageName =
new Intl.DisplayNames(["en"], { type: "language" }).of(detected_lang) ??
"unknown";
const content = `Detected language: ${inlineCode(
languageName
)}\n${codeBlock(text)}`;
if (content.length > 2000) {
await interaction.editReply({
content: `Detected language: ${inlineCode(languageName)}`,
files: [
{
name: "ocr.txt",
attachment: text,
},
attachment,
],
});
return;
}
await interaction.editReply({
content,
files: [attachment],
});
},
});

View File

@ -10,6 +10,7 @@ const envSchema = z.object({
.default("development"), .default("development"),
DEV_GUILD_ID: z.string(), DEV_GUILD_ID: z.string(),
DEV_CHANNEL_ID: z.string(), DEV_CHANNEL_ID: z.string(),
DEEPL_API_KEY: z.string(),
}); });
export const env = envSchema.parse(process.env); export const env = envSchema.parse(process.env);

View File

@ -1,15 +1,24 @@
import { import {
AutocompleteInteraction, AutocompleteInteraction,
ChatInputCommandInteraction, ChatInputCommandInteraction,
ContextMenuCommandBuilder,
Events, Events,
inlineCode, inlineCode,
MessageContextMenuCommandInteraction,
MessageFlags, MessageFlags,
SlashCommandBuilder,
UserContextMenuCommandInteraction,
} from "discord.js"; } from "discord.js";
import { client } from "../client"; import { client } from "../client";
import { log } from "../utils/logger"; import { log } from "../utils/logger";
import { defineEvent } from "."; import { defineEvent } from ".";
import { isExplicitCommandError, notifyError } from "../utils/error"; import {
isCombinedError,
isExplicitCommandError,
notifyError,
} from "../utils/error";
import { nanoid } from "../utils/functions"; import { nanoid } from "../utils/functions";
import type { Command } from "../types/command";
const running = new Map<string, number>(); const running = new Map<string, number>();
const getRunning = (command: string) => running.get(command) ?? 0; const getRunning = (command: string) => running.get(command) ?? 0;
@ -18,7 +27,10 @@ export default defineEvent({
name: Events.InteractionCreate, name: Events.InteractionCreate,
async execute(interaction) { async execute(interaction) {
if (interaction.isChatInputCommand()) { if (
interaction.isChatInputCommand() ||
interaction.isContextMenuCommand()
) {
await handleChatInputCommand(interaction); await handleChatInputCommand(interaction);
} else if (interaction.isAutocomplete()) { } else if (interaction.isAutocomplete()) {
await handleAutocomplete(interaction); await handleAutocomplete(interaction);
@ -27,9 +39,14 @@ export default defineEvent({
}); });
async function handleChatInputCommand( async function handleChatInputCommand(
interaction: ChatInputCommandInteraction interaction:
| ChatInputCommandInteraction
| MessageContextMenuCommandInteraction
| UserContextMenuCommandInteraction
) { ) {
const command = client.commands.get(interaction.commandName); const command = client.commands.get(interaction.commandName) as Command<
SlashCommandBuilder | ContextMenuCommandBuilder
>;
if (!command) return; if (!command) return;
if (command.isOwnerOnly && interaction.user.id !== client.ownerId) { if (command.isOwnerOnly && interaction.user.id !== client.ownerId) {
@ -61,7 +78,13 @@ async function handleChatInputCommand(
if (!isExplicitCommandError(err)) { if (!isExplicitCommandError(err)) {
const trace = nanoid(); const trace = nanoid();
content += `\ntrace: ${inlineCode(trace)}`; content += `\ntrace: ${inlineCode(trace)}`;
log.error("Unhandled Command Error", { trace, err }); if (isCombinedError(err)) {
err.errors.forEach((error) => {
log.error("Unhandled Command Error", { trace, err: error });
});
} else {
log.error("Unhandled Command Error", { trace, err });
}
notifyError(trace, err); notifyError(trace, err);
} }

View File

@ -1,7 +1,8 @@
import { getDefinitions } from "../utils/wiktionary"; import { ArtemisClient } from "../client";
import { env } from "../env";
const definitions = await getDefinitions("appl"); const client = new ArtemisClient();
await client.api.applicationCommands.bulkOverwriteGlobalCommands(
if (!definitions) process.exit(1); env.APPLICATION_ID,
[]
console.dir(definitions, { depth: null }); );

View File

@ -1,13 +1,29 @@
import type { import type {
AutocompleteInteraction, AutocompleteInteraction,
ChatInputCommandInteraction, ChatInputCommandInteraction,
ContextMenuCommandBuilder,
MessageContextMenuCommandInteraction,
SlashCommandBuilder, SlashCommandBuilder,
SlashCommandOptionsOnlyBuilder, SlashCommandOptionsOnlyBuilder,
UserContextMenuCommandInteraction,
} from "discord.js"; } from "discord.js";
export interface Command { export type CommandBuilder =
data: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder; | SlashCommandBuilder
execute(interaction: ChatInputCommandInteraction): Promise<void>; | SlashCommandOptionsOnlyBuilder
| ContextMenuCommandBuilder;
type InferInteraction<B> = B extends
| SlashCommandBuilder
| SlashCommandOptionsOnlyBuilder
? ChatInputCommandInteraction
: B extends ContextMenuCommandBuilder
? MessageContextMenuCommandInteraction | UserContextMenuCommandInteraction
: never;
export interface Command<B extends CommandBuilder = SlashCommandBuilder> {
data: B;
execute(interaction: InferInteraction<B>): Promise<void>;
autocomplete?(interaction: AutocompleteInteraction): Promise<void>; autocomplete?(interaction: AutocompleteInteraction): Promise<void>;
category?: string; category?: string;
maxConcurrency?: number; maxConcurrency?: number;

View File

@ -43,6 +43,6 @@ export async function confirmPrompt(
}); });
}); });
msg.delete(); interaction.deleteReply();
return confirmation?.customId === "confirm"; return confirmation?.customId === "confirm";
} }

57
src/utils/deepl.ts Normal file
View File

@ -0,0 +1,57 @@
import {
Translator,
type SourceLanguageCode,
type TargetLanguageCode,
} from "deepl-node";
import { env } from "../env";
import { lazy } from "./functions";
const translator = new Translator(env.DEEPL_API_KEY);
export const getSourceLanguages = lazy(() => translator.getSourceLanguages());
export const getTargetLanguages = lazy(() => translator.getTargetLanguages());
type TranslateOptions = {
text: string;
source?: string | null;
target?: string;
};
export async function translate({
text,
source = null,
target = "en-US",
}: TranslateOptions) {
return translator.translateText(
text,
source as SourceLanguageCode,
target as TargetLanguageCode
);
}
export async function getUsage() {
return translator.getUsage();
}
export async function getLanguages() {
return (await getSourceLanguages()).concat(await getTargetLanguages());
}
export async function languageCodeToName(code: string) {
return (await getLanguages()).find((l) => l.code === code)?.name;
}
export async function isSourceLanguageSupported(code: string) {
return (
(await getSourceLanguages()).find(
(l) => l.code.toLowerCase() === code.toLowerCase()
) !== undefined
);
}
export async function isTargetLanguageSupported(code: string) {
return (
(await getTargetLanguages()).find(
(l) => l.code.toLowerCase() === code.toLowerCase()
) !== undefined
);
}

View File

@ -14,6 +14,10 @@ export function isExplicitCommandError(
return error instanceof ExplicitCommandError; return error instanceof ExplicitCommandError;
} }
export function isCombinedError(error: any): error is { errors: any[] } {
return typeof error === "object" && "errors" in error;
}
export async function notifyError(trace: string, error: any) { export async function notifyError(trace: string, error: any) {
return (client.channels.cache.get(env.DEV_CHANNEL_ID) as TextChannel).send({ return (client.channels.cache.get(env.DEV_CHANNEL_ID) as TextChannel).send({
content: trace, content: trace,

View File

@ -1,7 +1,9 @@
import * as cheerio from "cheerio"; import * as cheerio from "cheerio";
import { execa } from "execa";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
export const nanoid = customAlphabet("1234567890abcdef"); export const nanoid = customAlphabet("1234567890abcdef");
export const shell = execa({ reject: false });
export function noop() {} export function noop() {}
@ -51,3 +53,9 @@ export function dedent(parts: TemplateStringsArray, ...values: unknown[]) {
.join("") .join("")
.replace(/(\n)\s+/g, "$1"); .replace(/(\n)\s+/g, "$1");
} }
export function lazy<T>(cb: () => T) {
let defaultValue: T;
return () => (defaultValue ??= cb());
}

View File

@ -1,34 +1,60 @@
import { Client, InteractionWebhook, MessageFlags } from "discord.js"; import {
Client,
InteractionWebhook,
MessageFlags,
TextChannel,
} from "discord.js";
import { silently } from "./functions"; import { silently } from "./functions";
import { client } from "../client"; import { client } from "../client";
import { env } from "../env"; import { env } from "../env";
import { readFile, rm, writeFile } from "node:fs/promises"; import { readFile, rm, writeFile } from "node:fs/promises";
import { log } from "./logger"; import { log } from "./logger";
export async function restart(state: string) { type RestartState = {
token?: string;
message?: {
id: string;
channelId: string;
};
};
export async function restart(state: RestartState) {
log.info("Shutting down..."); log.info("Shutting down...");
await writeFile("./data/temp/restart", state); await writeFile("./data/temp/restart", JSON.stringify(state));
await client.destroy(); await client.destroy();
process.exit(0); process.exit(0);
} }
export async function maybeSendRestarted() { export async function maybeSendRestarted() {
const restartToken = await silently(readFile("./data/temp/restart", "utf-8")); const content = await silently(readFile("./data/temp/restart", "utf-8"));
if (!content) return;
const state = JSON.parse(content) as RestartState;
if (restartToken) { if (state.token) {
const webhook = new InteractionWebhook( const webhook = new InteractionWebhook(
client as Client<true>, client as Client<true>,
env.APPLICATION_ID, env.APPLICATION_ID,
restartToken state.token
); );
await webhook.send({ await silently(
content: "Successfully restarted!", webhook.send({
flags: MessageFlags.Ephemeral, content: "Successfully restarted!",
}); flags: MessageFlags.Ephemeral,
})
);
} else if (state.message) {
const channel = client.channels.cache.get(
state.message.channelId
) as TextChannel;
if (!channel) return;
await silently(rm("./data/temp/restart")); await silently(
channel.messages.fetch(state.message.id).then((msg) => msg.react("☑️"))
);
} }
await silently(rm("./data/temp/restart"));
} }