From f43869032d6f92603168d10444905c9f05100d12 Mon Sep 17 00:00:00 2001 From: svifty7 Date: Thu, 30 Nov 2023 01:45:18 +0300 Subject: [PATCH] feat: spell conversation; fix: dice render --- package-lock.json | 1197 +++++++++++++++++++++++++++++------- package.json | 8 +- src/commands/tools/dice.ts | 10 +- src/commands/wiki/spell.ts | 378 +++++++++--- src/scenes/SpellScenes.ts | 264 -------- src/types/base.d.ts | 5 + src/types/gfm.d.ts | 11 + src/types/spell.d.ts | 2 + src/utils/useConfig.ts | 4 +- src/utils/useDiceRoller.ts | 134 ++-- src/utils/useHelpers.ts | 15 +- src/utils/useJSDom.ts | 45 ++ src/utils/useMarkup.ts | 188 ++++++ 13 files changed, 1599 insertions(+), 662 deletions(-) delete mode 100644 src/scenes/SpellScenes.ts create mode 100644 src/types/gfm.d.ts create mode 100644 src/utils/useJSDom.ts create mode 100644 src/utils/useMarkup.ts diff --git a/package-lock.json b/package-lock.json index 3800346..385b83f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,11 @@ "dice-roller-parser": "^0.1.8", "dotenv": "^16.0.3", "grammy": "^1.19.2", + "jsdom": "^23.0.0", "lodash-es": "^4.17.21", - "string-strip-html": "^11.6.20" + "sanitize-html": "^2.11.0", + "turndown": "^7.1.2", + "turndown-plugin-gfm": "^1.0.2" }, "devDependencies": { "@grammyjs/hydrate": "^1.3.1", @@ -27,8 +30,11 @@ "@rushstack/eslint-patch": "^1.4.0", "@types/cors": "^2.8.12", "@types/express": "^4.17.14", + "@types/jsdom": "^21.1.6", "@types/lodash-es": "^4.17.9", "@types/node": "^18.13.0", + "@types/sanitize-html": "^2.9.5", + "@types/turndown": "^5.0.4", "@typescript-eslint/eslint-plugin": "5.62.0", "@typescript-eslint/parser": "5.62.0", "eslint": "^8.31.0", @@ -486,6 +492,17 @@ "@types/range-parser": "*" } }, + "node_modules/@types/jsdom": { + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.6.tgz", + "integrity": "sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -546,6 +563,15 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "node_modules/@types/sanitize-html": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.9.5.tgz", + "integrity": "sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==", + "dev": true, + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, "node_modules/@types/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", @@ -562,6 +588,18 @@ "@types/node": "*" } }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/turndown": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.4.tgz", + "integrity": "sha512-28GI33lCCkU4SGH1GvjDhFgOVr+Tym4PXGBIU1buJUa6xQolniPArtUT+kv42RR2N9MsMLInkr904Aq+ESHBJg==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -788,6 +826,17 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1247,6 +1296,60 @@ "node": ">= 8" } }, + "node_modules/cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1263,12 +1366,25 @@ } } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/default-browser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", @@ -1431,6 +1547,62 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -1444,6 +1616,17 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1550,7 +1733,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -2524,10 +2706,34 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } }, "node_modules/http-errors": { "version": "2.0.0", @@ -2544,6 +2750,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/husky": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", @@ -2855,6 +3085,19 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3008,6 +3251,76 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.0.0.tgz", + "integrity": "sha512-cbL/UCtohJguhFC7c2/hgW6BeZCNvP7URQGnx9tSJRYKCdnfbfWOrtuLTMfiB2VxKsx5wPHVsh/J0aBy9lIIhQ==", + "dependencies": { + "cssstyle": "^3.0.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.14.2", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -3080,32 +3393,12 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.trim": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", - "integrity": "sha1-NkJefukL5KpeJ7zruFt9EepHqlc=" - }, - "node_modules/lodash.without": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz", - "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=" - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3235,6 +3528,23 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3490,6 +3800,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, "node_modules/o-son": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/o-son/-/o-son-1.0.2.tgz", @@ -3729,6 +4044,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3808,8 +4139,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3844,6 +4174,33 @@ "node": ">=4" } }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3885,11 +4242,15 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } @@ -3908,6 +4269,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3928,50 +4294,6 @@ } ] }, - "node_modules/ranges-apply": { - "version": "6.2.12", - "resolved": "https://registry.npmjs.org/ranges-apply/-/ranges-apply-6.2.12.tgz", - "integrity": "sha512-kvbXY6BNDpEAri/PlDZHlDyEAFvbAlfHGcIPQ6lg2BCifepx0t7knULy0ryvtsDuYCutK2ZVp+XTCMWcfaf/AQ==", - "dependencies": { - "ranges-merge": "^8.2.7", - "tiny-invariant": "^1.3.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/ranges-merge": { - "version": "8.2.7", - "resolved": "https://registry.npmjs.org/ranges-merge/-/ranges-merge-8.2.7.tgz", - "integrity": "sha512-ymJfok6pW4vndF8wy6SeqnLGu5GH8k+MM+W4fAqif79HGNloNzwE0ijVmuRd8D7ulJnwo5BGzwpZpXYHYeLlMQ==", - "dependencies": { - "ranges-push": "^6.2.7", - "ranges-sort": "^5.1.6" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/ranges-push": { - "version": "6.2.7", - "resolved": "https://registry.npmjs.org/ranges-push/-/ranges-push-6.2.7.tgz", - "integrity": "sha512-OX12airFLYK4/6dh3b82NRtGzpXyOWTmjiJzD/oMvzgZ2cfNbsa5qKLtf9AwZO6cVOpS2Li1l2mKTVjlM6MMpg==", - "dependencies": { - "string-collapse-leading-whitespace": "^6.1.7", - "string-trim-spaces-only": "^4.1.6" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/ranges-sort": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/ranges-sort/-/ranges-sort-5.1.6.tgz", - "integrity": "sha512-/Mlg0Oe5iwu2lxeZGG/zzz92rQ6LpKimMx9uBj4EQthSSUDpvNeQzMthhdDBi/NGhG29rIWGpgl9jUHomUmX3Q==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/raw-body": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", @@ -4083,6 +4405,11 @@ "jsesc": "bin/jsesc" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -4137,6 +4464,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -4310,6 +4642,30 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sanitize-html": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz", + "integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -4416,6 +4772,14 @@ "node": ">=8" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -4456,51 +4820,6 @@ "node": ">= 0.8" } }, - "node_modules/string-collapse-leading-whitespace": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/string-collapse-leading-whitespace/-/string-collapse-leading-whitespace-6.1.7.tgz", - "integrity": "sha512-RGpJrs/C31mPyBPvmjcM16joFYaywAoJ8L9pWY/xFegkykFdMrg569ragafoJZgv485hEwWjihD5VgwJftrRmw==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/string-left-right": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/string-left-right/-/string-left-right-5.1.7.tgz", - "integrity": "sha512-WSFZJ/oy3Ako6VS3+MyA7S1OceG+RIFkPUQzHumtw/N8aAA5WafVgQg2AE9WQWZ7py9bJlcH50BjSo+PlN8xNw==", - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lodash.isplainobject": "^4.0.6" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/string-strip-html": { - "version": "11.6.20", - "resolved": "https://registry.npmjs.org/string-strip-html/-/string-strip-html-11.6.20.tgz", - "integrity": "sha512-oM7a6FGBrs1w5gxPmbsJiOQBfNvDJRaratvfOqEh3nxTyZqEFX7PdxZ5bkYqbQ+FjggLDwwq5e7cS9J9xagekQ==", - "dependencies": { - "html-entities": "^2.3.3", - "lodash.isplainobject": "^4.0.6", - "lodash.trim": "^4.5.1", - "lodash.without": "^4.4.0", - "ranges-apply": "^6.2.12", - "ranges-push": "^6.2.7", - "string-left-right": "^5.1.7" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/string-trim-spaces-only": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/string-trim-spaces-only/-/string-trim-spaces-only-4.1.6.tgz", - "integrity": "sha512-rI++1I1wesrddwz1TH011+zNO0k+0u4b8RwDrzPlrQ3jvPrmrXfn5O5eWeLFuV/8zmPSXe5iq+G43hURZF89hQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/string.prototype.trim": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", @@ -4636,6 +4955,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, "node_modules/synckit": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", @@ -4658,11 +4982,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -4695,6 +5014,20 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -4739,6 +5072,19 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "dependencies": { + "domino": "^2.1.6" + } + }, + "node_modules/turndown-plugin-gfm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz", + "integrity": "sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4874,6 +5220,14 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4900,6 +5254,15 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4910,11 +5273,52 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -4979,6 +5383,39 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -5347,6 +5784,17 @@ "@types/range-parser": "*" } }, + "@types/jsdom": { + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.6.tgz", + "integrity": "sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -5407,6 +5855,15 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, + "@types/sanitize-html": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.9.5.tgz", + "integrity": "sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==", + "dev": true, + "requires": { + "htmlparser2": "^8.0.0" + } + }, "@types/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", @@ -5423,6 +5880,18 @@ "@types/node": "*" } }, + "@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "@types/turndown": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.4.tgz", + "integrity": "sha512-28GI33lCCkU4SGH1GvjDhFgOVr+Tym4PXGBIU1buJUa6xQolniPArtUT+kv42RR2N9MsMLInkr904Aq+ESHBJg==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -5549,6 +6018,14 @@ "dev": true, "requires": {} }, + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "requires": { + "debug": "^4.3.4" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -5889,6 +6366,47 @@ "which": "^2.0.1" } }, + "cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "requires": { + "rrweb-cssom": "^0.6.0" + } + }, + "data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "requires": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "dependencies": { + "tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "requires": { + "punycode": "^2.3.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "requires": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5897,12 +6415,22 @@ "ms": "2.1.2" } }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, "default-browser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", @@ -6015,6 +6543,44 @@ "esutils": "^2.0.2" } }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, "dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -6025,6 +6591,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -6115,8 +6686,7 @@ "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { "version": "8.52.0", @@ -6814,10 +7384,24 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + "html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "requires": { + "whatwg-encoding": "^3.1.1" + } + }, + "htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } }, "http-errors": { "version": "2.0.0", @@ -6831,6 +7415,24 @@ "toidentifier": "1.0.1" } }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, "husky": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", @@ -7034,6 +7636,16 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -7138,6 +7750,58 @@ "argparse": "^2.0.1" } }, + "jsdom": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.0.0.tgz", + "integrity": "sha512-cbL/UCtohJguhFC7c2/hgW6BeZCNvP7URQGnx9tSJRYKCdnfbfWOrtuLTMfiB2VxKsx5wPHVsh/J0aBy9lIIhQ==", + "requires": { + "cssstyle": "^3.0.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.14.2", + "xml-name-validator": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "requires": { + "punycode": "^2.3.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "requires": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -7201,32 +7865,12 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.trim": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz", - "integrity": "sha1-NkJefukL5KpeJ7zruFt9EepHqlc=" - }, - "lodash.without": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz", - "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7320,6 +7964,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7496,6 +8145,11 @@ } } }, + "nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, "o-son": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/o-son/-/o-son-1.0.2.tgz", @@ -7669,6 +8323,19 @@ "lines-and-columns": "^1.1.6" } }, + "parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7726,8 +8393,7 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { "version": "2.3.1", @@ -7747,6 +8413,16 @@ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true }, + "postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7773,11 +8449,15 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "qs": { "version": "6.11.0", @@ -7787,44 +8467,17 @@ "side-channel": "^1.0.4" } }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "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 }, - "ranges-apply": { - "version": "6.2.12", - "resolved": "https://registry.npmjs.org/ranges-apply/-/ranges-apply-6.2.12.tgz", - "integrity": "sha512-kvbXY6BNDpEAri/PlDZHlDyEAFvbAlfHGcIPQ6lg2BCifepx0t7knULy0ryvtsDuYCutK2ZVp+XTCMWcfaf/AQ==", - "requires": { - "ranges-merge": "^8.2.7", - "tiny-invariant": "^1.3.1" - } - }, - "ranges-merge": { - "version": "8.2.7", - "resolved": "https://registry.npmjs.org/ranges-merge/-/ranges-merge-8.2.7.tgz", - "integrity": "sha512-ymJfok6pW4vndF8wy6SeqnLGu5GH8k+MM+W4fAqif79HGNloNzwE0ijVmuRd8D7ulJnwo5BGzwpZpXYHYeLlMQ==", - "requires": { - "ranges-push": "^6.2.7", - "ranges-sort": "^5.1.6" - } - }, - "ranges-push": { - "version": "6.2.7", - "resolved": "https://registry.npmjs.org/ranges-push/-/ranges-push-6.2.7.tgz", - "integrity": "sha512-OX12airFLYK4/6dh3b82NRtGzpXyOWTmjiJzD/oMvzgZ2cfNbsa5qKLtf9AwZO6cVOpS2Li1l2mKTVjlM6MMpg==", - "requires": { - "string-collapse-leading-whitespace": "^6.1.7", - "string-trim-spaces-only": "^4.1.6" - } - }, - "ranges-sort": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/ranges-sort/-/ranges-sort-5.1.6.tgz", - "integrity": "sha512-/Mlg0Oe5iwu2lxeZGG/zzz92rQ6LpKimMx9uBj4EQthSSUDpvNeQzMthhdDBi/NGhG29rIWGpgl9jUHomUmX3Q==" - }, "raw-body": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", @@ -7909,6 +8562,11 @@ } } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -7941,6 +8599,11 @@ "glob": "^9.2.0" } }, + "rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + }, "run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -8057,6 +8720,27 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sanitize-html": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz", + "integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==", + "requires": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "requires": { + "xmlchars": "^2.2.0" + } + }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -8136,6 +8820,11 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, "spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -8173,39 +8862,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, - "string-collapse-leading-whitespace": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/string-collapse-leading-whitespace/-/string-collapse-leading-whitespace-6.1.7.tgz", - "integrity": "sha512-RGpJrs/C31mPyBPvmjcM16joFYaywAoJ8L9pWY/xFegkykFdMrg569ragafoJZgv485hEwWjihD5VgwJftrRmw==" - }, - "string-left-right": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/string-left-right/-/string-left-right-5.1.7.tgz", - "integrity": "sha512-WSFZJ/oy3Ako6VS3+MyA7S1OceG+RIFkPUQzHumtw/N8aAA5WafVgQg2AE9WQWZ7py9bJlcH50BjSo+PlN8xNw==", - "requires": { - "lodash.clonedeep": "^4.5.0", - "lodash.isplainobject": "^4.0.6" - } - }, - "string-strip-html": { - "version": "11.6.20", - "resolved": "https://registry.npmjs.org/string-strip-html/-/string-strip-html-11.6.20.tgz", - "integrity": "sha512-oM7a6FGBrs1w5gxPmbsJiOQBfNvDJRaratvfOqEh3nxTyZqEFX7PdxZ5bkYqbQ+FjggLDwwq5e7cS9J9xagekQ==", - "requires": { - "html-entities": "^2.3.3", - "lodash.isplainobject": "^4.0.6", - "lodash.trim": "^4.5.1", - "lodash.without": "^4.4.0", - "ranges-apply": "^6.2.12", - "ranges-push": "^6.2.7", - "string-left-right": "^5.1.7" - } - }, - "string-trim-spaces-only": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/string-trim-spaces-only/-/string-trim-spaces-only-4.1.6.tgz", - "integrity": "sha512-rI++1I1wesrddwz1TH011+zNO0k+0u4b8RwDrzPlrQ3jvPrmrXfn5O5eWeLFuV/8zmPSXe5iq+G43hURZF89hQ==" - }, "string.prototype.trim": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", @@ -8298,6 +8954,11 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, "synckit": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", @@ -8314,11 +8975,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, "titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -8339,6 +8995,17 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -8379,6 +9046,19 @@ } } }, + "turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "requires": { + "domino": "^2.1.6" + } + }, + "turndown-plugin-gfm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz", + "integrity": "sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8474,6 +9154,11 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -8494,6 +9179,15 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8504,11 +9198,42 @@ "spdx-expression-parse": "^3.0.0" } }, + "w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "requires": { + "xml-name-validator": "^5.0.0" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "requires": { + "iconv-lite": "0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==" + }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -8558,6 +9283,22 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "requires": {} + }, + "xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index fcfc700..ac2ee29 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,11 @@ "dice-roller-parser": "^0.1.8", "dotenv": "^16.0.3", "grammy": "^1.19.2", + "jsdom": "^23.0.0", "lodash-es": "^4.17.21", - "string-strip-html": "^11.6.20" + "sanitize-html": "^2.11.0", + "turndown": "^7.1.2", + "turndown-plugin-gfm": "^1.0.2" }, "devDependencies": { "@grammyjs/hydrate": "^1.3.1", @@ -36,8 +39,11 @@ "@rushstack/eslint-patch": "^1.4.0", "@types/cors": "^2.8.12", "@types/express": "^4.17.14", + "@types/jsdom": "^21.1.6", "@types/lodash-es": "^4.17.9", "@types/node": "^18.13.0", + "@types/sanitize-html": "^2.9.5", + "@types/turndown": "^5.0.4", "@typescript-eslint/eslint-plugin": "5.62.0", "@typescript-eslint/parser": "5.62.0", "eslint": "^8.31.0", diff --git a/src/commands/tools/dice.ts b/src/commands/tools/dice.ts index ab31db5..d5623be 100644 --- a/src/commands/tools/dice.ts +++ b/src/commands/tools/dice.ts @@ -10,12 +10,13 @@ import type { IContext } from '../../types/telegram.js'; import type { Conversation } from '@grammyjs/conversations'; const COMMAND_NAME = 'dice'; +const CANCEL_MSG = 'Закончить броски'; const { getUserMentionHtmlString, leaveScene } = useHelpers(); const { getRenderedMsg } = useDiceRoller(); const keyboard = new Keyboard() - .text('Закончить броски') + .text(CANCEL_MSG) .row() .text('d2') .text('d4') @@ -55,16 +56,15 @@ const handler = async ( if (ctx.hasCommand(helpCommand.command)) { await helpReply(ctx); - await handler(conversation); - return false; + return handler(conversation); } const { message: { text } } = ctx; - if (text === 'Закончить броски') { + if (text === CANCEL_MSG) { return false; } @@ -86,7 +86,7 @@ const handler = async ( return false; } - await ctx.replyWithMarkdownV2(msg, { + await ctx.reply(msg, { disable_notification: true, reply_markup: { keyboard diff --git a/src/commands/wiki/spell.ts b/src/commands/wiki/spell.ts index 8ee408f..eea972d 100644 --- a/src/commands/wiki/spell.ts +++ b/src/commands/wiki/spell.ts @@ -1,18 +1,28 @@ import { InlineKeyboard, Keyboard } from 'grammy'; -import { isNumber, toNumber } from 'lodash-es'; +import { toNumber } from 'lodash-es'; import cancelCallback from '../../callbacks/cancel.js'; import { useAxios } from '../../utils/useAxios.js'; +import { useConfig } from '../../utils/useConfig.js'; import { useHelpers } from '../../utils/useHelpers.js'; +import { useMarkup } from '../../utils/useMarkup.js'; import type { ICommand } from '../../types/commands.js'; -import type { TSpellLink } from '../../types/spell.js'; +import type { + TSpellItem, + TSpellItemComponents, + TSpellLink +} from '../../types/spell.js'; import type { IContext } from '../../types/telegram.js'; import type { Conversation } from '@grammyjs/conversations'; +import type { Other } from '@grammyjs/hydrate'; const COMMAND_NAME = 'spell'; +const CANCEL_MSG = 'Закончить поиск'; -const { getUserMentionHtmlString, leaveScene } = useHelpers(); +const { MAX_LENGTH } = useConfig(); +const { getUserMentionHtmlString, leaveScene, getUrl } = useHelpers(); +const { getDescriptionEmbeds } = useMarkup(); class SpellConversation { private readonly http = useAxios(); @@ -21,88 +31,171 @@ class SpellConversation { private prevSearch = ''; - init = async (conversation: Conversation): Promise => { - const ctx = await conversation.waitFor('message:text'); + init = async ( + conversation: Conversation, + context?: IContext + ): Promise => { + try { + context?.reply('Введи название заклинания (минимум 3 буквы)', { + reply_to_message_id: undefined, + reply_markup: { remove_keyboard: true } + }); - const { - msg: { text } - } = ctx; + const ctx = await conversation.waitFor('message:text'); - const search = text.trim(); + const { + msg: { text } + } = ctx; - if (!search) { - await ctx.reply('Введи название заклинания (минимум 3 буквы)', { - disable_notification: true - }); + const msg = text.trim(); - return this.init(conversation); - } + if (text === CANCEL_MSG) { + return false; + } - if (this.prevSearch === search) { - await ctx.reply('Ты уже задал этот вопрос', { - disable_notification: true - }); + const index = toNumber(msg); - return this.init(conversation); - } + if (Number.isFinite(index)) { + return this.select(conversation, ctx, index); + } - this.prevSearch = search; - this.spells = await conversation.external(() => this.loadSpells(search)); + if (msg.length < 3) { + await ctx.reply('Необходимо ввести минимум 3 буквы', { + disable_notification: true, + reply_markup: new InlineKeyboard().text( + 'Закончить поиск заклинаний', + cancelCallback.data + ) + }); - if (!this.spells.length) { - await ctx.reply('Я не смог найти такое заклинание', { - disable_notification: true - }); + return this.init(conversation); + } - return this.init(conversation); + return this.search(conversation, ctx, msg); + } catch (err) { + return Promise.reject(err); } + }; - const keyboard = new Keyboard([ - this.spells.map((_spell, index) => (index + 1).toString()) - ]); - - await ctx.reply( - `Я нашел несколько заклинаний, выбери подходящее из этого списка:${this.spells.map( - (spell, index) => - `${!index ? '\n' : ''}\n${index + 1}. ${spell.name.rus} [${ - spell.name.eng - }]` - )}`, - { - disable_notification: true, - reply_markup: keyboard.resized().selected() + private search = async ( + conversation: Conversation, + ctx: IContext, + search: string + ) => { + try { + if (this.prevSearch === search) { + await ctx.reply('Ты уже задал этот вопрос', { + disable_notification: true, + reply_markup: new InlineKeyboard().text( + 'Закончить поиск заклинаний', + cancelCallback.data + ) + }); + + return this.init(conversation); } - ); - return this.select(conversation); - }; + this.prevSearch = search; - private select = async (conversation: Conversation) => { - const ctx = await conversation.waitFor('message:text'); + const spells = await conversation.external(() => this.loadSpells(search)); - const { - msg: { text } - } = ctx; + if (!spells.length) { + await ctx.reply('Я не смог найти такое заклинание', { + disable_notification: true, + reply_markup: new InlineKeyboard().text( + 'Закончить поиск заклинаний', + cancelCallback.data + ) + }); - const match = text.match(/^(?\d+?)\.(.+?)(\[(.+?)])$/i); - const index = toNumber(match?.groups?.index?.trim()); + return this.init(conversation); + } - if (!isNumber(index)) { - await ctx.reply('Произошла ошибка, попробуй еще раз...', { - disable_notification: true - }); + if (spells.length > 10) { + await ctx.reply('Я нашел слишком много заклинаний', { + disable_notification: true, + reply_markup: new InlineKeyboard().text( + 'Закончить поиск заклинаний', + cancelCallback.data + ) + }); + + return this.init(conversation); + } - return false; + this.spells = spells; + + if (spells.length === 1) { + return this.select(conversation, ctx, 1); + } + + const keyboard = new Keyboard([ + this.spells.map((_spell, index) => (index + 1).toString()), + [CANCEL_MSG] + ]); + + await ctx.reply( + `Я нашел несколько заклинаний, выбери подходящее из этого списка:${this.spells.map( + (spell, index) => + `${!index ? '\n' : ''}\n${index + 1}. ${spell.name.rus} [${ + spell.name.eng + }]` + )}`, + { + disable_notification: true, + reply_markup: keyboard.resized().selected() + } + ); + + return this.init(conversation); + } catch (err) { + return Promise.reject(err); } + }; - const spell = this.spells[index - 1]; + private select = async ( + conversation: Conversation, + ctx: IContext, + index: number + ): Promise => { + try { + const spell = await this.loadSpell(this.spells[index - 1]!); - if (!spell) { - await ctx.reply('Произошла ошибка, попробуй еще раз...', { - disable_notification: true - }); + if (!spell) { + await ctx.reply('Произошла ошибка, попробуй еще раз...', { + disable_notification: true + }); - return this.select(conversation); + return this.init(conversation); + } + + const messages = await this.getSpellResponse(spell); + + for (let i = 0; i < messages.length; i++) { + const msg = messages[i]!; + + const config: Other<'sendMessage', 'chat_id' | 'text'> = { + disable_notification: true, + disable_web_page_preview: true + }; + + if (i > 0) { + config.reply_to_message_id = undefined; + } + + if (i + 1 === messages.length) { + config.reply_markup = new InlineKeyboard() + .url('Оригинал на TTG Club', getUrl(spell.url)) + .row() + .text('Закончить поиск заклинаний', cancelCallback.data); + } + + await ctx.reply(msg, config); + } + + return this.init(conversation); + } catch (err) { + return Promise.reject(err); } }; @@ -136,23 +229,128 @@ class SpellConversation { } }; - // private loadSpell = async (url: string): Promise => { - // try { - // const { data: spell } = await this.http.post({ - // url - // }); - // - // return spell; - // } catch (err) { - // return Promise.reject(err); - // } - // }; - // - // private getSpellResponse = (spell: TSpellItem) => { - // console.log(spell); - // - // return spell.name.rus; - // }; + private loadSpell = async (spellLink: TSpellLink): Promise => { + try { + const { data: spell } = await this.http.post({ + url: spellLink.url + }); + + return spell; + } catch (err) { + return Promise.reject(err); + } + }; + + private getSpellResponse = (spell: TSpellItem): Promise> => { + try { + const messages: string[] = [ + `${spell.name.rus} [${spell.name.eng}]` + ]; + + const updateMsg = (str: string) => { + const index = messages.length > 0 ? messages.length - 1 : 0; + + if (messages[index]!.length + str.length > MAX_LENGTH) { + messages[index + 1] = str; + + return; + } + + messages[index] += str; + }; + + updateMsg(`\n${this.getSubTitle(spell)}\n`); + + updateMsg( + `\nИсточник: ${spell.source.name} [${spell.source.shortName}]` + ); + + updateMsg(`\nВремя накладывания: ${spell.time}`); + updateMsg(`\nДистанция: ${spell.range}`); + updateMsg(`\nДлительность: ${spell.duration}`); + + updateMsg( + `\nКомпоненты: ${this.getComponents(spell.components)}\n` + ); + + const classes = spell.classes + .map( + classItem => + `${classItem.name}` + ) + .join(', '); + + if (classes.length) { + updateMsg(`\nКлассы: ${classes}`); + } + + const subClasses = spell.subclasses + ?.map( + subclass => + `${subclass.name} (${ + subclass.class + })` + ) + .join(', '); + + if (subClasses?.length) { + updateMsg(`\nПодклассы: ${subClasses}`); + } + + const races = spell.races + ?.map(race => `${race.name}`) + .join(', '); + + if (races?.length) { + updateMsg(`\nРасы и происхождения: ${races}`); + } + + const backgrounds = spell.backgrounds + ?.map( + background => + `${background.name}` + ) + .join(', '); + + if (backgrounds?.length) { + updateMsg(`\nПредыстории: ${backgrounds}`); + } + + if (spell.description) { + updateMsg(`\n\n`); + + for (const row of getDescriptionEmbeds(spell.description)) { + updateMsg(row); + } + } + + if (spell.upper) { + updateMsg(`\n\nНа более высоких уровнях: `); + + for (const row of getDescriptionEmbeds(`

${spell.upper}

`)) { + updateMsg(row); + } + } + + return Promise.resolve(messages); + } catch (err) { + return Promise.reject(err); + } + }; + + private getSubTitle = (spell: TSpellItem) => + `${spell.level ? `${spell.level} уровень` : 'заговор'}, ${spell.school}${ + spell.additionalType ? ` [${spell.additionalType}]` : '' + }${spell.ritual ? ' (ритуал)' : ''}`; + + private getComponents = (components: TSpellItemComponents) => + `${ + components.v + ? `Вербальный${components.s || components.m ? ', ' : ''}` + : '' + }${components.s ? `Соматический${components.m ? ', ' : ''}` : ''}${ + components.m ? `Материальный (${components.m})` : '' + }`; } const spellCommand: ICommand = { @@ -172,21 +370,17 @@ const spellCommand: ICommand = { const userName = getUserMentionHtmlString(ctx); - await ctx.reply( - `${userName} вошел(ла) в режим поиска заклинаний.` + - '\nВведи название заклинания (минимум 3 буквы)', - { - disable_notification: true, - reply_markup: new InlineKeyboard().text( - 'Закончить поиск заклинаний', - cancelCallback.data - ) - } - ); + await ctx.reply(`${userName} вошел(ла) в режим поиска заклинаний.`, { + disable_notification: true, + reply_markup: new InlineKeyboard().text( + 'Закончить поиск заклинаний', + cancelCallback.data + ) + }); const scene = new SpellConversation(); - await scene.init(conversation); + await scene.init(conversation, ctx); await leaveScene(ctx); } }; diff --git a/src/scenes/SpellScenes.ts b/src/scenes/SpellScenes.ts deleted file mode 100644 index de9ce4b..0000000 --- a/src/scenes/SpellScenes.ts +++ /dev/null @@ -1,264 +0,0 @@ -// import { Markup } from 'telegraf'; -// -// import { SOCIAL_LINKS } from '../locales/about.js'; -// import SpellsMiddleware from '../middlewares/SpellsMiddleware.js'; -// import BaseHandler from '../utils/BaseHandler.js'; -// import { useAxios } from '../utils/useAxios.js'; -// import TelegrafHelpers from '../utils/useHelpers.js'; -// -// import type IBot from '../types/TelegramBot.js'; -// -// enum ACTIONS { -// ExitFromSearch = '❌ Закончить поиск' -// } -// -// enum CALLBACK_ACTIONS { -// ExitFromSearch = 'exitFromSearch' -// } -// -// const scene = new BaseScene('findSpell'); -// const http = useAxios(); -// const spellsMiddleware = new SpellsMiddleware(); -// -// const EXIT_BUTTON: CallbackButton[] = [ -// Markup.button('Закончить поиск заклинания', CALLBACK_ACTIONS.ExitFromSearch) -// ]; -// -// const LEAVE_MSG = 'вышел(а) из режима поиска заклинания'; -// -// const getSpellListKeyboard = (spellList: NSpell.ISpell[]) => -// Markup.keyboard([ -// [Markup.button(ACTIONS.ExitFromSearch)], -// ...spellList.map(spell => [ -// Markup.button(`${spell.name} [${spell.englishName}]`) -// ]) -// ]); -// -// const sendSpellMessage = async (ctx: IBot.TContext, spell: NSpell.ISpell) => { -// const { messages, url } = spellsMiddleware.getSpellMessage(spell); -// -// try { -// for (let i = 0; i < messages.length; i++) { -// let extra: Extra = { -// reply_to_message_id: ctx.message?.message_id, -// disable_web_page_preview: true, -// disable_notification: true -// }; -// -// if (i === messages.length - 1) { -// extra = { -// ...extra, -// reply_markup: Markup.inlineKeyboard([ -// [Markup.urlButton('Оригинал на TTG Club', url)], -// EXIT_BUTTON -// ]) -// }; -// } -// -// await ctx.replyWithHTML(messages[i], extra); -// } -// } catch (err) { -// console.error(err); -// -// for (let i = 0; i < messages.length; i++) { -// const extra: Extra = { -// reply_to_message_id: ctx.message?.message_id, -// disable_web_page_preview: true, -// disable_notification: true -// }; -// -// await ctx.reply(messages[i], extra); -// } -// -// await ctx.reply( -// 'Произошла ошибка, поэтому я выслал тебе сырую версию сообщения заклинания... ' + -// 'пожалуйста, сообщи нам об этом в Discord', -// { -// reply_markup: Markup.inlineKeyboard([ -// [ -// Markup.urlButton( -// SOCIAL_LINKS.discord.label, -// SOCIAL_LINKS.discord.url -// ) -// ] -// ]), -// disable_notification: true -// } -// ); -// -// await BaseHandler.leaveScene(ctx, LEAVE_MSG); -// } -// }; -// -// const trySendSpellFromSession = async (ctx: IBot.TContext, name: string) => { -// if (ctx.scene.session.state?.spellList?.length && name) { -// const spell = ctx.scene.session.state.spellList.find( -// (item: NSpell.ISpell) => item.name === name -// ); -// -// if (!spell) { -// return false; -// } -// -// await sendSpellMessage(ctx, spell); -// -// return true; -// } -// -// return false; -// }; -// -// scene.enter(async ctx => { -// const userName = TelegrafHelpers.getUserMentionHTMLString(ctx); -// -// // eslint-disable-next-line no-param-reassign -// ctx.scene.session.state.spellList = []; -// -// await ctx.replyWithHTML( -// `${userName} вошел(ла) в режим поиска заклинаний.` + -// '\nВведи название заклинания (минимум 3 буквы)', -// { -// reply_to_message_id: ctx.message?.message_id, -// disable_notification: true, -// reply_markup: Markup.inlineKeyboard([EXIT_BUTTON]) -// } -// ); -// }); -// -// scene.hears(ACTIONS.ExitFromSearch, async ctx => { -// await BaseHandler.leaveScene(ctx, LEAVE_MSG); -// }); -// -// scene.on('text', async ctx => { -// try { -// if (!ctx.message || !('text' in ctx.message)) { -// await ctx.reply('Произошла какая-то ошибка...'); -// -// await BaseHandler.leaveScene(ctx, LEAVE_MSG); -// -// return; -// } -// -// if (ctx.message.text.length < 3) { -// await ctx.reply('Название слишком короткое', { -// reply_to_message_id: ctx.message.message_id, -// disable_notification: true, -// reply_markup: { -// ...Markup.keyboard([[Markup.button(ACTIONS.ExitFromSearch)]]), -// input_field_placeholder: 'Название...', -// resize_keyboard: true, -// selective: true -// } -// }); -// -// return; -// } -// -// const input: string = ctx.message.text.trim(); -// const match = input.match(/^(?.+?)(\[.+?])$/i); -// const matchedName = match?.groups?.spellName?.trim(); -// -// // eslint-disable-next-line no-param-reassign -// ctx.scene.session.state.searchStr = ctx.message.text.trim(); -// -// if (!!matchedName && (await trySendSpellFromSession(ctx, matchedName))) { -// return; -// } -// -// const value: string = matchedName || input; -// -// const apiOptions: NSpell.IRequest = { -// search: value -// }; -// -// if (matchedName) { -// apiOptions.exact = !!matchedName; -// } -// -// const resp = await http.get('/spells', apiOptions); -// const spellList: NSpell.ISpell[] = resp.spell; -// -// let spell: NSpell.ISpell | undefined; -// -// if (spellList.length === 1) { -// [spell] = spellList; -// -// await sendSpellMessage(ctx, spell); -// -// return; -// } -// -// if (spellList.length > 10) { -// await ctx.replyWithHTML( -// 'Я нашел слишком много заклинаний... попробуй уточнить название', -// { -// reply_to_message_id: ctx.message.message_id, -// disable_notification: true, -// reply_markup: { -// ...getSpellListKeyboard(ctx.scene.session.state.spellList), -// input_field_placeholder: 'Название...', -// resize_keyboard: true, -// selective: true -// } -// } -// ); -// -// return; -// } -// -// if (spellList.length > 1) { -// // eslint-disable-next-line no-param-reassign -// ctx.scene.session.state.spellList = spellList; -// -// await ctx.replyWithHTML( -// 'Я нашел несколько заклинаний, выбери подходящее из этого списка', -// { -// reply_to_message_id: ctx.message?.message_id, -// disable_notification: true, -// reply_markup: { -// ...getSpellListKeyboard(spellList), -// input_field_placeholder: 'Название...', -// selective: true, -// resize_keyboard: true -// } -// } -// ); -// -// return; -// } -// -// await ctx.reply( -// 'Я не смог найти такое заклинание... попробуй другое название', -// { -// reply_to_message_id: ctx.message.message_id, -// disable_notification: true, -// reply_markup: { -// ...getSpellListKeyboard(ctx.scene.session.state.spellList), -// input_field_placeholder: 'Название...', -// resize_keyboard: true, -// selective: true -// } -// } -// ); -// } catch (err) { -// console.error(err); -// -// await ctx.reply( -// 'Что-то пошло не так... попробуй запустить команду еще раз', -// { -// reply_to_message_id: ctx.message?.message_id, -// disable_notification: true -// } -// ); -// -// await BaseHandler.leaveScene(ctx, LEAVE_MSG); -// } -// }); -// -// scene.action(CALLBACK_ACTIONS.ExitFromSearch, async ctx => { -// await ctx.answerCbQuery(); -// -// await BaseHandler.leaveScene(ctx, LEAVE_MSG); -// }); -// -// export default scene; diff --git a/src/types/base.d.ts b/src/types/base.d.ts index 996aa2d..9f141c3 100644 --- a/src/types/base.d.ts +++ b/src/types/base.d.ts @@ -32,6 +32,11 @@ export type TRaceBadge = { url: string; }; +export type TBackgroundBadge = { + name: string; + url: string; +}; + export type TPrice = { dmg: string; xge: string; diff --git a/src/types/gfm.d.ts b/src/types/gfm.d.ts new file mode 100644 index 0000000..ecb2cd8 --- /dev/null +++ b/src/types/gfm.d.ts @@ -0,0 +1,11 @@ +declare module 'turndown-plugin-gfm' { + export function gfm(turndownService: any): void; + + export function highlightedCodeBlock(turndownService: any): void; + + export function strikethrough(turndownService: any): void; + + export function tables(turndownService: any): void; + + export function taskListItems(turndownService: any): void; +} diff --git a/src/types/spell.d.ts b/src/types/spell.d.ts index 3364491..545d8aa 100644 --- a/src/types/spell.d.ts +++ b/src/types/spell.d.ts @@ -1,4 +1,5 @@ import type { + TBackgroundBadge, TClassBadge, TName, TRaceBadge, @@ -47,5 +48,6 @@ export type TSpellItem = { concentration?: boolean; ritual?: boolean; races?: TRaceBadge[]; + backgrounds?: TBackgroundBadge[]; upper?: string; }; diff --git a/src/utils/useConfig.ts b/src/utils/useConfig.ts index ff04954..851ee1f 100644 --- a/src/utils/useConfig.ts +++ b/src/utils/useConfig.ts @@ -17,6 +17,7 @@ if (error !== undefined || parsed === undefined) { export const useConfig = (): { TOKEN: string; API_URL: string; + MAX_LENGTH: number; } => { if (!parsed.TOKEN) { throw new Error('TOKEN is not defined'); @@ -28,6 +29,7 @@ export const useConfig = (): { return { TOKEN: parsed.TOKEN, - API_URL: parsed.API_URL + API_URL: parsed.API_URL, + MAX_LENGTH: 4096 }; }; diff --git a/src/utils/useDiceRoller.ts b/src/utils/useDiceRoller.ts index cdb8228..7ab9b00 100644 --- a/src/utils/useDiceRoller.ts +++ b/src/utils/useDiceRoller.ts @@ -9,6 +9,8 @@ import DiceRollerParser, { } from 'dice-roller-parser'; import { orderBy } from 'lodash-es'; +import { useHelpers } from './useHelpers.js'; + import type { DiceRollResult } from 'dice-roller-parser/dist/rollTypes.js'; export interface IRollResult { @@ -20,18 +22,16 @@ export interface IRollResult { } export class TelegramRollRenderer { - render = (roll: RollBase) => this.doRender(roll, true); + public render = (roll: RollBase) => this.doRender(roll, true); private doRender = (roll: RollBase, root = false) => { let render = ''; - let label = ''; const { type } = roll; switch (type) { case 'diceexpressionroll': - // @ts-ignore - render = this.renderGroupExpr(roll as DiceExpressionRoll); + render = this.renderGroupExpr(roll as DiceExpressionRoll)!; break; case 'grouproll': @@ -55,9 +55,7 @@ export class TelegramRollRenderer { case 'fateroll': return this.renderFateRoll(roll as FateDieRoll); case 'number': - label = roll.label ? ` \\(${roll.label}\\)` : ''; - - return `${roll.value}${label}`; + return `${roll.value}${roll.label ? ` (${roll.label})` : ''}`; case 'fate': return `F`; default: @@ -65,14 +63,14 @@ export class TelegramRollRenderer { } if (!roll.valid) { - render = `~${render.replace(/~/g, '')}~`; + render = `${render.replace(/<\/?s>/g, '')}`; } if (root) { return this.stripBrackets(render); } - return roll.label ? `\\(${roll.label}\\: ${render}\\)` : render; + return roll.label ? `(${roll.label}: ${render})` : render; }; private renderGroup = (group: GroupRoll) => { @@ -83,13 +81,12 @@ export class TelegramRollRenderer { } if (replies.length > 1) { - return `{ ${replies.join(' \\+ ')} } \\= \\${group.value}`; + return `{ ${replies.join(' + ')} } = ${group.value}`; } - // @ts-ignore - const reply = this.stripBrackets(replies[0]); + const reply = this.stripBrackets(replies[0]!); - return `\\{ ${reply} \\} \\= \\${group.value}`; + return `{ ${reply} } = ${group.value}`; }; private renderGroupExpr = (group: DiceExpressionRoll) => { @@ -100,7 +97,7 @@ export class TelegramRollRenderer { } return replies.length > 1 - ? `\\(${replies.join(' \\+ ')} \\= \\${group.value}\\)` + ? `(${replies.join(' + ')} = ${group.value})` : replies[0]; }; @@ -117,16 +114,16 @@ export class TelegramRollRenderer { !['number', 'fate'].includes(die.die.type) || die.count.type !== 'number' ) { - reply += `\\[*Rolling\\: ${this.doRender(die.count)}d${this.doRender( + reply += `[Rolling: ${this.doRender(die.count)}d${this.doRender( die.die - )}*\\]`; + )}]`; } const matches = die.matched ? ` Match${die.value === 1 ? '' : 'es'}` : ''; - reply += ` \\= \\${die.value}${matches}`; + reply += ` = ${die.value}${matches}`; - return `\\(${reply}\\)`; + return `(${reply})`; }; private renderExpression = (expr: ExpressionRoll) => { @@ -134,101 +131,96 @@ export class TelegramRollRenderer { const expressions: string[] = []; for (let i = 0; i < expr.dice.length - 1; i++) { - // @ts-ignore - expressions.push(this.doRender(expr.dice[i])); - // @ts-ignore - expressions.push(`\\${expr.ops[i]}`); + expressions.push(this.doRender(expr.dice[i]!)); + expressions.push(expr.ops[i]!); } - // @ts-ignore - expressions.push(this.doRender(expr.dice.slice(-1)[0])); - expressions.push('\\='); - expressions.push(`\\${expr.value}`); + expressions.push(this.doRender(expr.dice.slice(-1)[0]!)); + expressions.push('='); + expressions.push(`${expr.value}`); - return `\\(${expressions.join(' ')}\\)`; + return `(${expressions.join(' ')})`; } - // @ts-ignore - if (expr.dice[0].type === 'number') { - return `\\${expr.value}`; + if (expr.dice[0]!.type === 'number') { + return `${expr.value}`; } - // @ts-ignore - return this.doRender(expr.dice[0]); + return this.doRender(expr.dice[0]!); }; private renderFunction = (roll: MathFunctionRoll) => { const render = this.doRender(roll.expr); - return `\\(${roll.op}${this.addBrackets(render)} \\= \\${roll.value}\\)`; + return `(${roll.op}${this.addBrackets(render)} = ${roll.value})`; }; private addBrackets = (render: string) => { - let newRender = render; + let updated = render; - if (!newRender.startsWith('(')) { - newRender = `\\(${newRender}`; + if (!updated.startsWith('(')) { + updated = `(${updated}`; } - if (!newRender.endsWith(')')) { - newRender = `${newRender}\\)`; + if (!updated.endsWith(')')) { + updated = `${updated})`; } - return newRender; + return updated; }; private stripBrackets = (render: string) => { - let newRender = render; + let updated = render; - if (newRender.startsWith('(')) { - newRender = newRender.substring(1); + if (updated.startsWith('(')) { + updated = updated.substring(1); } - if (newRender.endsWith(')')) { - newRender = render.substring(0, newRender.length - 1); + if (updated.endsWith(')')) { + updated = updated.substring(0, updated.length - 1); } - return newRender; + return updated; }; private renderRoll = (roll: DieRoll) => { - let rollDisplay = `\\${roll.roll}`; + let rollDisplay = `${roll.roll}`; if (!roll.valid) { - rollDisplay = `~${roll.roll}~`; + rollDisplay = `${roll.roll}`; } else if (roll.success && roll.value === 1) { - rollDisplay = `*${roll.roll}*`; + rollDisplay = `${roll.roll}`; } else if (roll.success && roll.value === -1) { - rollDisplay = `_${roll.roll}_`; + rollDisplay = `${roll.roll}`; } else if (!roll.success && roll.critical === 'success') { - rollDisplay = `*${roll.roll}*`; + rollDisplay = `${roll.roll}`; } else if (!roll.success && roll.critical === 'failure') { - rollDisplay = `_${roll.roll}_`; + rollDisplay = `${roll.roll}`; } if (roll.matched) { - rollDisplay = `__${rollDisplay}__`; + rollDisplay = `${rollDisplay}`; } return rollDisplay; }; private renderFateRoll = (roll: FateDieRoll) => { - const rollValue: string = - roll.roll === 0 ? '0' : roll.roll > 0 ? '\\+' : '\\-'; + // eslint-disable-next-line no-nested-ternary + const rollValue: string = roll.roll === 0 ? '0' : roll.roll > 0 ? '+' : '-'; - let rollDisplay = `\\${roll.roll}`; + let rollDisplay = `${roll.roll}`; if (!roll.valid) { - rollDisplay = `~${rollValue}~`; + rollDisplay = `${rollValue}`; } else if (roll.success && roll.value === 1) { - rollDisplay = `*${rollValue}*`; + rollDisplay = `${rollValue}`; } else if (roll.success && roll.value === -1) { - rollDisplay = `_${rollValue}_`; + rollDisplay = `${rollValue}`; } if (roll.matched) { - rollDisplay = `__${rollDisplay}__`; + rollDisplay = `${rollDisplay}`; } return rollDisplay; @@ -240,9 +232,7 @@ export function useDiceRoller() { const roller = new DiceRoller(); const { render } = new TelegramRollRenderer(); - - const getEscapedString = (str: string) => - str.replace(/([_*[\]()~`>#+\-=|{},.!])/g, '\\$1'); + const { getEscapedString } = useHelpers(); const getBaseResponse = ( rendered: string, @@ -290,6 +280,12 @@ export function useDiceRoller() { case '2d20kl1': return getDropOrKeepMsg(formula); + case 'пом': + return getDropOrKeepMsg('2d20kl1'); + + case 'пре': + return getDropOrKeepMsg('2d20kh1'); + default: return getDefaultDiceMsg(formula); } @@ -305,24 +301,24 @@ export function useDiceRoller() { ); } - let reply = `\n\n*Развернутый результат:* ${roll.rendered}`; - - if (notation !== '2d20' && notation !== '2к20') { - reply += `\n\n*Результат:* ${getEscapedString(roll.value.toString())}`; - } + let reply = ''; if (roll.highest) { - reply += `\n\n*Лучший бросок:* ${getEscapedString( + reply += `\nЛучший бросок: ${getEscapedString( roll.highest.toString() )}`; } if (roll.lowest) { - reply += `\n\n*Худший бросок:* ${getEscapedString( + reply += `\nХудший бросок: ${getEscapedString( roll.lowest.toString() )}`; } + reply += `\n\nРазвернутый результат: ${roll.rendered}`; + + reply += `\nРезультат: ${getEscapedString(roll.value.toString())}`; + return reply; } catch (err) { return Promise.reject(err); diff --git a/src/utils/useHelpers.ts b/src/utils/useHelpers.ts index 1c4deba..782d059 100644 --- a/src/utils/useHelpers.ts +++ b/src/utils/useHelpers.ts @@ -1,5 +1,9 @@ +import { useConfig } from './useConfig.js'; + import type { IContext } from '../types/telegram.js'; +const { API_URL } = useConfig(); + export const useHelpers = () => { const getUserMentionHtmlString = (ctx: IContext): string => { const fullName = ctx.from?.last_name @@ -53,9 +57,16 @@ export const useHelpers = () => { setTimeout(reply.delete, 1000); }; + const getUrl = (url: string) => `${API_URL}${url}`; + + const getEscapedString = (str: string) => + str.replace(/([_*[\]()~`>#+\-=|{},.!])/g, '\\$1'); + return { - getUserMentionHtmlString, + getUrl, + pluralize, leaveScene, - pluralize + getEscapedString, + getUserMentionHtmlString }; }; diff --git a/src/utils/useJSDom.ts b/src/utils/useJSDom.ts new file mode 100644 index 0000000..ce794a9 --- /dev/null +++ b/src/utils/useJSDom.ts @@ -0,0 +1,45 @@ +import { JSDOM } from 'jsdom'; + +export type TPairs = { + name: string; + value: string; +}; + +export const useJSDom = () => { + const getArrayParagraphs = (html: string): string[] => { + const fragment = JSDOM.fragment(html); + const array = Array.from(fragment.childNodes); + + return array.map(node => { + const dom = new JSDOM(); + const { window } = dom; + const { document } = window; + + document.body.append(node); + + return document.body.innerHTML; + }); + }; + + const getHTMLArrayFromPairs = (array: TPairs[]): string => + array + .map(pair => { + const dom = new JSDOM(); + const { window } = dom; + const { document } = window; + + const fragment = JSDOM.fragment( + `

${pair.name}. ${pair.value}

` + ); + + document.body.append(fragment); + + return document.body.innerHTML; + }) + .join(''); + + return { + getArrayParagraphs, + getHTMLArrayFromPairs + }; +}; diff --git a/src/utils/useMarkup.ts b/src/utils/useMarkup.ts new file mode 100644 index 0000000..b9d70b0 --- /dev/null +++ b/src/utils/useMarkup.ts @@ -0,0 +1,188 @@ +import sanitizeHtml from 'sanitize-html'; +import TurndownService from 'turndown'; +import { gfm } from 'turndown-plugin-gfm'; + +import { useHelpers } from './useHelpers.js'; +import { useJSDom } from './useJSDom.js'; + +const { getUrl } = useHelpers(); + +const allowedTags = [ + // Bold + 'b', + 'strong', + + // Italic + 'i', + 'em', + + // Strikethrough + 's', + 'del', + + // Underline + 'u', + 'ins', + + // Link + 'a', + + // Lists + 'ul', + 'li', + 'ol', + + // Paragraphs + 'p', + + // Table + 'table', + 'td', + 'th', + 'tr', + + // Dices + 'dice-roller' +]; + +export const useMarkup = () => { + const turndownService = new TurndownService({ + bulletListMarker: '-' + }); + + turndownService.use(gfm); + + turndownService.addRule('paragraph', { + filter: 'p', + replacement: content => `\n\n${content}\n\n` + }); + + turndownService.addRule('diceRoller', { + filter: node => node.nodeName === 'DICE-ROLLER', + replacement: (content, node) => { + let text = ''; + + if ('getAttribute' in node && node.getAttribute('formula')) { + text = `${node.getAttribute('formula')}`; + } + + if ('getAttribute' in node && node.getAttribute(':formula')) { + text = `${node.getAttribute('formula')}`; + } + + if (content) { + text = `${content}`; + } + + return text; + } + }); + + turndownService.addRule('inlineLink', { + filter: (node, options) => + options.linkStyle === 'inlined' && + node.nodeName === 'A' && + !!node.getAttribute('href'), + + replacement: (content, node) => { + const getUpdatedHref = (href: string) => { + if (href.startsWith('http')) { + return href; + } + + return getUrl(href); + }; + + let href: string | null = null; + + if ('getAttribute' in node) { + href = node.getAttribute('href'); + } + + if (href) { + href = getUpdatedHref(href); + } + + return `${content}`; + } + }); + + turndownService.addRule('baseTags', { + filter: [ + // Bold + 'b', + 'strong', + + // Italic + 'i', + 'em', + + // Strikethrough + 's', + 'del', + + // Underline + 'u', + 'ins' + ], + replacement: (content, node) => + `<${node.nodeName.toLowerCase()}>${content}` + }); + + const getSanitized = (html: string) => sanitizeHtml(html, { allowedTags }); + + const getMarkup = (html: string) => { + if (!html) { + return ''; + } + + const sanitized = getSanitized(html); + + return turndownService + .turndown(sanitized) + .replace(/\\\[/g, '[') + .replace(/\\]/g, ']'); + }; + + const getMarkupParagraphs = (html: string) => { + const { getArrayParagraphs } = useJSDom(); + + const array = getArrayParagraphs( + sanitizeHtml(html, { allowedTags: [...allowedTags, 'p'] }) + ); + + return array.map(node => getMarkup(node)); + }; + + const getDescriptionEmbeds = (html: string) => { + const rows = getMarkupParagraphs(html).filter(row => !!row); + + const embeds: string[] = []; + + let str = ''; + + for (const row of rows) { + if (str.length + row.length > 2048) { + embeds.push(str.trim()); + str = ''; + } + + str += `\n\n${row}`; + } + + str = str.trim(); + + if (embeds[embeds.length - 1] !== str) { + embeds.push(str.trim()); + } + + return embeds.filter(row => !!row); + }; + + return { + getSanitized, + getMarkup, + getMarkupParagraphs, + getDescriptionEmbeds + }; +};