diff --git a/.env.example b/.env.example index c29d8d9cf..43a030f33 100644 --- a/.env.example +++ b/.env.example @@ -14,4 +14,4 @@ IMGUR_KEY=5i8rythbhtx654235hrs34 # WILDBEAST_SHARDING_START=0 # WILDBEAST_SHARDING_END=1 # WILDBEAST_SHARDING_TOTAL=2 -# WILDBEAST_LANGUAGE=en-EN +# WILDBEAST_LANGUAGE=en-US diff --git a/crowdin.yml b/crowdin.yml index 8d67013d2..9bdcd2c4d 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -2,5 +2,5 @@ project_id_env: CROWDIN_PROJECT_ID api_token_env: CROWDIN_PERSONAL_TOKEN files: - - source: /src/languages/en-EN.ts - translation: /src/languages/%locale%.ts \ No newline at end of file + - source: /src/languages/en-US/**/*.strings.ts + translation: /src/languages/%two_letters_code%/**/%original_file_name% \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 74d8ed48c..074d50dc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,15 +15,15 @@ "@thesharks/jagtag-js": "^2.0.0", "chalk": "^4.1.2", "date-fns": "^2.28.0", + "deepmerge": "^4.2.2", "detritus-client": "^0.17.0-beta.1", "dotenv": "^16.0.0", - "glob": "^7.2.0", + "fast-glob": "^3.2.11", "intl-messageformat": "^9.11.4", "postgres": "^2.0.0-beta.11", "tslib": "^2.3.1" }, "devDependencies": { - "@types/glob": "^7.2.0", "@types/node": "^17.0.18", "@typescript-eslint/eslint-plugin": "^5.12.0", "discord-api-types": "^0.27.1", @@ -294,7 +294,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -307,7 +306,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -316,7 +314,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -502,16 +499,6 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -524,12 +511,6 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, - "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, "node_modules/@types/node": { "version": "17.0.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", @@ -1000,12 +981,14 @@ "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==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "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==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1015,7 +998,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1090,7 +1072,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "node_modules/cookie": { "version": "0.4.1", @@ -1154,6 +1137,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2005,7 +1996,6 @@ "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2033,7 +2023,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -2054,7 +2043,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2109,7 +2097,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "node_modules/function-bind": { "version": "1.1.1", @@ -2157,6 +2146,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2176,7 +2166,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -2335,6 +2324,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2343,7 +2333,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/internal-slot": { "version": "1.0.3", @@ -2441,7 +2432,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2459,7 +2449,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2483,7 +2472,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -2708,7 +2696,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -2717,7 +2704,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" @@ -2749,6 +2735,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2849,6 +2836,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "dependencies": { "wrappy": "1" } @@ -2928,6 +2916,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2960,7 +2949,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3004,7 +2992,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -3071,7 +3058,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -3096,7 +3082,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -3346,7 +3331,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -3577,7 +3561,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "node_modules/ws": { "version": "7.5.6", @@ -3834,7 +3819,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3843,14 +3827,12 @@ "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -4023,16 +4005,6 @@ "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", "dev": true }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", @@ -4045,12 +4017,6 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, - "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, "@types/node": { "version": "17.0.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", @@ -4343,12 +4309,14 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4358,7 +4326,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -4412,7 +4379,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "cookie": { "version": "0.4.1", @@ -4455,6 +4423,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -5044,7 +5017,6 @@ "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5069,7 +5041,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -5087,7 +5058,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -5130,7 +5100,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "function-bind": { "version": "1.1.1", @@ -5169,6 +5140,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5182,7 +5154,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -5290,6 +5261,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5298,7 +5270,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "internal-slot": { "version": "1.0.3", @@ -5368,8 +5341,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -5381,7 +5353,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -5395,8 +5366,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-number-object": { "version": "1.0.6", @@ -5569,14 +5539,12 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.2.3" @@ -5599,6 +5567,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5667,6 +5636,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -5727,7 +5697,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-key": { "version": "3.1.1", @@ -5750,8 +5721,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "postgres": { "version": "2.0.0-beta.11", @@ -5779,8 +5749,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "regexpp": { "version": "3.2.0", @@ -5814,8 +5783,7 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, "rimraf": { "version": "3.0.2", @@ -5830,7 +5798,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "requires": { "queue-microtask": "^1.2.2" } @@ -6007,7 +5974,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -6176,7 +6142,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "ws": { "version": "7.5.6", diff --git a/package.json b/package.json index 327ae2bf4..baa139558 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ }, "homepage": "https://wildbeast.guide/", "devDependencies": { - "@types/glob": "^7.2.0", "@types/node": "^17.0.18", "@typescript-eslint/eslint-plugin": "^5.12.0", "discord-api-types": "^0.27.1", @@ -54,9 +53,10 @@ "@thesharks/jagtag-js": "^2.0.0", "chalk": "^4.1.2", "date-fns": "^2.28.0", + "deepmerge": "^4.2.2", "detritus-client": "^0.17.0-beta.1", "dotenv": "^16.0.0", - "glob": "^7.2.0", + "fast-glob": "^3.2.11", "intl-messageformat": "^9.11.4", "postgres": "^2.0.0-beta.11", "tslib": "^2.3.1" diff --git a/src/database/migrations/index.ts b/src/database/migrations/index.ts index a14eb9c40..97610996c 100644 --- a/src/database/migrations/index.ts +++ b/src/database/migrations/index.ts @@ -3,7 +3,7 @@ import { Row, RowList } from 'postgres' import SQL from '../driver' import { basename, extname, join, resolve } from 'path' import { writeFile } from 'fs/promises' -import { glob } from 'glob' +import glob from 'fast-glob' import { debug, error, info, trace } from '../../utils/logger' const IS_TS_NODE = Symbol.for('ts-node.register.instance') in process diff --git a/src/entry.ts b/src/entry.ts index d52fa69e5..f33d948ba 100644 --- a/src/entry.ts +++ b/src/entry.ts @@ -5,6 +5,7 @@ import { RewriteFrames } from '@sentry/integrations' import { promisify } from 'util' import { exec } from 'child_process' import dirImport from './utils/dir-import' +import initLangs from './languages' info('Starting up...', 'Preflight'); @@ -25,7 +26,7 @@ info('Starting up...', 'Preflight'); }, release: revision }) - await dirImport('@(dist|src)/languages/**/*.[?jt]s') + await initLangs() await dirImport('@(dist|src)/events/**/*.[?jt]s') await client.addMultipleIn('./interactions') await client.run() diff --git a/src/interactions/base.ts b/src/interactions/base.ts index 403aceac8..8f98d0c75 100644 --- a/src/interactions/base.ts +++ b/src/interactions/base.ts @@ -5,6 +5,101 @@ import { error } from '../utils/logger' const { ApplicationCommandTypes, ApplicationCommandOptionTypes, MessageFlags, Permissions } = Constants export class BaseInteractionCommand extends Interaction.InteractionCommand { + translate: typeof translate = translate + translateThis: typeof translate = (key, args) => + this.translate(`slash-commands.${this.name.toLowerCase()}.${key}`, args) + + async onBefore (context: Interaction.InteractionContext): Promise { + if (context.locale !== undefined) { + this.translate = (key, args) => translate(key, args, context.locale) + } + return true + } + + async onSuccess (context: Interaction.InteractionContext, args: ParsedArgsFinished): Promise { + add({ + type: 'command', + guildId: context.guildId, + inDm: context.inDm, + userId: context.user.id, + data: { + args, + interaction_id: context.interactionId + }, + name: context.command.name + }) + } + + async onDmBlocked (context: Interaction.InteractionContext): Promise { + return await context.editOrRespond({ + content: this.translate('common.dmDisabled'), + flags: MessageFlags.EPHEMERAL + }) + } + + async onPermissionsFail (context: Interaction.InteractionContext, falied: bigint[]): Promise { + const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value))) + await context.editOrRespond({ + content: this.translate('common.permsMissingUser', { + perms: values.map(([key]) => key).join(', ') + }), + flags: MessageFlags.EPHEMERAL + }) + } + + async onPermissionsFailClient (context: Interaction.InteractionContext, falied: bigint[]): Promise { + const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value))) + await context.editOrRespond({ + content: this.translate('common.permsMissingOwn', { + perms: values.map(([key]) => key).join(', ') + }), + flags: MessageFlags.EPHEMERAL + }) + } + + async onRatelimit (context: Interaction.InteractionContext): Promise { + await context.editOrRespond({ + content: this.translate('common.cooldown'), + flags: MessageFlags.EPHEMERAL + }) + } + + async onRunError (context: Interaction.InteractionContext, args: ParsedArgsFinished, err: any): Promise { + const uuid = error(err, context.name, { + user: { + id: context.user.id, + username: context.user.username + }, + contexts: { + guild: { + id: context.guildId, + name: context.guild?.name + }, + command: { + name: context.command.name, + args + } + } + }) + return await context.editOrRespond({ + content: translate('common.failedToRun', { uuid }), + flags: MessageFlags.EPHEMERAL + }) + } +} + +class OptionBase extends Interaction.InteractionCommandOption { + translate: typeof translate = translate + translateThis: typeof translate = (key, args) => + this.translate(`context-menu.${this.name.toLowerCase()}.${key}`, args) + + async onBefore (context: Interaction.InteractionContext): Promise { + if (context.locale !== undefined) { + this.translate = (key, args) => translate(key, args, context.locale) + } + return true + } + async onSuccess (context: Interaction.InteractionContext, args: ParsedArgsFinished): Promise { add({ type: 'command', @@ -21,7 +116,7 @@ export class BaseInteractionCommand async onDmBlocked (context: Interaction.InteractionContext): Promise { return await context.editOrRespond({ - content: translate('commands.common.dmDisabled'), + content: this.translate('common.dmDisabled'), flags: MessageFlags.EPHEMERAL }) } @@ -29,9 +124,9 @@ export class BaseInteractionCommand async onPermissionsFail (context: Interaction.InteractionContext, falied: bigint[]): Promise { const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value))) await context.editOrRespond({ - content: - "You're missing the following permissions for this command to work:\n" + - values.map(([key, value]) => `\`${key}\``).join('\n'), + content: this.translate('common.permsMissingUser', { + perms: values.map(([key]) => key).join(', ') + }), flags: MessageFlags.EPHEMERAL }) } @@ -39,16 +134,16 @@ export class BaseInteractionCommand async onPermissionsFailClient (context: Interaction.InteractionContext, falied: bigint[]): Promise { const values = Object.entries(Permissions).filter(([key, value]) => falied.includes(BigInt(value))) await context.editOrRespond({ - content: - "I'm missing the following permissions for this command to work:\n" + - values.map(([key, value]) => `\`${key}\``).join('\n'), + content: this.translate('common.permsMissingOwn', { + perms: values.map(([key]) => key).join(', ') + }), flags: MessageFlags.EPHEMERAL }) } async onRatelimit (context: Interaction.InteractionContext): Promise { await context.editOrRespond({ - content: 'You are doing that too much, please wait a bit.', + content: this.translate('common.cooldown'), flags: MessageFlags.EPHEMERAL }) } @@ -71,17 +166,17 @@ export class BaseInteractionCommand } }) return await context.editOrRespond({ - content: translate('commands.common.failedToRun', { uuid }), + content: translate('common.failedToRun', { uuid }), flags: MessageFlags.EPHEMERAL }) } } -export class BaseCommandOption extends Interaction.InteractionCommandOption { +export class BaseCommandOption extends OptionBase { type = ApplicationCommandOptionTypes.SUB_COMMAND } -export class BaseCommandOptionGroup extends Interaction.InteractionCommandOption { +export class BaseCommandOptionGroup extends OptionBase { type = ApplicationCommandOptionTypes.SUB_COMMAND_GROUP } diff --git a/src/interactions/slash-commands/8ball.ts b/src/interactions/slash-commands/8ball.ts index 92e461693..0e6e0466f 100644 --- a/src/interactions/slash-commands/8ball.ts +++ b/src/interactions/slash-commands/8ball.ts @@ -1,14 +1,14 @@ import { Interaction } from 'detritus-client' -import { translate, traverse } from '../../utils/i18n' +import { traverse } from '../../utils/i18n' import { BaseSlashCommand } from '../base' export default class EightBallCommand extends BaseSlashCommand { - description = 'Ask the magic 8-ball for advice' name = '8ball' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext): Promise { - const length = traverse('commands.8ball.choices.length') - await context.editOrRespond(translate('commands.8ball.prefix', { response: translate(`commands.8ball.choices.${Math.floor(Math.random() * length)}`) })) + const length = traverse('slash-commands.8ball.choices.length') + await context.editOrRespond(this.translateThis('prefix', { response: this.translateThis(`choices.${Math.floor(Math.random() * length)}`) })) } } diff --git a/src/interactions/slash-commands/advice.ts b/src/interactions/slash-commands/advice.ts index 7c2945b79..7bdeaa04d 100644 --- a/src/interactions/slash-commands/advice.ts +++ b/src/interactions/slash-commands/advice.ts @@ -4,8 +4,8 @@ import fetch from 'node-fetch' import { BaseSlashCommand } from '../base' export default class AdviceCommand extends BaseSlashCommand { - description = 'Get some helpful advice' name = 'advice' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext): Promise { const advice = await (await fetch('https://api.adviceslip.com/advice')).json() diff --git a/src/interactions/slash-commands/booru/booru.derpibooru.ts b/src/interactions/slash-commands/booru/booru.derpibooru.ts index 8a9f6b306..409d0c929 100644 --- a/src/interactions/slash-commands/booru/booru.derpibooru.ts +++ b/src/interactions/slash-commands/booru/booru.derpibooru.ts @@ -22,7 +22,7 @@ export class BooruDerpibooruCommand extends BaseCommandOption { async onBeforeRun (context: Interaction.InteractionContext): Promise { if (!context.inDm && !context.channel!.nsfw) { await context.editOrRespond({ - content: translate('commands.common.nsfwDisabled'), + content: translate('common.nsfwDisabled'), flags: MessageFlags.EPHEMERAL }) return false @@ -34,7 +34,7 @@ export class BooruDerpibooruCommand extends BaseCommandOption { options: [ { name: 'query', - description: 'What to search for', + description: translate('slash-commands.booru.metadata.options.query'), required: true } ] @@ -53,14 +53,14 @@ export class BooruDerpibooruCommand extends BaseCommandOption { } })).json() if (json.total === 0) { - await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query })) + await context.editOrRespond(translate('common.noResultsFor', { query: args.query })) } else { const post = json.images[position] const artist: string = post.tags.filter((x: string) => x.startsWith('artist:'))[0].slice('artist:'.length) ?? 'Unknown' const embed = new Embed() .setAuthor(artist, 'https://i.imgur.com/f556NmB.png', post.source_url) .setImage(post.representations.full) - .addField('Score', `${post.upvotes as string} 👍 ${post.downvotes as string} 👎`, true) + .addField(this.translateThis('score'), `${post.upvotes as string} 👍 ${post.downvotes as string} 👎`, true) .setFooter('derpibooru.org', 'https://i.imgur.com/f556NmB.png') const components = new Components({ timeout: 5 * (60 * 1000), @@ -89,7 +89,7 @@ export class BooruDerpibooruCommand extends BaseCommandOption { // workaround: detritus sets customIds even when its not needed const urlButton = new ComponentButton({ style: MessageComponentButtonStyles.LINK, - label: translate('commands.common.open'), + label: translate('common.open'), url: `https://derpibooru.org/images/${post.id as string}` }) delete urlButton.customId diff --git a/src/interactions/slash-commands/booru/booru.e621.ts b/src/interactions/slash-commands/booru/booru.e621.ts index 93110a077..fc3f0fe0f 100644 --- a/src/interactions/slash-commands/booru/booru.e621.ts +++ b/src/interactions/slash-commands/booru/booru.e621.ts @@ -24,7 +24,7 @@ export class BooruE621Command extends BaseCommandOption { options: [ { name: 'query', - description: 'What to search for', + description: translate('slash-commands.booru.metadata.options.query'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const chunks = context.value.split(' ') @@ -63,15 +63,15 @@ export class BooruE621Command extends BaseCommandOption { } })).json() if (json.posts.length === 0) { - await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query })) + await context.editOrRespond(translate('common.noResultsFor', { query: args.query })) } else { const post = json.posts[position] const artist: string = post.tags.artist.filter((x: string) => !['conditional_dnp'].includes(x))[0] ?? 'Unknown' const embed = new Embed() .setAuthor(artist, 'https://en.wikifur.com/w/images/d/dd/E621Logo.png', artist === 'Unknown' ? undefined : `https://e621.net/artists/show_or_new?name=${encodeURIComponent(artist)}`) .setImage(post.file.url) - .addField('Score', `${post.score.up as string} 👍 ${post.score.down as string} 👎`, true) - .addField('Favorites', post.fav_count, true) + .addField(this.translateThis('score'), `${post.score.up as string} 👍 ${post.score.down as string} 👎`, true) + .addField(this.translateThis('favorites'), post.fav_count, true) if (context.channel !== null && !context.channel.nsfw) embed.setFooter('e926.net - NSFW disabled', 'https://en.wikifur.com/w/images/d/dd/E621Logo.png') else embed.setFooter('e621.net', 'https://en.wikifur.com/w/images/d/dd/E621Logo.png') const components = new Components({ @@ -101,7 +101,7 @@ export class BooruE621Command extends BaseCommandOption { // workaround: detritus sets customIds even when its not needed const urlButton = new ComponentButton({ style: MessageComponentButtonStyles.LINK, - label: translate('commands.common.open'), + label: translate('common.open'), url: `https://e621.net/posts/${post.id as string}` }) delete urlButton.customId diff --git a/src/interactions/slash-commands/booru/booru.gelbooru.ts b/src/interactions/slash-commands/booru/booru.gelbooru.ts index 7e963a2dc..d255616a7 100644 --- a/src/interactions/slash-commands/booru/booru.gelbooru.ts +++ b/src/interactions/slash-commands/booru/booru.gelbooru.ts @@ -22,7 +22,7 @@ export class BooruGelbooruCommand extends BaseCommandOption { async onBeforeRun (context: Interaction.InteractionContext): Promise { if (!context.inDm && !context.channel!.nsfw) { await context.editOrRespond({ - content: translate('commands.common.nsfwDisabled'), + content: translate('common.nsfwDisabled'), flags: MessageFlags.EPHEMERAL }) return false @@ -34,7 +34,7 @@ export class BooruGelbooruCommand extends BaseCommandOption { options: [ { name: 'query', - description: 'What to search for', + description: translate('slash-commands.booru.metadata.options.query'), required: true } ] @@ -58,12 +58,12 @@ export class BooruGelbooruCommand extends BaseCommandOption { } })).json() if (json.length === 0) { - await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query })) + await context.editOrRespond(translate('common.noResultsFor', { query: args.query })) } else { const post = json[position] const embed = new Embed() .setImage(post.file_url) - .addField('Score', post.score, true) + .addField(this.translateThis('score'), post.score, true) .setFooter('gelbooru.com') const components = new Components({ timeout: 5 * (60 * 1000), @@ -92,7 +92,7 @@ export class BooruGelbooruCommand extends BaseCommandOption { // workaround: detritus sets customIds even when its not needed const urlButton = new ComponentButton({ style: MessageComponentButtonStyles.LINK, - label: translate('commands.common.open'), + label: translate('common.open'), url: `https://gelbooru.com/index.php?page=post&s=view&id=${post.id as string}` }) delete urlButton.customId diff --git a/src/interactions/slash-commands/booru/booru.rule34.ts b/src/interactions/slash-commands/booru/booru.rule34.ts index 1bf0173fc..576dfb978 100644 --- a/src/interactions/slash-commands/booru/booru.rule34.ts +++ b/src/interactions/slash-commands/booru/booru.rule34.ts @@ -22,7 +22,7 @@ export class BooruRule34Command extends BaseCommandOption { async onBeforeRun (context: Interaction.InteractionContext): Promise { if (!context.inDm && !context.channel!.nsfw) { await context.editOrRespond({ - content: translate('commands.common.nsfwDisabled'), + content: translate('common.nsfwDisabled'), flags: MessageFlags.EPHEMERAL }) return false @@ -34,7 +34,7 @@ export class BooruRule34Command extends BaseCommandOption { options: [ { name: 'query', - description: 'What to search for', + description: translate('slash-commands.booru.metadata.options.query'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const chunks = context.value.split(' ') @@ -78,7 +78,7 @@ export class BooruRule34Command extends BaseCommandOption { }) const text = await http.clone().text() if (text.length < 1) { - await context.editOrRespond(translate('commands.common.noResultsFor', { query: args.query })) + await context.editOrRespond(translate('common.noResultsFor', { query: args.query })) return } else { data = await http.json() @@ -87,7 +87,7 @@ export class BooruRule34Command extends BaseCommandOption { const post = data[position] const embed = new Embed() .setImage(post.file_url) - .addField('Score', post.score, true) + .addField(this.translateThis('score'), post.score, true) .setFooter('rule34.xxx') const components = new Components({ timeout: 5 * (60 * 1000), @@ -116,7 +116,7 @@ export class BooruRule34Command extends BaseCommandOption { // workaround: detritus sets customIds even when its not needed const urlButton = new ComponentButton({ style: MessageComponentButtonStyles.LINK, - label: translate('commands.common.open'), + label: translate('common.open'), url: `https://rule34.xxx/index.php?page=post&s=view&id=${post.id as string}` }) delete urlButton.customId diff --git a/src/interactions/slash-commands/cat.ts b/src/interactions/slash-commands/cat.ts index 1c3bd257c..fe6910601 100644 --- a/src/interactions/slash-commands/cat.ts +++ b/src/interactions/slash-commands/cat.ts @@ -6,7 +6,7 @@ import { BaseSlashCommand } from '../base' export default class RandomCatCommand extends BaseSlashCommand { name = 'cat' - description = 'Sends a random cat image' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext | ComponentContext): Promise { const components = new Components({ @@ -23,7 +23,7 @@ export default class RandomCatCommand extends BaseSlashCommand { const embed = new Embed() .setDescription(fact) .setImage(`https://cataas.com/cat?${context.interaction.id}`) // cache busting - .setFooter('Powered by cataas.com') + .setFooter('cataas.com') await context.editOrRespond({ embed, components diff --git a/src/interactions/slash-commands/dice.ts b/src/interactions/slash-commands/dice.ts index 5966b0b7b..1965601c1 100644 --- a/src/interactions/slash-commands/dice.ts +++ b/src/interactions/slash-commands/dice.ts @@ -1,5 +1,6 @@ import { Interaction } from 'detritus-client' import { ApplicationCommandOptionTypes } from 'detritus-client/lib/constants' +import { translate } from '../../utils/i18n' import { BaseSlashCommand } from '../base' @@ -9,8 +10,8 @@ export interface CommandArgs { } export default class DiceCommand extends BaseSlashCommand { - description = 'Roll some dice' name = 'dice' + description = this.translateThis('metadata.description') constructor () { super({ @@ -18,13 +19,13 @@ export default class DiceCommand extends BaseSlashCommand { { type: ApplicationCommandOptionTypes.INTEGER, name: 'dice', - description: 'The number of dice to roll (default: 1)', + description: translate('slash-commands.dice.metadata.options.dice'), required: false }, { type: ApplicationCommandOptionTypes.INTEGER, name: 'sides', - description: 'The number of sides on the dice (default: 6)', + description: translate('slash-commands.dice.metadata.options.sides'), required: false } ] @@ -41,6 +42,11 @@ export default class DiceCommand extends BaseSlashCommand { total += Math.floor(Math.random() * diceSides) + 1 } - await context.editOrRespond(`${context.user.username} rolled ${diceCount}d${diceSides} and got ${total}`) + await context.editOrRespond(this.translateThis('response', { + username: context.user.username, + diceCount, + diceSides, + total + })) } } diff --git a/src/interactions/slash-commands/dog.ts b/src/interactions/slash-commands/dog.ts index d3610553d..590602ff4 100644 --- a/src/interactions/slash-commands/dog.ts +++ b/src/interactions/slash-commands/dog.ts @@ -6,7 +6,7 @@ import { BaseSlashCommand } from '../base' export default class RandomDogCommand extends BaseSlashCommand { name = 'dog' - description = 'Sends a random dog image' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext | ComponentContext): Promise { const components = new Components({ diff --git a/src/interactions/slash-commands/info.ts b/src/interactions/slash-commands/info.ts index b3f1059df..471e16f01 100644 --- a/src/interactions/slash-commands/info.ts +++ b/src/interactions/slash-commands/info.ts @@ -1,6 +1,5 @@ import { Interaction } from 'detritus-client' import { Embed } from 'detritus-client/lib/utils' -import { translate } from '../../utils/i18n' import { BaseSlashCommand } from '../base' @@ -11,8 +10,8 @@ import { MessageFlags } from 'detritus-client/lib/constants' const { version } = require('../../../package.json') export default class InfoCommand extends BaseSlashCommand { - description = 'Get information about the bot' name = 'info' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext): Promise { let owner @@ -24,19 +23,19 @@ export default class InfoCommand extends BaseSlashCommand { const uptime = sub(new Date(), { seconds: process.uptime() }) const embed = new Embed() .setTitle('Info') - .addField(translate('commands.info.guilds'), `${context.client.guilds.size}`, true) - .addField(translate('commands.info.uptime'), ``, true) - .addField(translate('commands.info.shard'), `${context.client.shardId}/${context.client.shardCount}`, true) - .addField(translate('commands.info.owner'), `${owner.username}#${owner.discriminator}`, true) - .addField(translate('commands.info.version'), `v${version as string}`, true) - .addField(translate('commands.info.node'), `${process.version}`, true) - .addField(translate('commands.info.os'), `${process.platform}`, true) - .addField(translate('commands.info.ram'), `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`, true) - .addField(translate('commands.info.cpu'), `${Math.round(process.cpuUsage().user / 1000 / 1000)}%`, true) + .addField(this.translateThis('guilds'), `${context.client.guilds.size}`, true) + .addField(this.translateThis('uptime'), ``, true) + .addField(this.translateThis('shard'), `${context.client.shardId}/${context.client.shardCount}`, true) + .addField(this.translateThis('owner'), `${owner.username}#${owner.discriminator}`, true) + .addField(this.translateThis('version'), `v${version as string}`, true) + .addField(this.translateThis('node'), `${process.version}`, true) + .addField(this.translateThis('os'), `${process.platform}`, true) + .addField(this.translateThis('ram'), `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`, true) + .addField(this.translateThis('cpu'), `${Math.round(process.cpuUsage().user / 1000 / 1000)}%`, true) .setColor(0x00AE86) .setThumbnail(context.client.user!.avatarUrl) - .setFooter(`${context.client.user!.username} - ${translate('commands.info.poweredBy')}`) - await context.editOrRespond( { + .setFooter(`${context.client.user!.username} - ${this.translateThis('poweredBy')}`) + await context.editOrRespond({ embed, flags: MessageFlags.EPHEMERAL }) diff --git a/src/interactions/slash-commands/inspire.ts b/src/interactions/slash-commands/inspire.ts index a1da607ce..b9a34b254 100644 --- a/src/interactions/slash-commands/inspire.ts +++ b/src/interactions/slash-commands/inspire.ts @@ -8,19 +8,19 @@ import { BaseSlashCommand } from '../base' export default class InspirobotCommand extends BaseSlashCommand { name = 'inspire' - description = 'Get a random quote from inspirobot.com' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext): Promise { const url = await (await fetch('https://inspirobot.me/api?generate=true')).text() try { const ctx = new URL(url) - await context.editOrRespond( { + await context.editOrRespond({ embed: { image: { url: ctx.href }, footer: { - text: 'Powered by inspirobot.me', + text: 'inspirobot.me', iconUrl: 'https://inspirobot.me/website/images/inspirobot-dark-green.png' }, color: 0x1a6607 @@ -29,7 +29,7 @@ export default class InspirobotCommand extends BaseSlashCommand { } catch (e) { error(e, this.constructor.name) await context.editOrRespond({ - content: translate('commands.common.softFail'), + content: translate('common.softFail'), flags: MessageFlags.EPHEMERAL }) } diff --git a/src/interactions/slash-commands/invite.ts b/src/interactions/slash-commands/invite.ts index 4f80c95ac..ebb5c92cf 100644 --- a/src/interactions/slash-commands/invite.ts +++ b/src/interactions/slash-commands/invite.ts @@ -1,17 +1,16 @@ import { Interaction } from 'detritus-client' import { MessageFlags } from 'detritus-client/lib/constants' -import { translate } from '../../utils/i18n' import { BaseSlashCommand } from '../base' export default class InviteCommand extends BaseSlashCommand { - description = 'Get an invite link for the bot' name = 'invite' + description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext): Promise { if (process.env.WILDBEAST_INVITE_OVERRIDE !== undefined) { - await context.editOrRespond( { - content: translate('commands.invite.done', { + await context.editOrRespond({ + content: this.translateThis('done', { invite: process.env.WILDBEAST_INVITE_OVERRIDE }), flags: MessageFlags.EPHEMERAL @@ -21,24 +20,24 @@ export default class InviteCommand extends BaseSlashCommand { if (!context.client.application!.botPublic) { if (context.client.application!.team !== undefined) { const teamowner = context.client.application!.team.owner! - await context.editOrRespond( { - content: translate('commands.invite.private', { + await context.editOrRespond({ + content: this.translateThis('private', { owner: `${teamowner.username}#${teamowner.discriminator}` }), flags: MessageFlags.EPHEMERAL }) } else { const owner = context.client.application!.owner - await context.editOrRespond( { - content: translate('commands.invite.private', { + await context.editOrRespond({ + content: this.translateThis('private', { owner: `${owner.username}#${owner.discriminator}` }), flags: MessageFlags.EPHEMERAL }) } } else { - await context.editOrRespond( { - content: translate('commands.invite.done', { + await context.editOrRespond({ + content: this.translateThis('done', { invite: `https://discordapp.com/oauth2/authorize?&client_id=${context.client.application!.id}&scope=bot%20applications.commands` }), flags: MessageFlags.EPHEMERAL diff --git a/src/interactions/slash-commands/meme.ts b/src/interactions/slash-commands/meme.ts index 6d9fb0ede..8f0a7e42a 100644 --- a/src/interactions/slash-commands/meme.ts +++ b/src/interactions/slash-commands/meme.ts @@ -9,7 +9,7 @@ import { BaseSlashCommand } from '../base' export default class RandomMemeCommand extends BaseSlashCommand { name = 'meme' - description = 'Get a random meme' + description = this.translateThis('metadata.description') triggerLoadingAfter = 2000 async run (context: Interaction.InteractionContext | ComponentContext): Promise { @@ -38,7 +38,7 @@ export default class RandomMemeCommand extends BaseSlashCommand { // workaround: detritus sets customIds even when its not needed const urlButton = new ComponentButton({ style: MessageComponentButtonStyles.LINK, - label: translate('commands.common.open'), + label: translate('common.open'), url: `https://reddit.com${post.permalink as string}` }) delete urlButton.customId diff --git a/src/interactions/slash-commands/ping.ts b/src/interactions/slash-commands/ping.ts index 4e18117e3..9d6ad1d05 100644 --- a/src/interactions/slash-commands/ping.ts +++ b/src/interactions/slash-commands/ping.ts @@ -3,8 +3,8 @@ import { Interaction } from 'detritus-client' import { BaseSlashCommand } from '../base' export default class PingCommand extends BaseSlashCommand { - description = 'Ping' name = 'ping' + // description = this.translateThis('metadata.description') async run (context: Interaction.InteractionContext): Promise { const { gateway, rest } = await context.client.ping() diff --git a/src/interactions/slash-commands/tag/tag.create.ts b/src/interactions/slash-commands/tag/tag.create.ts index 58aa029b7..a0271fcef 100644 --- a/src/interactions/slash-commands/tag/tag.create.ts +++ b/src/interactions/slash-commands/tag/tag.create.ts @@ -13,7 +13,7 @@ export interface CommandArgs { export class CreateTagCommand extends BaseCommandOption { name = 'create' - description = 'Create a new tag' + description = this.translateThis('metadata.descriptions.create') disableDm = true constructor () { @@ -21,12 +21,12 @@ export class CreateTagCommand extends BaseCommandOption { options: [ { name: 'name', - description: 'The name of the tag', + description: translate('slash-commands.tag.metadata.options.name'), required: true }, { name: 'content', - description: 'The content of the tag', + description: translate('slash-commands.tag.metadata.options.content'), required: true } ] @@ -37,7 +37,7 @@ export class CreateTagCommand extends BaseCommandOption { try { await driver`INSERT INTO tags (name, content, owner, guild) VALUES (${args.name}, ${args.content}, ${context.userId}, ${context.guildId!})` await context.editOrRespond({ - content: translate('commands.tag.created'), + content: this.translateThis('created'), embed: { title: args.name, description: args.content, @@ -49,14 +49,14 @@ export class CreateTagCommand extends BaseCommandOption { if (e instanceof PostgresError) { if (e.code === '23505') { await context.editOrRespond({ - content: translate('commands.tag.errors.conflict'), + content: this.translateThis('errors.conflict'), flags: MessageFlags.EPHEMERAL }) } } else { error(e, this.constructor.name) await context.editOrRespond({ - content: translate('commands.common.softFail'), + content: translate('common.softFail'), flags: MessageFlags.EPHEMERAL }) } diff --git a/src/interactions/slash-commands/tag/tag.delete.ts b/src/interactions/slash-commands/tag/tag.delete.ts index de611d0de..45c356855 100644 --- a/src/interactions/slash-commands/tag/tag.delete.ts +++ b/src/interactions/slash-commands/tag/tag.delete.ts @@ -12,7 +12,7 @@ export interface CommandArgs { export class DeleteTagCommand extends BaseCommandOption { name = 'delete' - description = 'Delete a tag you own' + description = this.translateThis('metadata.descriptions.delete') disableDm = true constructor () { @@ -20,7 +20,7 @@ export class DeleteTagCommand extends BaseCommandOption { options: [ { name: 'name', - description: 'The name of the tag', + description: translate('slash-commands.tag.metadata.options.name'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const search = `${context.value}%` @@ -37,7 +37,7 @@ export class DeleteTagCommand extends BaseCommandOption { const tag = await driver`SELECT name FROM tags WHERE name = ${args.name} AND owner = ${context.userId} AND guild = ${context.guildId!}` as Tag[] if (tag.length === 0) { await context.editOrRespond({ - content: translate('commands.tag.errors.notFound'), + content: this.translateThis('errors.notFound'), flags: MessageFlags.EPHEMERAL }) return false @@ -48,13 +48,13 @@ export class DeleteTagCommand extends BaseCommandOption { try { await driver`DELETE FROM tags WHERE name = ${args.name} AND owner = ${context.userId} AND guild = ${context.guildId!}` await context.editOrRespond({ - content: translate('commands.tag.deleted'), + content: this.translateThis('deleted'), flags: MessageFlags.EPHEMERAL }) } catch (e) { error(e, this.constructor.name) await context.editOrRespond({ - content: translate('commands.common.softFail'), + content: translate('common.softFail'), flags: MessageFlags.EPHEMERAL }) } diff --git a/src/interactions/slash-commands/tag/tag.edit.ts b/src/interactions/slash-commands/tag/tag.edit.ts index 5117b491d..678b838e8 100644 --- a/src/interactions/slash-commands/tag/tag.edit.ts +++ b/src/interactions/slash-commands/tag/tag.edit.ts @@ -13,7 +13,7 @@ export interface CommandArgs { export class EditTagCommand extends BaseCommandOption { name = 'edit' - description = 'Edit a tag you own' + description = this.translateThis('metadata.descriptions.edit') disableDm = true constructor () { @@ -21,7 +21,7 @@ export class EditTagCommand extends BaseCommandOption { options: [ { name: 'name', - description: 'The name of the tag', + description: translate('slash-commands.tag.metadata.options.name'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const search = `${context.value}%` @@ -32,7 +32,7 @@ export class EditTagCommand extends BaseCommandOption { }, { name: 'content', - description: 'The content of the tag', + description: translate('slash-commands.tag.metadata.options.content'), required: true } ] @@ -43,7 +43,7 @@ export class EditTagCommand extends BaseCommandOption { const tag = await driver`SELECT name FROM tags WHERE name = ${args.name} AND owner = ${context.userId} AND guild = ${context.guildId!}` as Tag[] if (tag.length === 0) { await context.editOrRespond({ - content: translate('commands.tag.errors.notFound'), + content: this.translateThis('errors.notFound'), flags: MessageFlags.EPHEMERAL }) return false @@ -54,7 +54,7 @@ export class EditTagCommand extends BaseCommandOption { try { await driver`UPDATE tags SET content = ${args.content}, updated_at = NOW() WHERE name = ${args.name} AND owner = ${context.userId} AND guild = ${context.guildId!}` await context.editOrRespond({ - content: translate('commands.tag.edited'), + content: this.translateThis('edited'), embed: { title: args.name, description: args.content, @@ -65,7 +65,7 @@ export class EditTagCommand extends BaseCommandOption { } catch (e) { error(e, this.constructor.name) await context.editOrRespond({ - content: translate('commands.common.softFail'), + content: translate('common.softFail'), flags: MessageFlags.EPHEMERAL }) } diff --git a/src/interactions/slash-commands/tag/tag.info.ts b/src/interactions/slash-commands/tag/tag.info.ts index 73bd53c38..e417cd0e8 100644 --- a/src/interactions/slash-commands/tag/tag.info.ts +++ b/src/interactions/slash-commands/tag/tag.info.ts @@ -13,7 +13,7 @@ export interface CommandArgs { export class TagInfoCommand extends BaseCommandOption { name = 'info' - description = 'Show info about a tag' + description = this.translateThis('metadata.descriptions.info') disableDm = true constructor () { @@ -21,7 +21,7 @@ export class TagInfoCommand extends BaseCommandOption { options: [ { name: 'name', - description: 'The name of the tag', + description: translate('slash-commands.tag.metadata.options.name'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const search = `${context.value}%` @@ -38,7 +38,7 @@ export class TagInfoCommand extends BaseCommandOption { const tag = await driver`SELECT name FROM tags WHERE name = ${args.name} AND guild = ${context.guildId!}` if (tag.length === 0) { await context.editOrRespond({ - content: translate('commands.tag.errors.notFound'), + content: this.translateThis('errors.notFound'), flags: MessageFlags.EPHEMERAL }) return false @@ -61,17 +61,17 @@ export class TagInfoCommand extends BaseCommandOption { }, fields: [ { - name: 'Created At', + name: this.translateThis('created'), value: ``, inline: true }, { - name: 'Updated At', + name: this.translateThis('updated'), value: ``, inline: true }, { - name: 'Ranking', + name: this.translateThis('ranking'), value: `Used ${tag.uses} times, ${ranking.indexOf(args.name) + 1}/${ranking.length}`, inline: true } @@ -81,7 +81,7 @@ export class TagInfoCommand extends BaseCommandOption { } catch (e) { error(e, this.constructor.name) await context.editOrRespond({ - content: translate('commands.common.softFail'), + content: translate('common.softFail'), flags: MessageFlags.EPHEMERAL }) } diff --git a/src/interactions/slash-commands/tag/tag.show.ts b/src/interactions/slash-commands/tag/tag.show.ts index f1625023c..b8ca43f71 100644 --- a/src/interactions/slash-commands/tag/tag.show.ts +++ b/src/interactions/slash-commands/tag/tag.show.ts @@ -14,7 +14,7 @@ export interface CommandArgs { export class ShowTagCommand extends BaseCommandOption { name = 'show' - description = 'Show a tag' + description = this.translateThis('metadata.descriptions.show') disableDm = true constructor () { @@ -22,7 +22,7 @@ export class ShowTagCommand extends BaseCommandOption { options: [ { name: 'name', - description: 'The name of the tag', + description: translate('slash-commands.tag.metadata.options.name'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const search = `${context.value}%` @@ -33,7 +33,7 @@ export class ShowTagCommand extends BaseCommandOption { }, { name: 'args', - description: 'Arguments to pass to the tag', + description: translate('slash-commands.tag.metadata.options.args'), required: false } ] @@ -44,7 +44,7 @@ export class ShowTagCommand extends BaseCommandOption { const tag = await driver`SELECT name FROM tags WHERE name = ${args.name} AND guild = ${context.guildId!}` as Tag[] if (tag.length === 0) { await context.editOrRespond({ - content: translate('commands.tag.errors.notFound'), + content: this.translateThis('errors.notFound'), flags: MessageFlags.EPHEMERAL }) return false @@ -67,7 +67,7 @@ export class ShowTagCommand extends BaseCommandOption { } catch (e) { error(e, this.constructor.name) await context.editOrRespond({ - content: translate('commands.common.softFail'), + content: translate('common.softFail'), flags: MessageFlags.EPHEMERAL }) } diff --git a/src/interactions/slash-commands/urbandictionary.ts b/src/interactions/slash-commands/urbandictionary.ts index 429dc9466..50ea9cebc 100644 --- a/src/interactions/slash-commands/urbandictionary.ts +++ b/src/interactions/slash-commands/urbandictionary.ts @@ -12,15 +12,15 @@ export interface CommandArgs { } export default class UrbanDictionaryCommand extends BaseSlashCommand { - description = 'Search Urban Dictionary' name = 'urbandictionary' + description = this.translateThis('metadata.description') constructor () { super({ options: [ { name: 'query', - description: 'What to search for', + description: translate('slash-commands.urbandictionary.metadata.options.query'), required: true, async onAutoComplete (context: Interaction.InteractionAutoCompleteContext): Promise { const hits = await (await fetch(`https://api.urbandictionary.com/v0/autocomplete?term=${context.value}`, { @@ -44,7 +44,7 @@ export default class UrbanDictionaryCommand extends BaseSlashCommand { } })).json() if (json.list.length === 0) { - await context.editOrRespond(translate('commands.urbandictionary.errors.notFound')) + await context.editOrRespond(this.translateThis('errors.notFound')) } else { const post = json.list[position] const embed = new Embed() @@ -53,7 +53,7 @@ export default class UrbanDictionaryCommand extends BaseSlashCommand { .setTitle(post.word) .setUrl(post.permalink) .setDescription(stylize(post.definition)) - .addField(translate('commands.urbandictionary.example'), post.example?.length > 0 ? stylize(post.example) : translate('commands.urbandictionary.noExample')) + .addField(this.translateThis('example'), post.example?.length > 0 ? stylize(post.example) : this.translateThis('noExample')) .addField('👍', post.thumbs_up, true) .addField('👎', post.thumbs_down, true) const components = new Components({ @@ -83,7 +83,7 @@ export default class UrbanDictionaryCommand extends BaseSlashCommand { // workaround: detritus sets customIds even when its not needed const urlButton = new ComponentButton({ style: MessageComponentButtonStyles.LINK, - label: translate('commands.common.open'), + label: translate('common.open'), url: post.permalink }) delete urlButton.customId diff --git a/src/languages/en-EN.ts b/src/languages/en-EN.ts deleted file mode 100644 index c00bfb81a..000000000 --- a/src/languages/en-EN.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { languages } from '../cache' - -languages.set('en-EN', - { - i18n: { - disclaimer: 'Please note: translations are provided by the community, and we cannot guarantee their correctness, completeness, or quality', - cta: 'Do you speak fluent English and want to help us translate? Check out {url}' - }, - prereqs: { - errors: { - masterUser: 'This command is only for the bot owner', - serverOwner: 'You must be the server owner to run this command' - } - }, - commands: { - common: { - failedToRun: 'Yikes! This command failed, please give my owner this error code: `{uuid}`', - softFail: 'Something went wrong, try again later', - attachmentNeeded: 'Please upload an image while using this command', - nsfwDisabled: 'This channel needs to be marked as NSFW before this command can be used', - cooldown: 'This command is on cooldown, try again later', - dmDisabled: 'This command cannot be used in DMs', - permsMissingOwn: "I'm missing the following permissions: `{perms}`", - permsMissingUser: "You're missing the following permissions: `{perms}`", - working: 'Working on it...', - noResults: 'No results found', - noResultsFor: 'No results found for `{query}`', - open: 'Open' - }, - dice: { - resultMany: 'You rolled **{result}**\n```{explaination}```', - resultSingle: 'You rolled **{result}**', - resultTooLarge: '[result too large to show explaination]', - badSyntax: 'Please specify how many dice you want to roll like this: `4d6`, `2d12`' - }, - info: { - guilds: 'Guilds on this shard', - uptime: 'Uptime', - shard: 'Current shard', - version: 'Version', - owner: 'Owner', - node: 'Node.js version', - ram: 'Memory usage', - os: 'Operating system', - cpu: 'CPU usage', - poweredBy: 'Powered by WildBeast' - }, - invite: { - done: 'Use the following link to invite me: {invite}', - private: 'This bot is marked as private, please ask {owner} to invite me to your server' - }, - tag: { - errors: { - notFound: 'No such tag', - conflict: 'A tag with that name already exists', - illegal: "You can't name your tag that", - notYours: "You don't own that tag, so you can't edit it" - }, - owner: 'The owner of that tag is {user}', - created: 'Your tag was created', - deleted: 'Your tag was deleted', - edited: 'Your tag was edited' - }, - kick: { - noMentions: "Please provide IDs or mention users you'd like to kick", - noResults: "Couldn't find those users in the server", - done: 'Kicked {num} members', - failed: 'Failed to kick {num} members' - }, - ban: { - noMentions: "Please provide IDs or mention users you'd like to ban", - noResults: "Couldn't find those users in the server", - done: 'Banned {num} members', - failed: 'Failed to ban {num} members' - }, - softban: { - noMentions: "Please provide IDs or mention users you'd like to softban", - noResults: "Couldn't find those users in the server", - done: 'Softbanned {num} members', - failed: 'Failed to softban {num} members' - }, - purge: { - notANumber: 'Your last argument must be a number', - tooManyOrFew: "You're trying to remove too {num, select, few {few} many {many}} messages", - noResults: 'I was not able to find any messages for purging that are under two weeks old' - }, - urbandictionary: { - errors: { - notFound: "This word is so screwed up, even Urban Dictionary doesn't know it" - }, - example: 'Example', - noExample: '[no example provided]' - }, - '8ball': { - prefix: 'The magic 8 ball says: `{response}`', - choices: [ - 'Signs point to yes', - 'Yes', - 'Reply hazy, try again', - 'Without a doubt', - 'My sources say no', - 'As I see it, yes', - 'You may rely on it', - 'Concentrate and ask again', - 'Outlook not so good', - 'It is decidedly so', - 'Better not tell you now', - 'Very doubtful', - 'Yes - definitely', - 'It is certain', - 'Cannot predict now', - 'Most likely', - 'Ask again later', - 'My reply is no', - 'Outlook good', - "Don't count on it", - 'Who cares?', - 'Never, ever, ever', - 'Possibly', - 'There is a small chance' - ] - } - } - } -) diff --git a/src/languages/en-US/common.strings.ts b/src/languages/en-US/common.strings.ts new file mode 100644 index 000000000..d3060ac26 --- /dev/null +++ b/src/languages/en-US/common.strings.ts @@ -0,0 +1,15 @@ +export default +{ + failedToRun: 'Yikes! This command failed, please give my owner this error code: `{uuid}`', + softFail: 'Something went wrong, try again later', + attachmentNeeded: 'Please upload an image while using this command', + nsfwDisabled: 'This channel needs to be marked as NSFW before this command can be used', + cooldown: 'This command is on cooldown, try again later', + dmDisabled: 'This command cannot be used in DMs', + permsMissingOwn: "I'm missing the following permissions: `{perms}`", + permsMissingUser: "You're missing the following permissions: `{perms}`", + working: 'Working on it...', + noResults: 'No results found', + noResultsFor: 'No results found for `{query}`', + open: 'Open' +} diff --git a/src/languages/en-US/slash-commands/8ball.strings.ts b/src/languages/en-US/slash-commands/8ball.strings.ts new file mode 100644 index 000000000..4d9aef7a3 --- /dev/null +++ b/src/languages/en-US/slash-commands/8ball.strings.ts @@ -0,0 +1,33 @@ +export default +{ + metadata: { + description: 'Ask the magic 8-ball for advice' + }, + prefix: 'The magic 8 ball says: `{response}`', + choices: [ + 'Signs point to yes', + 'Yes', + 'Reply hazy, try again', + 'Without a doubt', + 'My sources say no', + 'As I see it, yes', + 'You may rely on it', + 'Concentrate and ask again', + 'Outlook not so good', + 'It is decidedly so', + 'Better not tell you now', + 'Very doubtful', + 'Yes - definitely', + 'It is certain', + 'Cannot predict now', + 'Most likely', + 'Ask again later', + 'My reply is no', + 'Outlook good', + "Don't count on it", + 'Who cares?', + 'Never, ever, ever', + 'Possibly', + 'There is a small chance' + ] +} diff --git a/src/languages/en-US/slash-commands/advice.strings.ts b/src/languages/en-US/slash-commands/advice.strings.ts new file mode 100644 index 000000000..f77a161c1 --- /dev/null +++ b/src/languages/en-US/slash-commands/advice.strings.ts @@ -0,0 +1,6 @@ +export default +{ + metadata: { + description: 'Get some helpful advice' + } +} diff --git a/src/languages/en-US/slash-commands/booru.strings.ts b/src/languages/en-US/slash-commands/booru.strings.ts new file mode 100644 index 000000000..111345059 --- /dev/null +++ b/src/languages/en-US/slash-commands/booru.strings.ts @@ -0,0 +1,10 @@ +export default +{ + metadata: { + options: { + query: 'What to search for' + } + }, + score: 'Score', + favorites: 'Favorites' +} diff --git a/src/languages/en-US/slash-commands/cat.strings.ts b/src/languages/en-US/slash-commands/cat.strings.ts new file mode 100644 index 000000000..ac35c07ac --- /dev/null +++ b/src/languages/en-US/slash-commands/cat.strings.ts @@ -0,0 +1,6 @@ +export default +{ + metadata: { + description: 'Sends a random cat image' + } +} diff --git a/src/languages/en-US/slash-commands/dice.strings.ts b/src/languages/en-US/slash-commands/dice.strings.ts new file mode 100644 index 000000000..40517ab48 --- /dev/null +++ b/src/languages/en-US/slash-commands/dice.strings.ts @@ -0,0 +1,11 @@ +export default +{ + metadata: { + description: 'Roll some dice', + options: { + dice: 'The number of dice to roll (default: 1)', + sides: 'The number of sides on the dice (default: 6)' + } + }, + response: '{username} rolled {diceCount}d{diceSides} and got {total}' +} diff --git a/src/languages/en-US/slash-commands/dog.strings.ts b/src/languages/en-US/slash-commands/dog.strings.ts new file mode 100644 index 000000000..58b302d69 --- /dev/null +++ b/src/languages/en-US/slash-commands/dog.strings.ts @@ -0,0 +1,6 @@ +export default +{ + metadata: { + description: 'Sends a random dog image' + } +} diff --git a/src/languages/en-US/slash-commands/info.strings.ts b/src/languages/en-US/slash-commands/info.strings.ts new file mode 100644 index 000000000..ec504418e --- /dev/null +++ b/src/languages/en-US/slash-commands/info.strings.ts @@ -0,0 +1,16 @@ +export default +{ + metadata: { + description: 'Get information about the bot' + }, + guilds: 'Guilds on this shard', + uptime: 'Uptime', + shard: 'Current shard', + version: 'Version', + owner: 'Owner', + node: 'Node.js version', + ram: 'Memory usage', + os: 'Operating system', + cpu: 'CPU usage', + poweredBy: 'Powered by WildBeast' +} diff --git a/src/languages/en-US/slash-commands/inspire.strings.ts b/src/languages/en-US/slash-commands/inspire.strings.ts new file mode 100644 index 000000000..11f907ea7 --- /dev/null +++ b/src/languages/en-US/slash-commands/inspire.strings.ts @@ -0,0 +1,6 @@ +export default +{ + metadata: { + description: 'Get a random quote from inspirobot.com' + } +} diff --git a/src/languages/en-US/slash-commands/invite.strings.ts b/src/languages/en-US/slash-commands/invite.strings.ts new file mode 100644 index 000000000..810b52279 --- /dev/null +++ b/src/languages/en-US/slash-commands/invite.strings.ts @@ -0,0 +1,8 @@ +export default +{ + metadata: { + description: 'Get an invite link for the bot' + }, + done: 'Use the following link to invite me: {invite}', + private: 'This bot is marked as private, please ask {owner} to invite me to your server' +} diff --git a/src/languages/en-US/slash-commands/meme.strings.ts b/src/languages/en-US/slash-commands/meme.strings.ts new file mode 100644 index 000000000..a823b79ba --- /dev/null +++ b/src/languages/en-US/slash-commands/meme.strings.ts @@ -0,0 +1,6 @@ +export default +{ + metadata: { + description: 'Get a random meme' + } +} diff --git a/src/languages/en-US/slash-commands/ping.strings.ts b/src/languages/en-US/slash-commands/ping.strings.ts new file mode 100644 index 000000000..c9199677b --- /dev/null +++ b/src/languages/en-US/slash-commands/ping.strings.ts @@ -0,0 +1,4 @@ +export default +{ + response: 'Pong! (gateway: {gateway}ms) (rest: {rest}ms)' +} diff --git a/src/languages/en-US/slash-commands/tag.strings.ts b/src/languages/en-US/slash-commands/tag.strings.ts new file mode 100644 index 000000000..aa6cf9cc3 --- /dev/null +++ b/src/languages/en-US/slash-commands/tag.strings.ts @@ -0,0 +1,30 @@ +export default +{ + metadata: { + descriptions: { + create: 'Create a new tag', + delete: 'Delete a tag', + edit: 'Edit a tag', + info: 'Get information about a tag', + show: 'Show a tag' + }, + options: { + name: 'The name of the tag', + content: 'The content of the tag', + args: 'The arguments to pass to the command' + } + }, + errors: { + notFound: 'No such tag', + conflict: 'A tag with that name already exists', + illegal: "You can't name your tag that", + notYours: "You don't own that tag, so you can't edit it" + }, + owner: 'The owner of that tag is {user}', + created: 'Your tag was created', + deleted: 'Your tag was deleted', + edited: 'Your tag was edited', + createdAt: 'Created At', + updatedAt: 'Updated At', + ranking: 'Ranking' +} diff --git a/src/languages/en-US/slash-commands/urbandictionary.strings.ts b/src/languages/en-US/slash-commands/urbandictionary.strings.ts new file mode 100644 index 000000000..5523e9e1b --- /dev/null +++ b/src/languages/en-US/slash-commands/urbandictionary.strings.ts @@ -0,0 +1,14 @@ +export default +{ + metadata: { + description: 'Search Urban Dictionary', + options: { + query: 'What to search for' + } + }, + errors: { + notFound: "This word is so screwed up, even Urban Dictionary doesn't know it" + }, + example: 'Example', + noExample: '[no example provided]' +} diff --git a/src/languages/index.ts b/src/languages/index.ts new file mode 100644 index 000000000..d2ec2956f --- /dev/null +++ b/src/languages/index.ts @@ -0,0 +1,28 @@ +import deepmerge from 'deepmerge' +import glob from 'fast-glob' +import { basename, extname, relative, sep } from 'path' +import { languages } from '../cache' +import { debug } from '../utils/logger' + +export default async function (): Promise { + const files = await glob('**/*.strings.[?jt]s', { + absolute: true, + cwd: __dirname + }) + for (const file of files) { + const name = basename(file, `.strings${extname(file)}`) + const relativePath = relative(__dirname, file) + const [language, ...categories] = relativePath.split(sep).slice(0, -1) + const strings = await import(file) + // input: ['a', 'b', 'c'] + // output: { a: { b: { c: 'foo' } } } + const obj = categories.reduce((o, c) => ({ [c]: o }), { [name]: strings.default }) + if (languages.has(language)) { + const original = languages.get(language) + languages.set(language, deepmerge(original!, obj)) + } else { + languages.set(language, obj) + } + debug(`Language ${language} updated for ${categories.join('.')}.${name} with ${Object.keys(strings.default).length} definitions`, 'Languages') + } +} diff --git a/src/utils/dir-import.ts b/src/utils/dir-import.ts index 4cdfa731b..aa9a7737f 100644 --- a/src/utils/dir-import.ts +++ b/src/utils/dir-import.ts @@ -1,4 +1,4 @@ -import { sync } from 'glob' +import { sync } from 'fast-glob' const IS_TS_NODE = Symbol.for('ts-node.register.instance') in process diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts index 434746986..319e5b084 100644 --- a/src/utils/i18n.ts +++ b/src/utils/i18n.ts @@ -1,13 +1,17 @@ import { IntlMessageFormat } from 'intl-messageformat' -import { error, fatal } from './logger' +import { error, fatal, warn } from './logger' import { languages } from '../cache' -const defaultLang = process.env.WILDBEAST_LANGUAGE ?? 'en-EN' +const defaultLang = process.env.WILDBEAST_LANGUAGE ?? 'en-US' export function translate (key: string, args?: Record, lang?: string): string { if (!languages.has(defaultLang)) { fatal(`Default language ${defaultLang} not found in cache!`, 'i18n') } + if (lang !== undefined && !languages.has(lang)) { + warn(`A translation call requested language ${lang}, but it's not found in cache. Defaulting back to ${defaultLang}`, 'i18n') + lang = defaultLang + } try { const msg = traverse(key, lang) if (!(typeof msg === 'string')) error(`Translation key ${key} not found in cache!`, 'i18n')