From 6e22b02658a7284f02dbda8cc1326e3b9377411c Mon Sep 17 00:00:00 2001 From: sogehige Date: Thu, 12 May 2022 15:19:37 +0200 Subject: [PATCH] feat(plugins): add plugin system (#5137) --- d.ts/src/helpers/socket.d.ts | 16 +- d.ts/src/plugins.d.ts | 15 + locales/en/ui.menu.json | 3 +- locales/en/ui/errors.json | 3 + locales/en/ui/registry/alerts.json | 2 +- locales/en/ui/registry/plugins.json | 41 ++ package-lock.json | 508 +++++++++--------- package.json | 15 +- src/database/entity/plugins.ts | 34 ++ .../14.0.x/1650978850216-addPluginTable.ts | 16 + .../14.0.x/1650978850216-addPluginTable.ts | 16 + .../14.0.x/1650978850216-addPluginTable.ts | 15 + src/helpers/errors.ts | 9 +- src/main.ts | 1 + src/plugins.ts | 223 ++++++++ src/plugins/index.ts | 38 ++ src/plugins/nodes/filter.ts | 41 ++ src/plugins/nodes/filterPermission.ts | 15 + src/plugins/nodes/listener.ts | 3 + src/plugins/nodes/othersIdle.ts | 16 + src/plugins/nodes/outputLog.ts | 9 + src/plugins/nodes/twitchBanUser.ts | 9 + src/plugins/nodes/twitchSendMessage.ts | 10 + src/plugins/nodes/twitchTimeoutUser.ts | 17 + src/plugins/nodes/variableSaveToDatabase.ts | 12 + src/plugins/nodes/variableSetVariable.ts | 19 + src/plugins/template.ts | 29 + src/services/twitch/chat.ts | 3 + 28 files changed, 879 insertions(+), 259 deletions(-) create mode 100644 d.ts/src/plugins.d.ts create mode 100644 locales/en/ui/errors.json create mode 100644 locales/en/ui/registry/plugins.json create mode 100644 src/database/entity/plugins.ts create mode 100644 src/database/migration/mysql/14.0.x/1650978850216-addPluginTable.ts create mode 100644 src/database/migration/postgres/14.0.x/1650978850216-addPluginTable.ts create mode 100644 src/database/migration/sqlite/14.0.x/1650978850216-addPluginTable.ts create mode 100644 src/plugins.ts create mode 100644 src/plugins/index.ts create mode 100644 src/plugins/nodes/filter.ts create mode 100644 src/plugins/nodes/filterPermission.ts create mode 100644 src/plugins/nodes/listener.ts create mode 100644 src/plugins/nodes/othersIdle.ts create mode 100644 src/plugins/nodes/outputLog.ts create mode 100644 src/plugins/nodes/twitchBanUser.ts create mode 100644 src/plugins/nodes/twitchSendMessage.ts create mode 100644 src/plugins/nodes/twitchTimeoutUser.ts create mode 100644 src/plugins/nodes/variableSaveToDatabase.ts create mode 100644 src/plugins/nodes/variableSetVariable.ts create mode 100644 src/plugins/template.ts diff --git a/d.ts/src/helpers/socket.d.ts b/d.ts/src/helpers/socket.d.ts index c7123835671..b05bce01a24 100644 --- a/d.ts/src/helpers/socket.d.ts +++ b/d.ts/src/helpers/socket.d.ts @@ -30,9 +30,12 @@ import type { } from '@entity/user'; import type { VariableInterface, VariableWatchInterface } from '@entity/variable'; import { HelixVideo } from '@twurple/api/lib'; +import { ValidationError } from 'class-validator'; import { Socket } from 'socket.io'; import { FindConditions } from 'typeorm'; +import { Plugin } from '~/database/entity/plugins'; + type Configuration = { [x:string]: Configuration | string; }; @@ -69,11 +72,22 @@ type GenericEvents = { type generic> = { getAll: (cb: (error: Error | string | null, items: Readonly>[]) => void) => void, getOne: (id: Required, cb: (error: Error | string | null, item?: Readonly>) => void) => void, - setById: (opts: { id: Required, item: Partial }, cb: (error: Error | string | null, item?: Readonly> | null) => void) => void, + setById: (opts: { id: Required, item: Partial }, cb: (error: ValidationError[] | Error | string | null, item?: Readonly> | null) => void) => void, + save: (item: Partial, cb: (error: ValidationError[] | Error | string | null, item?: Readonly> | null) => void) => void, deleteById: (id: Required, cb: (error: Error | string | null) => void) => void; + validate: (item: Partial, cb: (error: ValidationError[] | Error | string | null) => void) => void, }; export type ClientToServerEventsWithNamespace = { + '/core/plugins': GenericEvents & { + 'listeners': (cb: (listeners: Record) => void) => void, + 'generic::getOne': generic['getOne'], + 'generic::getAll': generic['getAll'], + 'generic::save': generic['save'], + 'generic::deleteById': generic['deleteById'], + 'generic::validate': generic['validate'], + + }, '/core/emotes': GenericEvents & { 'testExplosion': (cb: (err: Error | string | null, data: null ) => void) => void, 'testFireworks': (cb: (err: Error | string | null, data: null ) => void) => void, diff --git a/d.ts/src/plugins.d.ts b/d.ts/src/plugins.d.ts new file mode 100644 index 00000000000..65e5181b608 --- /dev/null +++ b/d.ts/src/plugins.d.ts @@ -0,0 +1,15 @@ +export type Node = { + id: number, + name: string, + data: { value: T, data: string }, + class: string, + html: string, + inputs: { input_1: { connections: { + node: string, + }[] }} | Record, + outputs: { output_1: { connections: { + node: string, + }[] }, output_2: { connections: { + node: string, + }[] }}, +}; \ No newline at end of file diff --git a/locales/en/ui.menu.json b/locales/en/ui.menu.json index 5b87ea76504..e467d3e640c 100644 --- a/locales/en/ui.menu.json +++ b/locales/en/ui.menu.json @@ -97,5 +97,6 @@ "obswebsocket": "OBS Websocket", "api-explorer": "API Explorer", "emotescombo": "Emotes Combo", - "notifications": "Notifications" + "notifications": "Notifications", + "plugins": "Plugins" } diff --git a/locales/en/ui/errors.json b/locales/en/ui/errors.json new file mode 100644 index 00000000000..78fd7cb19eb --- /dev/null +++ b/locales/en/ui/errors.json @@ -0,0 +1,3 @@ +{ + "isNotEmpty": "This field is required." +} \ No newline at end of file diff --git a/locales/en/ui/registry/alerts.json b/locales/en/ui/registry/alerts.json index 52c7606536c..28674e0843c 100644 --- a/locales/en/ui/registry/alerts.json +++ b/locales/en/ui/registry/alerts.json @@ -65,7 +65,7 @@ "name": "Variant occurence" }, "filter": { - "name": "Occurence filter", + "name": "Filter", "operator": "Operator", "rule": "Rule", "addRule": "Add rule", diff --git a/locales/en/ui/registry/plugins.json b/locales/en/ui/registry/plugins.json new file mode 100644 index 00000000000..a9316dbefbd --- /dev/null +++ b/locales/en/ui/registry/plugins.json @@ -0,0 +1,41 @@ +{ + "common-errors": { + "missing-sender-attributes": "This node needs to be linked with listeners with sender attributes" + }, + "filter": { + "permission": { + "name": "Permission filter" + } + }, + "listener": { + "name": "Event listener", + "type": { + "twitchChatMessage": "Twitch chat message", + "twitchCommand": "Twitch command" + }, + "command": { + "add-parameter": "Add parameter", + "parameters": "Parameters", + "order-is-important": "order is important" + } + }, + "others": { + "idle": { + "name": "Idle" + } + }, + "output": { + "log": { + "name": "Log message" + }, + "timeout-user": { + "name": "Timeout user" + }, + "ban-user": { + "name": "Ban user" + }, + "send-twitch-message": { + "name": "Send Twitch Message" + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4b4ae8d2340..b5fddee02c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "@sogebot/backend", - "version": "14.30.0-SNAPSHOT", + "version": "14.32.0-SNAPSHOT", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sogebot/backend", - "version": "14.30.0-SNAPSHOT", + "version": "14.32.0-SNAPSHOT", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@sogebot/ui-admin": "^46.1.0", + "@sogebot/ui-admin": "^48.0.0", "@sogebot/ui-helpers": "^3.0.10", "@sogebot/ui-oauth": "~3.1.0", - "@sogebot/ui-overlay": "^24.0.0", + "@sogebot/ui-overlay": "^24.1.1", "@sogebot/ui-public": "^2.0.0", "@twurple/api": "^5.1.6", "@twurple/chat": "^5.1.6", @@ -24,12 +24,13 @@ "better-sqlite3": "^7.5.1", "blocked-at": "1.2.0", "chalk": "4.1.2", + "class-validator": "^0.13.2", "cors": "2.8.5", "cron-parser": "4.4.0", "cross-env": "7.0.3", "crypto-browserify": "3.12.0", "currency-symbol-map": "5.0.1", - "dayjs": "1.11.1", + "dayjs": "1.11.2", "decode-html": "2.0.0", "discord.js": "13.6.0", "dotenv": "11.0.0", @@ -48,13 +49,13 @@ "jsonwebtoken": "8.5.1", "localtunnel": "^2.0.2", "lodash": "4.17.21", - "mathjs": "10.5.1", + "mathjs": "10.5.3", "mkdir": "0.0.2", "multer": "^1.4.4", "mysql2": "2.3.3", "node-fetch": "^2.6.6", "node-hue-api": "3.4.3", - "npm": "^8.9.0", + "npm": "^8.10.0", "npm-check-updates": "^12.5.11", "obs-websocket-js": "4.0.3", "patch-package": "6.4.7", @@ -84,7 +85,7 @@ "vm2": "^3.9.9", "ws": "8.6.0", "xregexp": "5.1.0", - "yargs": "17.4.1", + "yargs": "17.5.0", "ytdl-core": "4.11.0", "ytpl": "2.3.0", "ytsr": "3.8.0" @@ -1842,9 +1843,9 @@ } }, "node_modules/@d-fischer/cache-decorators/node_modules/@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" }, "node_modules/@d-fischer/connection": { "version": "6.6.2", @@ -1949,9 +1950,9 @@ } }, "node_modules/@d-fischer/rate-limiter/node_modules/@types/node": { - "version": "12.20.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.50.tgz", - "integrity": "sha512-+9axpWx2b2JCVovr7Ilgt96uc6C1zBKOQMpGtRbWT9IoR/8ue32GGMfGA4woP8QyP2gBs6GQWEVM3tCybGCxDA==" + "version": "12.20.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.51.tgz", + "integrity": "sha512-anVDMfReTatfH8GVmHmaTZOL0jeTLNZ9wK9SSrQS3tMmn4vUc+9fVWlUzAieuQefWDyWUz4Z3aqXxDgO1VsYjg==" }, "node_modules/@d-fischer/shared-utils": { "version": "3.2.0", @@ -1966,9 +1967,9 @@ } }, "node_modules/@d-fischer/shared-utils/node_modules/@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" }, "node_modules/@d-fischer/typed-event-emitter": { "version": "3.2.3", @@ -1983,9 +1984,9 @@ } }, "node_modules/@d-fischer/typed-event-emitter/node_modules/@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" }, "node_modules/@discordjs/builders": { "version": "0.11.0", @@ -2235,9 +2236,9 @@ "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.10.tgz", - "integrity": "sha512-Q0YbBd6OTsXm8Y21+YUSDXupHnodNC2M4O18jtd3iwJ3+vMZNdKGols0a9G6JOK0dcJ3IdUUHoh908ZI6qhk8Q==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -2307,9 +2308,9 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", "engines": { "node": ">=12" } @@ -3130,10 +3131,13 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@sogebot/ui-admin": { - "version": "46.2.1", - "resolved": "https://registry.npmjs.org/@sogebot/ui-admin/-/ui-admin-46.2.1.tgz", - "integrity": "sha512-Yvlb78tnS+PQ0w0KtWkAYQRi8L7UOHVZD+ndQcmh50keaxaVfRjdze7VoWNdn5ifyPnx25tz9EWaRym5NXl6CQ==", + "version": "48.0.0", + "resolved": "https://registry.npmjs.org/@sogebot/ui-admin/-/ui-admin-48.0.0.tgz", + "integrity": "sha512-ggtUbicnaVqEC9sT67iNaFFNFJJaPMy+/g4M49g0/FPptf3O0q1/ts80CsSAaAdGFEQbCz7VV38MJwl8CpsOuA==", "hasInstallScript": true, + "dependencies": { + "drawflow": "^0.0.58" + }, "engines": { "npm": ">=8.0.0" } @@ -3173,9 +3177,9 @@ } }, "node_modules/@sogebot/ui-overlay": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/@sogebot/ui-overlay/-/ui-overlay-24.0.0.tgz", - "integrity": "sha512-6MQ4V7W2SCkFlLnbtSrUSWaGXWS8CRZr59jVJiosJzfAsYPa6xroQrhQgzbX9bBsC+ENbE+neDJW7L0DNziUJQ==", + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/@sogebot/ui-overlay/-/ui-overlay-24.1.1.tgz", + "integrity": "sha512-xSz9JcMRGoJIVvaPluC7FnAqoOS25mul7QobdJsp3N7CfhKswXF2w0GxSryVfeKdIu4CAY/h0+i6nu2YHEno6g==", "hasInstallScript": true, "engines": { "npm": ">7.19.0" @@ -3348,9 +3352,9 @@ } }, "node_modules/@twurple/chat/node_modules/@types/node": { - "version": "12.20.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.50.tgz", - "integrity": "sha512-+9axpWx2b2JCVovr7Ilgt96uc6C1zBKOQMpGtRbWT9IoR/8ue32GGMfGA4woP8QyP2gBs6GQWEVM3tCybGCxDA==" + "version": "12.20.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.51.tgz", + "integrity": "sha512-anVDMfReTatfH8GVmHmaTZOL0jeTLNZ9wK9SSrQS3tMmn4vUc+9fVWlUzAieuQefWDyWUz4Z3aqXxDgO1VsYjg==" }, "node_modules/@twurple/common": { "version": "5.1.6", @@ -3646,7 +3650,8 @@ "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==" + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true }, "node_modules/@types/module-alias": { "version": "2.0.1", @@ -6011,9 +6016,9 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", "engines": { "node": ">=12" } @@ -6248,9 +6253,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001338", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001338.tgz", - "integrity": "sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ==", + "version": "1.0.30001340", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001340.tgz", + "integrity": "sha512-jUNz+a9blQTQVu4uFcn17uAD8IDizPzQkIKh3LCJfg9BkyIqExYYdyc/ZSlWUSKb8iYiXxKsxbv4zYSvkqjrxw==", "funding": [ { "type": "opencollective", @@ -6384,9 +6389,9 @@ } }, "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.1.tgz", + "integrity": "sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==" }, "node_modules/cint": { "version": "8.2.1", @@ -6420,7 +6425,6 @@ "version": "0.13.2", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", - "peer": true, "dependencies": { "libphonenumber-js": "^1.9.43", "validator": "^13.7.0" @@ -7195,9 +7199,9 @@ } }, "node_modules/core-js": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.4.tgz", - "integrity": "sha512-1uLykR+iOfYja+6Jn/57743gc9n73EWiOnSJJ4ba3B4fOEYDBv25MagmEZBxTp5cWq4b/KPx/l77zgsp28ju4w==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.5.tgz", + "integrity": "sha512-VP/xYuvJ0MJWRAobcmQ8F2H6Bsn+s7zqAAjFaHGBMc5AQm7zaelhD1LGduFn2EehEcQcU+br6t+fwbpQ5d1ZWA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -7205,9 +7209,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.4.tgz", - "integrity": "sha512-dIWcsszDezkFZrfm1cnB4f/J85gyhiCpxbgBdohWCDtSVuAaChTSpPV7ldOQf/Xds2U5xCIJZOK82G4ZPAIswA==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.5.tgz", + "integrity": "sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg==", "dependencies": { "browserslist": "^4.20.3", "semver": "7.0.0" @@ -7226,9 +7230,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.4.tgz", - "integrity": "sha512-4iF+QZkpzIz0prAFuepmxwJ2h5t4agvE8WPYqs2mjLJMNNwJOnpch76w2Q7bUfCPEv/V7wpvOfog0w273M+ZSw==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.5.tgz", + "integrity": "sha512-8xo9R00iYD7TcV7OrC98GwxiUEAabVWO3dix+uyWjnYrx9fyASLlIX+f/3p5dW5qByaP2bcZ8X/T47s55et/tA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -7826,9 +7830,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz", - "integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==" + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz", + "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==" }, "node_modules/de-indent": { "version": "1.0.2", @@ -8664,9 +8668,9 @@ } }, "node_modules/dompurify": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.6.tgz", - "integrity": "sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.7.tgz", + "integrity": "sha512-fsVZLywBd3awZIG3qU4JEdw7DCb0uUCajTfWRrLhsgKjTBd5CIIluPoAkNfco05GuNYQGj4/+bQIhlq96eT9eQ==", "dev": true }, "node_modules/domutils": { @@ -8740,6 +8744,11 @@ "node": ">=12" } }, + "node_modules/drawflow": { + "version": "0.0.58", + "resolved": "https://registry.npmjs.org/drawflow/-/drawflow-0.0.58.tgz", + "integrity": "sha512-vvtDTuSPEFf3CPk86Z89YFXNwxALfio50FM8wDL328hK7sBha1V+eQfCFn+pV2m/f90XNg6rgKJYdOQ4QlQisA==" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -10472,9 +10481,9 @@ } }, "node_modules/foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", "dev": true }, "node_modules/foreground-child": { @@ -11094,9 +11103,9 @@ "dev": true }, "node_modules/globals": { - "version": "13.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.14.0.tgz", - "integrity": "sha512-ERO68sOYwm5UuLvSJTY7w7NP2c8S4UcXs3X1GBX8cwOr+ShOcDBbCY5mH4zxz0jsYCdJ8ve8Mv9n2YGJMB1aeg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -12437,9 +12446,9 @@ } }, "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" }, "node_modules/ip-regex": { "version": "4.3.0", @@ -12477,9 +12486,9 @@ } }, "node_modules/ircv3/node_modules/@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" }, "node_modules/is-absolute-url": { "version": "2.1.0", @@ -13748,11 +13757,11 @@ } }, "node_modules/keyv": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.2.tgz", - "integrity": "sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.7.tgz", + "integrity": "sha512-HeOstD8SXvtWoQhMMBCelcUuZsiV7T7MwsADtOXT0KuwYP9nCxrSoMDeLXNDTLN3VFSuRp38JzoGbbTboq3QQw==", "dependencies": { - "compress-brotli": "^1.3.6", + "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" } }, @@ -13884,6 +13893,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", + "deprecated": "This module is not used anymore. npm config is parsed by npm itself and by @npmcli/config", "dependencies": { "figgy-pudding": "^3.5.1", "find-up": "^3.0.0", @@ -13949,8 +13959,7 @@ "node_modules/libphonenumber-js": { "version": "1.9.53", "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz", - "integrity": "sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw==", - "peer": true + "integrity": "sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw==" }, "node_modules/lilconfig": { "version": "2.0.4", @@ -14651,9 +14660,9 @@ } }, "node_modules/make-fetch-happen": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.2.tgz", - "integrity": "sha512-GWMGiZsKVeJACQGJ1P3Z+iNec7pLsU6YW1q11eaPn3RR8nRXHppFWfP7Eu0//55JK3hSjrAQRl8sDa5uXpq1Ew==", + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.3.tgz", + "integrity": "sha512-s/UjmGjUHn9m52cctFhN2ITObbT+axoUhgeir8xGrOlPbKDyJsdhQzb8PGncPQQ28uduHybFJ6Iumy2OZnreXw==", "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.0.2", @@ -14677,9 +14686,9 @@ } }, "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", "engines": { "node": ">=12" } @@ -14739,12 +14748,11 @@ } }, "node_modules/mathjs": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.5.1.tgz", - "integrity": "sha512-xfVDhVB53HqogiSG+4NWAWW9Y3jiItuJv4G/8DC0sU8Qy31P+Pgr8yCzS074i2ZnEd3ekwzn30mdj1GOmNHHaw==", + "version": "10.5.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.5.3.tgz", + "integrity": "sha512-RtF+F7SdRIf2E2+btyGDhuB6wmPjOH9KeNglfclaaaI6eK7ZNfsQS6IRAKvGyNsr8sFc6Pyb3JBLqEhYBuknVQ==", "dependencies": { "@babel/runtime": "^7.17.9", - "@types/mocha": "^9.1.1", "complex.js": "^2.1.1", "decimal.js": "^10.3.1", "escape-latex": "^1.2.0", @@ -14758,7 +14766,7 @@ "mathjs": "bin/cli.js" }, "engines": { - "node": ">= 12" + "node": ">= 14" } }, "node_modules/md5.js": { @@ -16138,9 +16146,9 @@ } }, "node_modules/npm": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.9.0.tgz", - "integrity": "sha512-4mhU5nEv7ktvHmJnM2nmWP2Zk4cCsD26imX+dvZ76HOuFUnIpU6+i1MWuoMg8N/xWHQgB0d2/ybWEGgJR9M/pw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-8.10.0.tgz", + "integrity": "sha512-6oo65q9Quv9mRPGZJufmSH+C/UFdgelwzRXiglT/2mDB50zdy/lZK5dFY0TJ9fJ/8gHqnxcX1NM206KLjTBMlQ==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -16249,7 +16257,7 @@ "libnpmsearch": "^5.0.2", "libnpmteam": "^4.0.2", "libnpmversion": "^3.0.1", - "make-fetch-happen": "^10.1.2", + "make-fetch-happen": "^10.1.3", "minipass": "^3.1.6", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", @@ -16449,9 +16457,9 @@ } }, "node_modules/npm-check-updates/node_modules/lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", "engines": { "node": ">=12" } @@ -16571,9 +16579,9 @@ } }, "node_modules/npm-package-arg/node_modules/lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", "engines": { "node": ">=12" } @@ -16695,7 +16703,7 @@ "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.1.1", + "version": "5.2.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -17049,7 +17057,7 @@ } }, "node_modules/npm/node_modules/builtins": { - "version": "5.0.0", + "version": "5.0.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -17438,7 +17446,7 @@ } }, "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.0", + "version": "5.0.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -17541,7 +17549,7 @@ } }, "node_modules/npm/node_modules/ip": { - "version": "1.1.5", + "version": "1.1.8", "inBundle": true, "license": "MIT" }, @@ -17565,7 +17573,7 @@ } }, "node_modules/npm/node_modules/is-core-module": { - "version": "2.8.1", + "version": "2.9.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -17615,7 +17623,7 @@ "license": "MIT" }, "node_modules/npm/node_modules/just-diff": { - "version": "5.0.1", + "version": "5.0.2", "inBundle": true, "license": "MIT" }, @@ -17780,7 +17788,7 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "7.8.1", + "version": "7.9.0", "inBundle": true, "license": "ISC", "engines": { @@ -17788,7 +17796,7 @@ } }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.1.2", + "version": "10.1.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -18097,7 +18105,7 @@ } }, "node_modules/npm/node_modules/npm-packlist": { - "version": "5.0.2", + "version": "5.0.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -18529,13 +18537,13 @@ } }, "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "6.1.1", + "version": "6.2.0", "inBundle": true, "license": "MIT", "dependencies": { "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { "node": ">= 10" @@ -22192,9 +22200,9 @@ } }, "node_modules/read-package-json/node_modules/lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", "engines": { "node": ">=12" } @@ -25572,9 +25580,9 @@ "integrity": "sha512-IT3q0lPvtkqQ8toHQN/BkOi4VIqoqheqM1FnkNWT9y0G8B3xJhwnoKBu5OHx8zHDOvveQzfKuFowJ0VSARiIDg==" }, "node_modules/uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", + "version": "3.15.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz", + "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==", "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -26111,9 +26119,9 @@ } }, "node_modules/user-agents": { - "version": "1.0.1011", - "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1011.tgz", - "integrity": "sha512-CVaRv2e9zx5fsAZwMjnlcvQIz1bbN+J3xpEd154rAmRt/anouJpLod2B3U5tW33HoKULc8IPJJd06V1OIAJCBQ==", + "version": "1.0.1014", + "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1014.tgz", + "integrity": "sha512-aVL83+Q0D7xnGDdXs1Cq5nmAjdRMXqU4jLqHih+g5A5nD3RAXpe+H2OFQNYqYQhkzCostypwQvlk16gkacegrg==", "dependencies": { "dot-json": "^1.2.2", "lodash.clonedeep": "^4.5.0" @@ -26203,7 +26211,6 @@ "version": "13.7.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", - "peer": true, "engines": { "node": ">= 0.10" } @@ -28134,9 +28141,9 @@ } }, "node_modules/yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.0.tgz", + "integrity": "sha512-3sLxVhbAB5OC8qvVRebCLWuouhwh/rswsiDYx3WGxajUk/l4G20SKfrKKFeNIHboUFt2JFgv2yfn+5cgOr/t5A==", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -28326,9 +28333,9 @@ } }, "node_modules/zod": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.15.1.tgz", - "integrity": "sha512-WAdjcoOxa4S9oc/u7fTbC3CC7uVqptLLU0LKqS8RDBOrCXp2t5avM8BUfgNVZJymGWAx6SEUYxWPPoYuQ5rgwQ==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.16.0.tgz", + "integrity": "sha512-szrIkryADbTM+xBt2a1KoS2CJQXec4f9xG78bj5MJeEH/XqmmHpnO+fG3IE115AKBJak+2HrbxLZkc9mhdbDKA==", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -29491,9 +29498,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" } } }, @@ -29577,9 +29584,9 @@ }, "dependencies": { "@types/node": { - "version": "12.20.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.50.tgz", - "integrity": "sha512-+9axpWx2b2JCVovr7Ilgt96uc6C1zBKOQMpGtRbWT9IoR/8ue32GGMfGA4woP8QyP2gBs6GQWEVM3tCybGCxDA==" + "version": "12.20.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.51.tgz", + "integrity": "sha512-anVDMfReTatfH8GVmHmaTZOL0jeTLNZ9wK9SSrQS3tMmn4vUc+9fVWlUzAieuQefWDyWUz4Z3aqXxDgO1VsYjg==" } } }, @@ -29593,9 +29600,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" } } }, @@ -29609,9 +29616,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" } } }, @@ -29802,9 +29809,9 @@ "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" }, "@jridgewell/trace-mapping": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.10.tgz", - "integrity": "sha512-Q0YbBd6OTsXm8Y21+YUSDXupHnodNC2M4O18jtd3iwJ3+vMZNdKGols0a9G6JOK0dcJ3IdUUHoh908ZI6qhk8Q==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -29859,9 +29866,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==" }, "mkdirp": { "version": "1.0.4", @@ -30575,9 +30582,12 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "@sogebot/ui-admin": { - "version": "46.2.1", - "resolved": "https://registry.npmjs.org/@sogebot/ui-admin/-/ui-admin-46.2.1.tgz", - "integrity": "sha512-Yvlb78tnS+PQ0w0KtWkAYQRi8L7UOHVZD+ndQcmh50keaxaVfRjdze7VoWNdn5ifyPnx25tz9EWaRym5NXl6CQ==" + "version": "48.0.0", + "resolved": "https://registry.npmjs.org/@sogebot/ui-admin/-/ui-admin-48.0.0.tgz", + "integrity": "sha512-ggtUbicnaVqEC9sT67iNaFFNFJJaPMy+/g4M49g0/FPptf3O0q1/ts80CsSAaAdGFEQbCz7VV38MJwl8CpsOuA==", + "requires": { + "drawflow": "^0.0.58" + } }, "@sogebot/ui-helpers": { "version": "3.0.10", @@ -30615,9 +30625,9 @@ } }, "@sogebot/ui-overlay": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/@sogebot/ui-overlay/-/ui-overlay-24.0.0.tgz", - "integrity": "sha512-6MQ4V7W2SCkFlLnbtSrUSWaGXWS8CRZr59jVJiosJzfAsYPa6xroQrhQgzbX9bBsC+ENbE+neDJW7L0DNziUJQ==" + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/@sogebot/ui-overlay/-/ui-overlay-24.1.1.tgz", + "integrity": "sha512-xSz9JcMRGoJIVvaPluC7FnAqoOS25mul7QobdJsp3N7CfhKswXF2w0GxSryVfeKdIu4CAY/h0+i6nu2YHEno6g==" }, "@sogebot/ui-public": { "version": "2.0.0", @@ -30759,9 +30769,9 @@ } }, "@types/node": { - "version": "12.20.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.50.tgz", - "integrity": "sha512-+9axpWx2b2JCVovr7Ilgt96uc6C1zBKOQMpGtRbWT9IoR/8ue32GGMfGA4woP8QyP2gBs6GQWEVM3tCybGCxDA==" + "version": "12.20.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.51.tgz", + "integrity": "sha512-anVDMfReTatfH8GVmHmaTZOL0jeTLNZ9wK9SSrQS3tMmn4vUc+9fVWlUzAieuQefWDyWUz4Z3aqXxDgO1VsYjg==" } } }, @@ -31050,7 +31060,8 @@ "@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==" + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true }, "@types/module-alias": { "version": "2.0.1", @@ -32975,9 +32986,9 @@ } }, "lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==" }, "minimatch": { "version": "5.0.1", @@ -33157,9 +33168,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001338", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001338.tgz", - "integrity": "sha512-1gLHWyfVoRDsHieO+CaeYe7jSo/MT7D7lhaXUiwwbuR5BwQxORs0f1tAwUSQr3YbxRXJvxHM/PA5FfPQRnsPeQ==" + "version": "1.0.30001340", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001340.tgz", + "integrity": "sha512-jUNz+a9blQTQVu4uFcn17uAD8IDizPzQkIKh3LCJfg9BkyIqExYYdyc/ZSlWUSKb8iYiXxKsxbv4zYSvkqjrxw==" }, "caseless": { "version": "0.12.0", @@ -33247,9 +33258,9 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" }, "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.1.tgz", + "integrity": "sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==" }, "cint": { "version": "8.2.1", @@ -33280,7 +33291,6 @@ "version": "0.13.2", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", - "peer": true, "requires": { "libphonenumber-js": "^1.9.43", "validator": "^13.7.0" @@ -33906,14 +33916,14 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "core-js": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.4.tgz", - "integrity": "sha512-1uLykR+iOfYja+6Jn/57743gc9n73EWiOnSJJ4ba3B4fOEYDBv25MagmEZBxTp5cWq4b/KPx/l77zgsp28ju4w==" + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.5.tgz", + "integrity": "sha512-VP/xYuvJ0MJWRAobcmQ8F2H6Bsn+s7zqAAjFaHGBMc5AQm7zaelhD1LGduFn2EehEcQcU+br6t+fwbpQ5d1ZWA==" }, "core-js-compat": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.4.tgz", - "integrity": "sha512-dIWcsszDezkFZrfm1cnB4f/J85gyhiCpxbgBdohWCDtSVuAaChTSpPV7ldOQf/Xds2U5xCIJZOK82G4ZPAIswA==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.5.tgz", + "integrity": "sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg==", "requires": { "browserslist": "^4.20.3", "semver": "7.0.0" @@ -33927,9 +33937,9 @@ } }, "core-js-pure": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.4.tgz", - "integrity": "sha512-4iF+QZkpzIz0prAFuepmxwJ2h5t4agvE8WPYqs2mjLJMNNwJOnpch76w2Q7bUfCPEv/V7wpvOfog0w273M+ZSw==" + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.5.tgz", + "integrity": "sha512-8xo9R00iYD7TcV7OrC98GwxiUEAabVWO3dix+uyWjnYrx9fyASLlIX+f/3p5dW5qByaP2bcZ8X/T47s55et/tA==" }, "core-util-is": { "version": "1.0.2", @@ -34383,9 +34393,9 @@ } }, "dayjs": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz", - "integrity": "sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==" + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz", + "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==" }, "de-indent": { "version": "1.0.2", @@ -35038,9 +35048,9 @@ } }, "dompurify": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.6.tgz", - "integrity": "sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.7.tgz", + "integrity": "sha512-fsVZLywBd3awZIG3qU4JEdw7DCb0uUCajTfWRrLhsgKjTBd5CIIluPoAkNfco05GuNYQGj4/+bQIhlq96eT9eQ==", "dev": true }, "domutils": { @@ -35104,6 +35114,11 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-11.0.0.tgz", "integrity": "sha512-Fp/b504Y5W+e+FpCxTFMUZ7ZEQkQYF0rx+KZtmwixJxGQbLHrhCwo3FjZgNC8vIfrSi29PABNbMoCGD9YoiXbQ==" }, + "drawflow": { + "version": "0.0.58", + "resolved": "https://registry.npmjs.org/drawflow/-/drawflow-0.0.58.tgz", + "integrity": "sha512-vvtDTuSPEFf3CPk86Z89YFXNwxALfio50FM8wDL328hK7sBha1V+eQfCFn+pV2m/f90XNg6rgKJYdOQ4QlQisA==" + }, "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -36501,9 +36516,9 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", "dev": true }, "foreground-child": { @@ -36978,9 +36993,9 @@ } }, "globals": { - "version": "13.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.14.0.tgz", - "integrity": "sha512-ERO68sOYwm5UuLvSJTY7w7NP2c8S4UcXs3X1GBX8cwOr+ShOcDBbCY5mH4zxz0jsYCdJ8ve8Mv9n2YGJMB1aeg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -37993,9 +38008,9 @@ } }, "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" }, "ip-regex": { "version": "4.3.0", @@ -38024,9 +38039,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.16.tgz", - "integrity": "sha512-X3bUMdK/VmvrWdoTkz+VCn6nwKwrKCFTHtqwBIaQJNx4RUIBBUFXM00bqPz/DsDd+Icjmzm6/tyYZzeGVqb6/Q==" + "version": "14.18.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.17.tgz", + "integrity": "sha512-oajWz4kOajqpKJMPgnCvBajPq8QAvl2xIWoFjlAJPKGu6n7pjov5SxGE45a+0RxHDoo4ycOMoZw1SCOWtDERbw==" } } }, @@ -38967,11 +38982,11 @@ } }, "keyv": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.2.tgz", - "integrity": "sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.2.7.tgz", + "integrity": "sha512-HeOstD8SXvtWoQhMMBCelcUuZsiV7T7MwsADtOXT0KuwYP9nCxrSoMDeLXNDTLN3VFSuRp38JzoGbbTboq3QQw==", "requires": { - "compress-brotli": "^1.3.6", + "compress-brotli": "^1.3.8", "json-buffer": "3.0.1" } }, @@ -39136,8 +39151,7 @@ "libphonenumber-js": { "version": "1.9.53", "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz", - "integrity": "sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw==", - "peer": true + "integrity": "sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw==" }, "lilconfig": { "version": "2.0.4", @@ -39682,9 +39696,9 @@ } }, "make-fetch-happen": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.2.tgz", - "integrity": "sha512-GWMGiZsKVeJACQGJ1P3Z+iNec7pLsU6YW1q11eaPn3RR8nRXHppFWfP7Eu0//55JK3hSjrAQRl8sDa5uXpq1Ew==", + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.3.tgz", + "integrity": "sha512-s/UjmGjUHn9m52cctFhN2ITObbT+axoUhgeir8xGrOlPbKDyJsdhQzb8PGncPQQ28uduHybFJ6Iumy2OZnreXw==", "requires": { "agentkeepalive": "^4.2.1", "cacache": "^16.0.2", @@ -39705,9 +39719,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==" } } }, @@ -39745,12 +39759,11 @@ "dev": true }, "mathjs": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.5.1.tgz", - "integrity": "sha512-xfVDhVB53HqogiSG+4NWAWW9Y3jiItuJv4G/8DC0sU8Qy31P+Pgr8yCzS074i2ZnEd3ekwzn30mdj1GOmNHHaw==", + "version": "10.5.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.5.3.tgz", + "integrity": "sha512-RtF+F7SdRIf2E2+btyGDhuB6wmPjOH9KeNglfclaaaI6eK7ZNfsQS6IRAKvGyNsr8sFc6Pyb3JBLqEhYBuknVQ==", "requires": { "@babel/runtime": "^7.17.9", - "@types/mocha": "^9.1.1", "complex.js": "^2.1.1", "decimal.js": "^10.3.1", "escape-latex": "^1.2.0", @@ -40865,9 +40878,9 @@ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" }, "npm": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.9.0.tgz", - "integrity": "sha512-4mhU5nEv7ktvHmJnM2nmWP2Zk4cCsD26imX+dvZ76HOuFUnIpU6+i1MWuoMg8N/xWHQgB0d2/ybWEGgJR9M/pw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-8.10.0.tgz", + "integrity": "sha512-6oo65q9Quv9mRPGZJufmSH+C/UFdgelwzRXiglT/2mDB50zdy/lZK5dFY0TJ9fJ/8gHqnxcX1NM206KLjTBMlQ==", "requires": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^5.0.4", @@ -40904,7 +40917,7 @@ "libnpmsearch": "^5.0.2", "libnpmteam": "^4.0.2", "libnpmversion": "^3.0.1", - "make-fetch-happen": "^10.1.2", + "make-fetch-happen": "^10.1.3", "minipass": "^3.1.6", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", @@ -40955,7 +40968,7 @@ "bundled": true }, "@npmcli/arborist": { - "version": "5.1.1", + "version": "5.2.0", "bundled": true, "requires": { "@isaacs/string-locale-compare": "^1.1.0", @@ -41201,7 +41214,7 @@ } }, "builtins": { - "version": "5.0.0", + "version": "5.0.1", "bundled": true, "requires": { "semver": "^7.0.0" @@ -41465,7 +41478,7 @@ } }, "https-proxy-agent": { - "version": "5.0.0", + "version": "5.0.1", "bundled": true, "requires": { "agent-base": "6", @@ -41536,7 +41549,7 @@ } }, "ip": { - "version": "1.1.5", + "version": "1.1.8", "bundled": true }, "ip-regex": { @@ -41551,7 +41564,7 @@ } }, "is-core-module": { - "version": "2.8.1", + "version": "2.9.0", "bundled": true, "requires": { "has": "^1.0.3" @@ -41582,7 +41595,7 @@ "bundled": true }, "just-diff": { - "version": "5.0.1", + "version": "5.0.2", "bundled": true }, "just-diff-apply": { @@ -41701,11 +41714,11 @@ } }, "lru-cache": { - "version": "7.8.1", + "version": "7.9.0", "bundled": true }, "make-fetch-happen": { - "version": "10.1.2", + "version": "10.1.3", "bundled": true, "requires": { "agentkeepalive": "^4.2.1", @@ -41916,7 +41929,7 @@ } }, "npm-packlist": { - "version": "5.0.2", + "version": "5.0.3", "bundled": true, "requires": { "glob": "^8.0.1", @@ -42197,12 +42210,12 @@ } }, "socks-proxy-agent": { - "version": "6.1.1", + "version": "6.2.0", "bundled": true, "requires": { "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" + "debug": "^4.3.3", + "socks": "^2.6.2" } }, "spdx-correct": { @@ -42476,9 +42489,9 @@ } }, "lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==" }, "minimatch": { "version": "5.0.1", @@ -42564,9 +42577,9 @@ } }, "lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==" } } }, @@ -45360,9 +45373,9 @@ } }, "lru-cache": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.9.0.tgz", - "integrity": "sha512-lkcNMUKqdJk96TuIXUidxaPuEg5sJo/+ZyVE2BDFnuZGzwXem7d8582eG8vbu4todLfT14snP6iHriCHXXi5Rw==" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==" }, "minimatch": { "version": "5.0.1", @@ -47946,9 +47959,9 @@ "integrity": "sha512-IT3q0lPvtkqQ8toHQN/BkOi4VIqoqheqM1FnkNWT9y0G8B3xJhwnoKBu5OHx8zHDOvveQzfKuFowJ0VSARiIDg==" }, "uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==" + "version": "3.15.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.5.tgz", + "integrity": "sha512-hNM5q5GbBRB5xB+PMqVRcgYe4c8jbyZ1pzZhS6jbq54/4F2gFK869ZheiE5A8/t+W5jtTNpWef/5Q9zk639FNQ==" }, "unbox-primitive": { "version": "1.0.2", @@ -48345,9 +48358,9 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, "user-agents": { - "version": "1.0.1011", - "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1011.tgz", - "integrity": "sha512-CVaRv2e9zx5fsAZwMjnlcvQIz1bbN+J3xpEd154rAmRt/anouJpLod2B3U5tW33HoKULc8IPJJd06V1OIAJCBQ==", + "version": "1.0.1014", + "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.1014.tgz", + "integrity": "sha512-aVL83+Q0D7xnGDdXs1Cq5nmAjdRMXqU4jLqHih+g5A5nD3RAXpe+H2OFQNYqYQhkzCostypwQvlk16gkacegrg==", "requires": { "dot-json": "^1.2.2", "lodash.clonedeep": "^4.5.0" @@ -48427,8 +48440,7 @@ "validator": { "version": "13.7.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", - "peer": true + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" }, "vary": { "version": "1.1.2", @@ -49947,9 +49959,9 @@ } }, "yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.0.tgz", + "integrity": "sha512-3sLxVhbAB5OC8qvVRebCLWuouhwh/rswsiDYx3WGxajUk/l4G20SKfrKKFeNIHboUFt2JFgv2yfn+5cgOr/t5A==", "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -50089,9 +50101,9 @@ } }, "zod": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.15.1.tgz", - "integrity": "sha512-WAdjcoOxa4S9oc/u7fTbC3CC7uVqptLLU0LKqS8RDBOrCXp2t5avM8BUfgNVZJymGWAx6SEUYxWPPoYuQ5rgwQ==" + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.16.0.tgz", + "integrity": "sha512-szrIkryADbTM+xBt2a1KoS2CJQXec4f9xG78bj5MJeEH/XqmmHpnO+fG3IE115AKBJak+2HrbxLZkc9mhdbDKA==" } } } diff --git a/package.json b/package.json index 3e3bf655002..f6507e6eaeb 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ "author": "Michal Orlik ", "license": "MIT", "dependencies": { - "@sogebot/ui-admin": "^46.1.0", + "@sogebot/ui-admin": "^48.0.0", "@sogebot/ui-helpers": "^3.0.10", "@sogebot/ui-oauth": "~3.1.0", - "@sogebot/ui-overlay": "^24.0.0", + "@sogebot/ui-overlay": "^24.1.1", "@sogebot/ui-public": "^2.0.0", "@twurple/api": "^5.1.6", "@twurple/chat": "^5.1.6", @@ -25,12 +25,13 @@ "better-sqlite3": "^7.5.1", "blocked-at": "1.2.0", "chalk": "4.1.2", + "class-validator": "^0.13.2", "cors": "2.8.5", "cron-parser": "4.4.0", "cross-env": "7.0.3", "crypto-browserify": "3.12.0", "currency-symbol-map": "5.0.1", - "dayjs": "1.11.1", + "dayjs": "1.11.2", "decode-html": "2.0.0", "discord.js": "13.6.0", "dotenv": "11.0.0", @@ -49,13 +50,13 @@ "jsonwebtoken": "8.5.1", "localtunnel": "^2.0.2", "lodash": "4.17.21", - "mathjs": "10.5.1", + "mathjs": "10.5.3", "mkdir": "0.0.2", "multer": "^1.4.4", "mysql2": "2.3.3", "node-fetch": "^2.6.6", "node-hue-api": "3.4.3", - "npm": "^8.9.0", + "npm": "^8.10.0", "npm-check-updates": "^12.5.11", "obs-websocket-js": "4.0.3", "patch-package": "6.4.7", @@ -85,7 +86,7 @@ "vm2": "^3.9.9", "ws": "8.6.0", "xregexp": "5.1.0", - "yargs": "17.4.1", + "yargs": "17.5.0", "ytdl-core": "4.11.0", "ytpl": "2.3.0", "ytsr": "3.8.0" @@ -178,4 +179,4 @@ "npx jsonlint" ] } -} \ No newline at end of file +} diff --git a/src/database/entity/plugins.ts b/src/database/entity/plugins.ts new file mode 100644 index 00000000000..7e73407c01b --- /dev/null +++ b/src/database/entity/plugins.ts @@ -0,0 +1,34 @@ +import { IsNotEmpty } from 'class-validator'; +import { Entity, PrimaryColumn, Column, BaseEntity } from 'typeorm'; + +import { translate } from '../../translate'; + +@Entity() +export class Plugin extends BaseEntity { + + @PrimaryColumn() + id: string; + + @Column() + @IsNotEmpty({ message: () => translate('ui.errors.isNotEmpty') }) + name: string; + + @Column() + enabled: boolean; + + @Column({ type: (process.env.TYPEORM_CONNECTION ?? 'better-sqlite3') === 'mysql' ? 'longtext' : 'text' }) + workflow: string; +} + +@Entity() +export class PluginVariable extends BaseEntity { + + @PrimaryColumn() + variableName: string; + + @PrimaryColumn() + pluginId: string; + + @Column({ type: (process.env.TYPEORM_CONNECTION ?? 'better-sqlite3') === 'mysql' ? 'longtext' : 'text' }) + value: string; +} \ No newline at end of file diff --git a/src/database/migration/mysql/14.0.x/1650978850216-addPluginTable.ts b/src/database/migration/mysql/14.0.x/1650978850216-addPluginTable.ts new file mode 100644 index 00000000000..fe2a6f80b7d --- /dev/null +++ b/src/database/migration/mysql/14.0.x/1650978850216-addPluginTable.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addPluginTable1650978850216 implements MigrationInterface { + name = 'addPluginTable1650978850216'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE \`plugin\` (\`id\` varchar(255) NOT NULL, \`name\` varchar(255) NOT NULL, \`enabled\` tinyint NOT NULL, \`workflow\` longtext NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + await queryRunner.query(`CREATE TABLE \`plugin_variable\` (\`variableName\` varchar(255) NOT NULL, \`pluginId\` varchar(255) NOT NULL, \`value\` longtext NOT NULL, PRIMARY KEY (\`variableName\`, \`pluginId\`)) ENGINE=InnoDB`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`plugin_variable\``); + await queryRunner.query(`DROP TABLE \`plugin\``); + } + +} diff --git a/src/database/migration/postgres/14.0.x/1650978850216-addPluginTable.ts b/src/database/migration/postgres/14.0.x/1650978850216-addPluginTable.ts new file mode 100644 index 00000000000..dfe2f6a4820 --- /dev/null +++ b/src/database/migration/postgres/14.0.x/1650978850216-addPluginTable.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addPluginTable1650978850216 implements MigrationInterface { + name = 'addPluginTable1650978850216'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "plugin" ("id" character varying NOT NULL, "name" character varying NOT NULL, "enabled" boolean NOT NULL, "workflow" text NOT NULL, CONSTRAINT "PK_9a65387180b2e67287345684c03" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE TABLE "plugin_variable" ("variableName" character varying NOT NULL, "pluginId" character varying NOT NULL, "value" text NOT NULL, CONSTRAINT "PK_8c7cf84aebae071dcbdb47381d6" PRIMARY KEY ("variableName", "pluginId"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "plugin_variable"`); + await queryRunner.query(`DROP TABLE "plugin"`); + } + +} diff --git a/src/database/migration/sqlite/14.0.x/1650978850216-addPluginTable.ts b/src/database/migration/sqlite/14.0.x/1650978850216-addPluginTable.ts new file mode 100644 index 00000000000..5f2a851d0cd --- /dev/null +++ b/src/database/migration/sqlite/14.0.x/1650978850216-addPluginTable.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addPluginTable1650978850216 implements MigrationInterface { + name = 'addPluginTable1650978850216'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "plugin" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "workflow" text NOT NULL, "enabled" boolean NOT NULL)`); + await queryRunner.query(`CREATE TABLE "plugin_variable" ("variableName" varchar NOT NULL, "pluginId" varchar NOT NULL, "value" text NOT NULL, PRIMARY KEY ("variableName", "pluginId"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "plugin"`); + } + +} diff --git a/src/helpers/errors.ts b/src/helpers/errors.ts index 8ef3f9c0dac..9a718db25d3 100644 --- a/src/helpers/errors.ts +++ b/src/helpers/errors.ts @@ -1,2 +1,9 @@ +import { ValidationError } from 'class-validator'; +import { isArray } from 'lodash'; + export class UnauthorizedError extends Error {} -export class TokenError extends Error {} \ No newline at end of file +export class TokenError extends Error {} + +export function isValidationError(e: unknown): e is ValidationError[] { + return isArray(e) && e.length > 0 && e[0].constraints; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 5e6bcc78460..2c64263d7fd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -109,6 +109,7 @@ async function main () { require('./stats'); require('./users'); require('./events'); + require('./plugins'); require('./customvariables'); require('./permissions'); require('./updater'); diff --git a/src/plugins.ts b/src/plugins.ts new file mode 100644 index 00000000000..1be06d2ac83 --- /dev/null +++ b/src/plugins.ts @@ -0,0 +1,223 @@ +import { validateOrReject } from 'class-validator'; +import { cloneDeep } from 'lodash'; +import merge from 'lodash/merge'; + +import type { Node } from '../d.ts/src/plugins'; +import { Plugin, PluginVariable } from './database/entity/plugins'; +import { isValidationError } from './helpers/errors'; +import { adminEndpoint } from './helpers/socket'; +import { processes, processNode } from './plugins/index'; + +import Core from '~/_interface'; +import { onStartup } from '~/decorators/on'; + +const twitchChatMessage = { + sender: { + userName: 'string', + userId: 'string', + }, + message: 'string', +}; +const twitchCommand = { + sender: { + userName: 'string', + userId: 'string', + }, + message: 'string', +}; + +const generateRegex = (parameters: { name: string; type: 'number' | 'word' | 'sentence'; }[]) => { + const matcher = { + 'number': '[0-9]+', + 'word': '[a-zA-Z]+', + 'sentence': '\'[a-zA-Z ]+\'', + } as const; + + const regex = []; + for (const param of parameters) { + regex.push(`(?<${param.name}>${matcher[param.type]})`); + } + return `^${regex.join(' ')}$`; +}; + +class Plugins extends Core { + listeners = { + twitchChatMessage, + twitchCommand, + } as const; + + @onStartup() + onStartup() { + this.addMenu({ + category: 'registry', name: 'plugins', id: 'registry/plugins', this: null, + }); + } + + sockets() { + adminEndpoint('/core/plugins', 'generic::getAll', async (cb) => { + cb(null, await Plugin.find()); + }); + adminEndpoint('/core/plugins', 'generic::getOne', async (id, cb) => { + cb(null, await Plugin.findOne(id)); + }); + adminEndpoint('/core/plugins', 'generic::deleteById', async (id, cb) => { + await Plugin.delete({ id }); + cb(null); + }); + adminEndpoint('/core/plugins', 'generic::validate', async (data, cb) => { + try { + const item = new Plugin(); + merge(item, data); + await validateOrReject(item); + cb(null); + } catch (e) { + if (e instanceof Error) { + cb(e.message); + } + if (isValidationError(e)) { + cb(e); + } + } + }); + adminEndpoint('/core/plugins', 'generic::save', async (item, cb) => { + try { + const itemToSave = new Plugin(); + merge(itemToSave, item); + await validateOrReject(itemToSave); + await itemToSave.save(); + cb(null, itemToSave); + } catch (e) { + if (e instanceof Error) { + cb(e.message, undefined); + } + if (isValidationError(e)) { + cb(e, undefined); + } + } + }); + adminEndpoint('/core/plugins', 'listeners', async (cb) => { + cb(this.listeners); + }); + } + + async processPath(pluginId: string, workflow: Node[], currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser ) { + parameters = cloneDeep(parameters); + variables = cloneDeep(variables); + + // we need to check inputs first (currently just for load variable) + if (currentNode.inputs.input_1) { + const inputs = currentNode.inputs.input_1.connections.map((item) => workflow.find(wItem => wItem.id === Number(item.node))); + for(const node of inputs) { + if (!node) { + continue; + } + switch(node.name) { + case 'variableLoadFromDatabase': { + const variableName = node.data.value; + const defaultValue = (JSON.parse(node.data.data) as any).value; + + const variable = await PluginVariable.findOne({ variableName, pluginId }); + variables[variableName] = variable ? JSON.parse(variable.value) : defaultValue; + break; + } + } + } + } + + const result = await processNode(currentNode.name as keyof typeof processes, pluginId, currentNode, parameters, variables, userstate); + const output = result ? 'output_1' : 'output_2'; + + if (currentNode.outputs[output]) { + const nodes = currentNode.outputs[output].connections.map((item) => workflow.find(wItem => wItem.id === Number(item.node))); + for (const node of nodes) { + if (!node) { + continue; + } + this.processPath(pluginId, workflow, node, parameters, variables, userstate); + } + } + } + + async process(type: keyof typeof this.listeners, message: string, userstate: ChatUser) { + const plugins = await Plugin.find({ enabled: true }); + const pluginsWithListener: Plugin[] = []; + for (const plugin of plugins) { + // explore drawflow + const workflow = Object.values( + JSON.parse(plugin.workflow).drawflow.Home.data + ) as Node[]; + + const listeners = workflow.filter((o: any) => { + let params: Record = {}; + const isListener = o.name === 'listener'; + const isType = o.data.value === type; + + params.message = message; + + if (isListener && isType) { + switch(type) { + case 'twitchCommand': { + const { command, parameters } = JSON.parse(o.data.data); + + const haveSubCommandOrParameters = message.split(' ').length > 1; + + const isStartingWithCommand = message.startsWith(`!${command}`); + const doesParametersMatch = () => { + try { + if (parameters.length === 0) { + throw new Error(); // not expecting params + } + const messageWithoutCommand = message.replace(`!${command}`, '').trim(); + const paramMatch = messageWithoutCommand.match(generateRegex(parameters as any)); + if (paramMatch && paramMatch.groups) { + const groups: { [key: string]: string | number; } = paramMatch.groups; + for (const param of parameters) { + if (param.type === 'number') { + groups[param.name] = Number(groups[param.name]); + } + } + params = paramMatch.groups; + return true; + } + return false; + } catch (e) { + // not valid regex or not expecting params + if (haveSubCommandOrParameters && message !== `!${command}`) { + return false; + } + return true; + } + }; + + if (isStartingWithCommand && doesParametersMatch()) { + this.processPath(plugin.id, workflow, o, params, {}, userstate); + } + break; + } + default: + this.processPath(plugin.id, workflow, o, params, {}, userstate); + return true; + } + } + return false; + }); + + if (listeners.length > 0) { + pluginsWithListener.push(plugin); + } + } + return pluginsWithListener; + } + + async trigger(type: 'message', message: string, userstate: ChatUser) { + switch(type) { + case 'message': { + this.process(message.startsWith('!') ? 'twitchCommand' : 'twitchChatMessage', message, userstate); + break; + } + default: + } + } +} + +export default new Plugins(); diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 00000000000..5311342ca51 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,38 @@ +import type { Node } from '../../d.ts/src/plugins'; +import filter from './nodes/filter'; +import filterPermission from './nodes/filterPermission'; +import listener from './nodes/listener'; +import othersIdle from './nodes/othersIdle'; +import outputLog from './nodes/outputLog'; +import twitchBanUser from './nodes/twitchBanUser'; +import twitchSendMessage from './nodes/twitchSendMessage'; +import twitchTimeoutUser from './nodes/twitchTimeoutUser'; +import variableSaveToDatabase from './nodes/variableSaveToDatabase'; +import variableSetVariable from './nodes/variableSetVariable'; + +import { warning } from '~/helpers/log'; + +export const processes = { + listener, + othersIdle, + outputLog, + filterPermission, + filter, + variableSaveToDatabase, + variableSetVariable, + twitchTimeoutUser, + twitchBanUser, + twitchSendMessage, + default: (pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) => { + warning(`PLUGINS: no idea what should I do with ${currentNode.name}, stopping`); + return true; + }, +}; + +function processNode (type: keyof typeof processes, pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser): Promise | boolean { + return (processes[processes[type] ? type : 'default'](pluginId, currentNode as any, parameters, variables, userstate)); +} + +export { + processNode, +}; \ No newline at end of file diff --git a/src/plugins/nodes/filter.ts b/src/plugins/nodes/filter.ts new file mode 100644 index 00000000000..1eeacfa1758 --- /dev/null +++ b/src/plugins/nodes/filter.ts @@ -0,0 +1,41 @@ +import { itemsToEvalPart } from '@sogebot/ui-helpers/queryFilter'; +import { VM } from 'vm2'; + +import type { Node } from '~/../d.ts/src/plugins'; +import { error } from '~/helpers/log'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + const advancedMode = JSON.parse(currentNode.data.data).advancedMode ?? false; + + let script = null; + + if (advancedMode) { + script = currentNode.data.value; + } else { + const filter = currentNode.data.value + ? JSON.parse(currentNode.data.value) : null; + if (filter && filter.items.length > 0) { + script = itemsToEvalPart(filter.items, filter.operator); + } + } + + if (!script) { + return false; + } + try { + const sandbox = { + sender: { + userName: userstate.userName, + userId: userstate.userId, + }, + ...parameters, + ...variables, + }; + const vm = new VM({ sandbox }); + const result = vm.run(`(function () { return ${script} })`)(); + return !!result; + } catch (e) { + error(`PLUGINS#${pluginId}: ${(e as Error).stack}`); + return false; + } +} \ No newline at end of file diff --git a/src/plugins/nodes/filterPermission.ts b/src/plugins/nodes/filterPermission.ts new file mode 100644 index 00000000000..f38ea5f593c --- /dev/null +++ b/src/plugins/nodes/filterPermission.ts @@ -0,0 +1,15 @@ +import type { Node } from '~/../d.ts/src/plugins'; +import { check } from '~/helpers/permissions/index'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + const permissionsAccessList = currentNode.data.value; + let haveAccess = false; + for (const permId of permissionsAccessList) { + if (haveAccess) { + break; + } + const status = await check(userstate.userId, permId); + haveAccess = status.access; + } + return haveAccess; +} \ No newline at end of file diff --git a/src/plugins/nodes/listener.ts b/src/plugins/nodes/listener.ts new file mode 100644 index 00000000000..94190c31c54 --- /dev/null +++ b/src/plugins/nodes/listener.ts @@ -0,0 +1,3 @@ +export default function () { + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/othersIdle.ts b/src/plugins/nodes/othersIdle.ts new file mode 100644 index 00000000000..baddb679458 --- /dev/null +++ b/src/plugins/nodes/othersIdle.ts @@ -0,0 +1,16 @@ +import { setTimeout } from 'timers/promises'; + +import { template } from '../../plugins/template'; + +import type { Node } from '~/../d.ts/src/plugins'; +import { warning } from '~/helpers/log'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + let miliseconds = await template(currentNode.data.value, { parameters, ...variables }); + if (isNaN(Number(miliseconds))) { + warning(`PLUGINS#${pluginId}: Idling value is not a number! Got: ${miliseconds}, defaulting to 1000`); + miliseconds = '1000'; + } + await setTimeout(Number(miliseconds)); + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/outputLog.ts b/src/plugins/nodes/outputLog.ts new file mode 100644 index 00000000000..2d811a3cdc4 --- /dev/null +++ b/src/plugins/nodes/outputLog.ts @@ -0,0 +1,9 @@ +import { template } from '../template'; + +import type { Node } from '~/../d.ts/src/plugins'; +import { info } from '~/helpers/log'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + info(`PLUGINS#${pluginId}: ${await template(currentNode.data.value, { parameters, ...variables })}`); + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/twitchBanUser.ts b/src/plugins/nodes/twitchBanUser.ts new file mode 100644 index 00000000000..44e063d274e --- /dev/null +++ b/src/plugins/nodes/twitchBanUser.ts @@ -0,0 +1,9 @@ +import type { Node } from '~/../d.ts/src/plugins'; +import { info } from '~/helpers/log'; +import { tmiEmitter } from '~/helpers/tmi'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + info(`PLUGINS#${pluginId}: Banning user ${userstate.userName}#${userstate.userId}`); + tmiEmitter.emit('ban', userstate.userName); + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/twitchSendMessage.ts b/src/plugins/nodes/twitchSendMessage.ts new file mode 100644 index 00000000000..b437d527d5a --- /dev/null +++ b/src/plugins/nodes/twitchSendMessage.ts @@ -0,0 +1,10 @@ +import { template } from '../template'; + +import type { Node } from '~/../d.ts/src/plugins'; +import { sendMessage } from '~/helpers/commons/sendMessage'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variablesArg: Record, userstate: ChatUser) { + const message = await template(currentNode.data.value, { parameters, ...variablesArg }, userstate); + sendMessage(message, userstate, { parameters, ...variablesArg }); + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/twitchTimeoutUser.ts b/src/plugins/nodes/twitchTimeoutUser.ts new file mode 100644 index 00000000000..e0bdbebfff8 --- /dev/null +++ b/src/plugins/nodes/twitchTimeoutUser.ts @@ -0,0 +1,17 @@ +import { template } from '../template'; + +import type { Node } from '~/../d.ts/src/plugins'; +import { info, warning } from '~/helpers/log'; +import { tmiEmitter } from '~/helpers/tmi'; +import { isModerator } from '~/helpers/user'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + let seconds = await template(currentNode.data.value, { parameters, ...variables }); + if (isNaN(Number(seconds))) { + warning(`PLUGINS#${pluginId}: Idling value is not a number! Got: ${seconds}, defaulting to 600s`); + seconds = '600'; + } + info(`PLUGINS#${pluginId}: Timeouting user ${userstate.userName}#${userstate.userId} for ${seconds}s`); + tmiEmitter.emit('timeout', userstate.userName, Number(seconds), isModerator(userstate)); + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/variableSaveToDatabase.ts b/src/plugins/nodes/variableSaveToDatabase.ts new file mode 100644 index 00000000000..31890a45267 --- /dev/null +++ b/src/plugins/nodes/variableSaveToDatabase.ts @@ -0,0 +1,12 @@ +import type { Node } from '~/../d.ts/src/plugins'; +import { PluginVariable } from '~/database/entity/plugins'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + const variableName = currentNode.data.value; + const variable = new PluginVariable(); + variable.variableName = variableName; + variable.pluginId = pluginId; + variable.value = JSON.stringify(variables[variableName]); + await variable.save(); + return true; +} \ No newline at end of file diff --git a/src/plugins/nodes/variableSetVariable.ts b/src/plugins/nodes/variableSetVariable.ts new file mode 100644 index 00000000000..5550e22d3a9 --- /dev/null +++ b/src/plugins/nodes/variableSetVariable.ts @@ -0,0 +1,19 @@ +import { VM } from 'vm2'; + +import { template } from '../template'; + +import type { Node } from '~/../d.ts/src/plugins'; + +export default async function(pluginId: string, currentNode: Node, parameters: Record, variables: Record, userstate: ChatUser) { + const variableName = currentNode.data.value; + const toEval = await template(JSON.parse(currentNode.data.data).value.trim(), { parameters, ...variables }); + + const vm = new VM({ + sandbox: { + parameters, ...variables, + }, + }); + const value = await vm.run(`(function () { return ${toEval} })`)(); + variables[variableName] = value; + return true; +} \ No newline at end of file diff --git a/src/plugins/template.ts b/src/plugins/template.ts new file mode 100644 index 00000000000..1a1e9315726 --- /dev/null +++ b/src/plugins/template.ts @@ -0,0 +1,29 @@ +import { getGlobalVariables } from '~/helpers/checkFilter'; +import { flatten } from '~/helpers/flatten'; +import { showWithAt } from '~/helpers/tmi'; + +export async function template(message: string, params: Record, userstate?: ChatUser) { + params = flatten(params); + const regexp = new RegExp(`{ *?(?[a-zA-Z0-9.]+) *?}`, 'g'); + const match = message.matchAll(regexp); + for (const item of match) { + message = message.replace(item[0], params[item[1]]); + } + + // global variables replacer + if (!message.includes('$')) { + // message doesn't have any variables + return message; + } + + const variables = await getGlobalVariables(message, { sender: userstate }); + for (const variable of Object.keys(variables)) { + const regexp2 = new RegExp(`\\${variable}`, 'g'); + message = message.replace(regexp2, String(variables[variable as keyof typeof variables] ?? '')); + } + + if (userstate) { + message = message.replace(/\$sender/g, showWithAt ? `@${userstate.userName}` : userstate.userName); + } + return message; +} \ No newline at end of file diff --git a/src/services/twitch/chat.ts b/src/services/twitch/chat.ts index 997b880d6c5..93fa0ef5fe8 100644 --- a/src/services/twitch/chat.ts +++ b/src/services/twitch/chat.ts @@ -896,6 +896,9 @@ class Chat { } } + // trigger plugins + (await import('../../plugins')).default.trigger('message', message, userstate); + if (!skip && !isNil(userName)) { const user = await changelog.get(userstate.userId); if (user) {