From a736f786c29b492e19769080eede54be7a4874e2 Mon Sep 17 00:00:00 2001 From: Danil Nikolaev Date: Tue, 3 Feb 2026 13:39:01 +0300 Subject: [PATCH] feat(openai): add chat streaming and model commands - add /openai (/chatgpt) chat command using OpenAI Responses API (streaming + incremental edits) - add /openAIListModels, /openAIGetModel, /openAISetModel - introduce base Command class and migrate non-chat commands to it - wire OpenAI client + env vars (OPENAI_API_KEY, OPENAI_MODEL) - bump deps (@google/genai, systeminformation, @types/node) and add openai --- bun.lock | 25 ++-- package-lock.json | 46 ++++++-- package.json | 7 +- src/base/chat-command.ts | 60 +--------- src/base/command.ts | 58 ++++++++++ src/commands/admins-add.ts | 4 +- src/commands/admins-remove.ts | 4 +- src/commands/ae.ts | 4 +- src/commands/ban.ts | 4 +- src/commands/choice.ts | 4 +- src/commands/coin.ts | 4 +- src/commands/debug.ts | 4 +- src/commands/dice.ts | 4 +- src/commands/distort.ts | 9 +- src/commands/gemini-chat.ts | 2 +- src/commands/gemini-generate-image.ts | 4 +- src/commands/gemini-get-model.ts | 4 +- src/commands/gemini-list-models.ts | 4 +- src/commands/gemini-set-model.ts | 4 +- src/commands/help.ts | 8 +- src/commands/id.ts | 4 +- src/commands/ignore.ts | 4 +- src/commands/leave.ts | 4 +- src/commands/mistral-chat.ts | 2 +- src/commands/mistral-get-model.ts | 4 +- src/commands/mistral-list-models.ts | 4 +- src/commands/mistral-set-model.ts | 4 +- src/commands/ollama-chat.ts | 10 +- src/commands/ollama-get-model.ts | 4 +- src/commands/ollama-list-models.ts | 4 +- src/commands/ollama-prompt.ts | 4 +- src/commands/ollama-search.ts | 4 +- src/commands/ollama-set-model.ts | 4 +- src/commands/openai-chat.ts | 160 ++++++++++++++++++++++++++ src/commands/openai-get-model.ts | 13 +++ src/commands/openai-list-models.ts | 37 ++++++ src/commands/openai-set-model.ts | 25 ++++ src/commands/ping.ts | 4 +- src/commands/prefix-response.ts | 4 +- src/commands/qr.ts | 4 +- src/commands/quote.ts | 4 +- src/commands/random-int.ts | 4 +- src/commands/random-string.ts | 4 +- src/commands/shutdown.ts | 4 +- src/commands/start.ts | 8 +- src/commands/system-info.ts | 4 +- src/commands/test.ts | 4 +- src/commands/title.ts | 4 +- src/commands/transliteration.ts | 4 +- src/commands/unban.ts | 4 +- src/commands/unignore.ts | 4 +- src/commands/uptime.ts | 4 +- src/commands/what-better.ts | 4 +- src/commands/when.ts | 4 +- src/commands/youtube-download.ts | 4 +- src/common/environment.ts | 10 ++ src/index.ts | 31 +++-- src/util/utils.ts | 52 +++++---- 58 files changed, 512 insertions(+), 211 deletions(-) create mode 100644 src/base/command.ts create mode 100644 src/commands/openai-chat.ts create mode 100644 src/commands/openai-get-model.ts create mode 100644 src/commands/openai-list-models.ts create mode 100644 src/commands/openai-set-model.ts diff --git a/bun.lock b/bun.lock index 9ba447d..a3d17f7 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "": { "name": "tg-chat-bot", "dependencies": { - "@google/genai": "^1.38.0", + "@google/genai": "^1.39.0", "@libsql/client": "^0.17.0", "@mistralai/mistralai": "^1.13.0", "@napi-rs/canvas": "^0.1.89", @@ -14,19 +14,20 @@ "drizzle-orm": "^1.0.0-beta.12-5845444", "emoji-regex": "^10.6.0", "ollama": "^0.6.3", + "openai": "^6.17.0", "puppeteer": "^24.36.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "qrcode": "^1.5.4", "sharp": "^0.34.5", - "systeminformation": "^5.30.6", + "systeminformation": "^5.30.7", "twemoji": "^14.0.2", "typescript-telegram-bot-api": "^0.11.0", "youtubei.js": "^16.0.1", }, "devDependencies": { "@types/bun": "^1.3.8", - "@types/node": "^25.1.0", + "@types/node": "^25.2.0", "@types/qrcode": "^1.5.6", "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", @@ -152,7 +153,7 @@ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], - "@google/genai": ["@google/genai@1.38.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-V/4CQVQGovvGHuS73lwJwHKR9x33kCij3zz/ReEQ4A7RJaV0U7m4k1mvYhFk55cGZdF5JLKu2S9BTaFuEs5xTA=="], + "@google/genai": ["@google/genai@1.39.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-Vz7AQsOdBeiIcxmXIQNy/hzDvyAOE1lSpWA10itUQza7h3aQFF6QSGaQ7o1GYsjMD3XslK4Ee/Ol0eLhRXb7gA=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -312,7 +313,7 @@ "@types/mssql": ["@types/mssql@9.1.8", "", { "dependencies": { "@types/node": "*", "tarn": "^3.0.1", "tedious": "*" } }, "sha512-mt9h5jWj+DYE5jxnKaWSV/GqDf9FV52XYVk6T3XZF69noEe+JJV6MKirii48l81+cjmAkSq+qeKX+k61fHkYrQ=="], - "@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="], + "@types/node": ["@types/node@25.2.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w=="], "@types/qrcode": ["@types/qrcode@1.5.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="], @@ -752,6 +753,8 @@ "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + "openai": ["openai@6.17.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], @@ -880,7 +883,7 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "systeminformation": ["systeminformation@5.30.6", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-LEIyK1aEv5P3BhAPW3swdlIyCihxwEq/Gki+kcONieU4PIeRCSLDuGkk0Va/56PSBgjVgEksOM88dmY6YqOyfQ=="], + "systeminformation": ["systeminformation@5.30.7", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-33B/cftpaWdpvH+Ho9U1b08ss8GQuLxrWHelbJT1yw4M48Taj8W3ezcPuaLoIHZz5V6tVHuQPr5BprEfnBLBMw=="], "tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], @@ -944,7 +947,7 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], @@ -974,6 +977,8 @@ "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + "@libsql/isomorphic-ws/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "@puppeteer/browsers/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "@types/mssql/@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], @@ -984,10 +989,14 @@ "@types/ws/@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], + "@types/yauzl/@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/utils/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + "bun-types/@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="], + "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -1006,8 +1015,6 @@ "protobufjs/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], - "puppeteer-core/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "puppeteer-extra-plugin-user-data-dir/fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], "puppeteer-extra-plugin-user-data-dir/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], diff --git a/package-lock.json b/package-lock.json index 85d9757..554af66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "tg-chat-bot", "version": "1.0.0", "dependencies": { - "@google/genai": "^1.38.0", + "@google/genai": "^1.39.0", "@libsql/client": "^0.17.0", "@mistralai/mistralai": "^1.13.0", "@napi-rs/canvas": "^0.1.89", @@ -17,19 +17,20 @@ "drizzle-orm": "^1.0.0-beta.12-5845444", "emoji-regex": "^10.6.0", "ollama": "^0.6.3", + "openai": "^6.17.0", "puppeteer": "^24.36.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "qrcode": "^1.5.4", "sharp": "^0.34.5", - "systeminformation": "^5.30.6", + "systeminformation": "^5.30.7", "twemoji": "^14.0.2", "typescript-telegram-bot-api": "^0.11.0", "youtubei.js": "^16.0.1" }, "devDependencies": { "@types/bun": "^1.3.8", - "@types/node": "^25.1.0", + "@types/node": "^25.2.0", "@types/qrcode": "^1.5.6", "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", @@ -975,9 +976,9 @@ } }, "node_modules/@google/genai": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.38.0.tgz", - "integrity": "sha512-V/4CQVQGovvGHuS73lwJwHKR9x33kCij3zz/ReEQ4A7RJaV0U7m4k1mvYhFk55cGZdF5JLKu2S9BTaFuEs5xTA==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.39.0.tgz", + "integrity": "sha512-Vz7AQsOdBeiIcxmXIQNy/hzDvyAOE1lSpWA10itUQza7h3aQFF6QSGaQ7o1GYsjMD3XslK4Ee/Ol0eLhRXb7gA==", "license": "Apache-2.0", "dependencies": { "google-auth-library": "^10.3.0", @@ -2242,9 +2243,9 @@ } }, "node_modules/@types/node": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", - "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", + "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -5303,6 +5304,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openai": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.17.0.tgz", + "integrity": "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6351,9 +6373,9 @@ } }, "node_modules/systeminformation": { - "version": "5.30.6", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.6.tgz", - "integrity": "sha512-LEIyK1aEv5P3BhAPW3swdlIyCihxwEq/Gki+kcONieU4PIeRCSLDuGkk0Va/56PSBgjVgEksOM88dmY6YqOyfQ==", + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.7.tgz", + "integrity": "sha512-33B/cftpaWdpvH+Ho9U1b08ss8GQuLxrWHelbJT1yw4M48Taj8W3ezcPuaLoIHZz5V6tVHuQPr5BprEfnBLBMw==", "license": "MIT", "os": [ "darwin", diff --git a/package.json b/package.json index 4ac3193..070800b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "bun:start": "bun run dist/index.js" }, "dependencies": { - "@google/genai": "^1.38.0", + "@google/genai": "^1.39.0", "@libsql/client": "^0.17.0", "@mistralai/mistralai": "^1.13.0", "@napi-rs/canvas": "^0.1.89", @@ -17,19 +17,20 @@ "drizzle-orm": "^1.0.0-beta.12-5845444", "emoji-regex": "^10.6.0", "ollama": "^0.6.3", + "openai": "^6.17.0", "puppeteer": "^24.36.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "qrcode": "^1.5.4", "sharp": "^0.34.5", - "systeminformation": "^5.30.6", + "systeminformation": "^5.30.7", "twemoji": "^14.0.2", "typescript-telegram-bot-api": "^0.11.0", "youtubei.js": "^16.0.1" }, "devDependencies": { "@types/bun": "^1.3.8", - "@types/node": "^25.1.0", + "@types/node": "^25.2.0", "@types/qrcode": "^1.5.6", "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", diff --git a/src/base/chat-command.ts b/src/base/chat-command.ts index c0ab79b..c13d8f2 100644 --- a/src/base/chat-command.ts +++ b/src/base/chat-command.ts @@ -1,58 +1,4 @@ -import {Message} from "typescript-telegram-bot-api"; -import {Requirements} from "./requirements"; +import {Command} from "./command"; -export type ArgsMode = "none" | "optional" | "required"; - -export abstract class ChatCommand { - - regexp?: RegExp | null; - command?: string | string[]; - argsMode: ArgsMode = "none"; - - requirements?: Requirements = null; - title?: string; - description?: string; - - get finalRegexp(): RegExp { - if (!this.regexp) { - const inferred = name(this.constructor.name); - const names = this.command ?? inferred; - this.regexp = createCommandRegExp(names, this.argsMode); - } - return this.regexp; - } - - abstract execute( - msg: Message, - match?: RegExpExecArray - ): Promise; -} - -export function name(s: string) { - return s - .replace(/Command$/, "") - .replace(/([a-z0-9])([A-Z])/g, "$1$2") - .toLowerCase(); -} - -function escapeRe(s: string) { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - -export function createCommandRegExp( - names: string | string[], - argsMode: ArgsMode = "optional", -) { - const list = Array.isArray(names) ? names : [names]; - const group = list.map(escapeRe).join("|"); - - const base = `^\\/(${group})(?:@([\\w_]+))?`; // (1)=cmd, (2)=bot - const tail = - argsMode === "none" - ? "\\s*$" - : argsMode === "required" - ? "\\s+([\\s\\S]+)\\s*$" // (3)=args обязателен - : "(?:\\s+([\\s\\S]+))?\\s*$"; // (3)=args опционален - - return new RegExp(base + tail, "i"); -} +export abstract class ChatCommand extends Command { +} \ No newline at end of file diff --git a/src/base/command.ts b/src/base/command.ts new file mode 100644 index 0000000..b7a5301 --- /dev/null +++ b/src/base/command.ts @@ -0,0 +1,58 @@ +import {Message} from "typescript-telegram-bot-api"; +import {Requirements} from "./requirements"; + +export type ArgsMode = "none" | "optional" | "required"; + +export abstract class Command { + + regexp?: RegExp | null; + command?: string | string[]; + argsMode: ArgsMode = "none"; + + requirements?: Requirements = null; + title?: string; + description?: string; + + get finalRegexp(): RegExp { + if (!this.regexp) { + const inferred = name(this.constructor.name); + const names = this.command ?? inferred; + this.regexp = createCommandRegExp(names, this.argsMode); + } + return this.regexp; + } + + abstract execute( + msg: Message, + match?: RegExpExecArray + ): Promise; +} + +export function name(s: string) { + return s + .replace(/Command$/, "") + .replace(/([a-z0-9])([A-Z])/g, "$1$2") + .toLowerCase(); +} + +function escapeRe(s: string) { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +export function createCommandRegExp( + names: string | string[], + argsMode: ArgsMode = "optional", +) { + const list = Array.isArray(names) ? names : [names]; + const group = list.map(escapeRe).join("|"); + + const base = `^\\/(${group})(?:@([\\w_]+))?`; // (1)=cmd, (2)=bot + const tail = + argsMode === "none" + ? "\\s*$" + : argsMode === "required" + ? "\\s+([\\s\\S]+)\\s*$" // (3)=args обязателен + : "(?:\\s+([\\s\\S]+))?\\s*$"; // (3)=args опционален + + return new RegExp(base + tail, "i"); +} diff --git a/src/commands/admins-add.ts b/src/commands/admins-add.ts index 519cd27..e33272a 100644 --- a/src/commands/admins-add.ts +++ b/src/commands/admins-add.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; @@ -6,7 +6,7 @@ import {fullName, logError, oldSendMessage} from "../util/utils"; import {Environment} from "../common/environment"; import {botUser} from "../index"; -export class AdminsAdd extends ChatCommand { +export class AdminsAdd extends Command { command = "addAdmin"; title = "/addAdmin"; description = "Add user to admins"; diff --git a/src/commands/admins-remove.ts b/src/commands/admins-remove.ts index a41c03a..f50560d 100644 --- a/src/commands/admins-remove.ts +++ b/src/commands/admins-remove.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; @@ -6,7 +6,7 @@ import {fullName, logError, oldSendMessage} from "../util/utils"; import {Environment} from "../common/environment"; import {botUser} from "../index"; -export class AdminsRemove extends ChatCommand { +export class AdminsRemove extends Command { command = "removeAdmin"; title = "/removeAdmin"; description = "Remove user from admins"; diff --git a/src/commands/ae.ts b/src/commands/ae.ts index 3adf48d..927bf2c 100644 --- a/src/commands/ae.ts +++ b/src/commands/ae.ts @@ -1,10 +1,10 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {errorPlaceholder, logError, oldSendMessage} from "../util/utils"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; -export class Ae extends ChatCommand { +export class Ae extends Command { argsMode = "required" as const; title = "/ae"; diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 88b6ba1..269f45d 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; @@ -6,7 +6,7 @@ import {bot, botUser} from "../index"; import {fullName, logError, oldSendMessage, oldReplyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; -export class Ban extends ChatCommand { +export class Ban extends Command { title = "/ban [reply]"; description = "ban user from chat"; diff --git a/src/commands/choice.ts b/src/commands/choice.ts index 54425d1..7b3ed61 100644 --- a/src/commands/choice.ts +++ b/src/commands/choice.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage, randomValue} from "../util/utils"; -export class Choice extends ChatCommand { +export class Choice extends Command { command = "choice"; argsMode = "required" as const; diff --git a/src/commands/coin.ts b/src/commands/coin.ts index 02ec46a..a71efb0 100644 --- a/src/commands/coin.ts +++ b/src/commands/coin.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {getRangedRandomInt, logError, oldReplyToMessage} from "../util/utils"; -export class Coin extends ChatCommand { +export class Coin extends Command { title = "/coin"; description = "Heads or tails"; diff --git a/src/commands/debug.ts b/src/commands/debug.ts index a0cf5da..d0cce74 100644 --- a/src/commands/debug.ts +++ b/src/commands/debug.ts @@ -1,10 +1,10 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {logError, replyToMessage} from "../util/utils"; -export class Debug extends ChatCommand { +export class Debug extends Command { title = "/debug"; description = "Returns msg (or reply) as json"; diff --git a/src/commands/dice.ts b/src/commands/dice.ts index 50a516d..813f41c 100644 --- a/src/commands/dice.ts +++ b/src/commands/dice.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, randomValue} from "../util/utils"; import {bot} from "../index"; @@ -6,7 +6,7 @@ import {bot} from "../index"; type DiceEmoji = "🎲" | "🎯" | "🏀" | "⚽" | "🎳" | "🎰"; const emojis = ["🎲", "🎯", "🏀", "⚽", "🎳", "🎰"]; -export class Dice extends ChatCommand { +export class Dice extends Command { title = "/dice"; description = "Sends random or specific dice"; diff --git a/src/commands/distort.ts b/src/commands/distort.ts index d1d0de8..36e3061 100644 --- a/src/commands/distort.ts +++ b/src/commands/distort.ts @@ -1,9 +1,9 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {downloadTelegramFile, extractImageFileId, logError, oldReplyToMessage, waveDistortSharp} from "../util/utils"; import {bot} from "../index"; -export class Distort extends ChatCommand { +export class Distort extends Command { command = "distort"; argsMode = "optional" as const; @@ -40,7 +40,10 @@ export class Distort extends ChatCommand { await bot.sendChatAction({chat_id: chatId, action: "upload_photo"}); const file = await bot.getFile({file_id: fileId}); - if (!file.file_path) throw new Error("No file_path in Telegram getFile response"); + if (!file.file_path) { + // noinspection ExceptionCaughtLocallyJS + throw new Error("No file_path in Telegram getFile response"); + } const inputBuf = await downloadTelegramFile(file.file_path); diff --git a/src/commands/gemini-chat.ts b/src/commands/gemini-chat.ts index b2c3282..1eb87cb 100644 --- a/src/commands/gemini-chat.ts +++ b/src/commands/gemini-chat.ts @@ -1,4 +1,3 @@ -import {ChatCommand} from "../base/chat-command"; import {Message} from "typescript-telegram-bot-api"; import {Environment} from "../common/environment"; import {bot, googleAi} from "../index"; @@ -13,6 +12,7 @@ import { oldReplyToMessage, startIntervalEditor } from "../util/utils"; +import {ChatCommand} from "../base/chat-command"; export class GeminiChat extends ChatCommand { command = "gemini"; diff --git a/src/commands/gemini-generate-image.ts b/src/commands/gemini-generate-image.ts index 3cb1b1b..93c66a9 100644 --- a/src/commands/gemini-generate-image.ts +++ b/src/commands/gemini-generate-image.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; @@ -6,7 +6,7 @@ import {googleAi} from "../index"; import {logError, replyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; -export class GeminiGenerateImage extends ChatCommand { +export class GeminiGenerateImage extends Command { command = "geminiGenImage"; argsMode = "required" as const; diff --git a/src/commands/gemini-get-model.ts b/src/commands/gemini-get-model.ts index 79c0713..d2ba9bf 100644 --- a/src/commands/gemini-get-model.ts +++ b/src/commands/gemini-get-model.ts @@ -1,9 +1,9 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, replyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; -export class GeminiGetModel extends ChatCommand { +export class GeminiGetModel extends Command { title = "/geminiGetModel"; description = "Get current Gemini model"; diff --git a/src/commands/gemini-list-models.ts b/src/commands/gemini-list-models.ts index 85bb443..2d01b1a 100644 --- a/src/commands/gemini-list-models.ts +++ b/src/commands/gemini-list-models.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; import {googleAi} from "../index"; import {logError, replyToMessage} from "../util/utils"; -export class GeminiListModels extends ChatCommand { +export class GeminiListModels extends Command { title = "/geminiListModels"; description = "List all Gemini models"; diff --git a/src/commands/gemini-set-model.ts b/src/commands/gemini-set-model.ts index f04487f..512d252 100644 --- a/src/commands/gemini-set-model.ts +++ b/src/commands/gemini-set-model.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; import {Environment} from "../common/environment"; import {logError, replyToMessage} from "../util/utils"; -export class GeminiSetModel extends ChatCommand { +export class GeminiSetModel extends Command { argsMode = "required" as const; title = "/geminiSetModel"; diff --git a/src/commands/help.ts b/src/commands/help.ts index a65132a..f9a53f1 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,10 +1,10 @@ import {Message} from "typescript-telegram-bot-api"; import {chatCommandToString, delay, logError, sendMessage} from "../util/utils"; -import {ChatCommand} from "../base/chat-command"; -import {chatCommands} from "../index"; +import {Command} from "../base/command"; +import {commands} from "../index"; import {TelegramError} from "typescript-telegram-bot-api/dist/errors"; -export class Help extends ChatCommand { +export class Help extends Command { command = ["h", "help"]; title = "/help"; @@ -13,7 +13,7 @@ export class Help extends ChatCommand { async execute(msg: Message) { let text = "Commands:\n\n"; - chatCommands.forEach(c => { + commands.forEach(c => { text += `${chatCommandToString(c)}\n`; }); diff --git a/src/commands/id.ts b/src/commands/id.ts index 356b49b..5bba0f9 100644 --- a/src/commands/id.ts +++ b/src/commands/id.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage} from "../util/utils"; -export class Id extends ChatCommand { +export class Id extends Command { title = "/id"; description = "ID of chat, user and reply (if replied to any message)"; diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts index cb6d7ae..ae9339e 100644 --- a/src/commands/ignore.ts +++ b/src/commands/ignore.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; @@ -6,7 +6,7 @@ import {fullName, logError, oldSendMessage} from "../util/utils"; import {botUser} from "../index"; import {Environment} from "../common/environment"; -export class Ignore extends ChatCommand { +export class Ignore extends Command { title = "/ignore"; description = "Bot will ignore user"; diff --git a/src/commands/leave.ts b/src/commands/leave.ts index 5d7eaf8..40446aa 100644 --- a/src/commands/leave.ts +++ b/src/commands/leave.ts @@ -1,10 +1,10 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {bot} from "../index"; -export class Leave extends ChatCommand { +export class Leave extends Command { title = "/leave"; description = "Bot will leave current chat"; diff --git a/src/commands/mistral-chat.ts b/src/commands/mistral-chat.ts index 39acae7..2aa3ea1 100644 --- a/src/commands/mistral-chat.ts +++ b/src/commands/mistral-chat.ts @@ -1,4 +1,3 @@ -import {ChatCommand} from "../base/chat-command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; @@ -12,6 +11,7 @@ import { import {Environment} from "../common/environment"; import {bot, mistralAi} from "../index"; import {MessageStore} from "../common/message-store"; +import {ChatCommand} from "../base/chat-command"; export class MistralChat extends ChatCommand { command = "mistral"; diff --git a/src/commands/mistral-get-model.ts b/src/commands/mistral-get-model.ts index a67356b..1d963ed 100644 --- a/src/commands/mistral-get-model.ts +++ b/src/commands/mistral-get-model.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, replyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; -export class MistralGetModel extends ChatCommand { +export class MistralGetModel extends Command { title = "/mistralGetModel"; description = "Get current Mistral model"; diff --git a/src/commands/mistral-list-models.ts b/src/commands/mistral-list-models.ts index 6fb818e..200a824 100644 --- a/src/commands/mistral-list-models.ts +++ b/src/commands/mistral-list-models.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; import {mistralAi} from "../index"; import {logError, oldReplyToMessage, replyToMessage} from "../util/utils"; -export class MistralListModels extends ChatCommand { +export class MistralListModels extends Command { title = "/mistralListModels"; description = "List all Mistral models"; diff --git a/src/commands/mistral-set-model.ts b/src/commands/mistral-set-model.ts index 77b87e4..432d57f 100644 --- a/src/commands/mistral-set-model.ts +++ b/src/commands/mistral-set-model.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; import {Environment} from "../common/environment"; import {logError, replyToMessage} from "../util/utils"; -export class MistralSetModel extends ChatCommand { +export class MistralSetModel extends Command { argsMode = "required" as const; title = "/mistralSetModel"; diff --git a/src/commands/ollama-chat.ts b/src/commands/ollama-chat.ts index c2b6915..1c01ecb 100644 --- a/src/commands/ollama-chat.ts +++ b/src/commands/ollama-chat.ts @@ -1,6 +1,5 @@ -import {ChatCommand} from "../base/chat-command"; import {Message} from "typescript-telegram-bot-api"; -import {abortOllamaRequest, bot, chatCommands, getOllamaRequest, ollama, ollamaRequests} from "../index"; +import {abortOllamaRequest, bot, commands, getOllamaRequest, ollama, ollamaRequests} from "../index"; import { collectReplyChainText, escapeMarkdownV2Text, @@ -14,9 +13,10 @@ import {MessageStore} from "../common/message-store"; import {Cancel} from "../callback_commands/cancel"; import {OllamaCancel} from "../callback_commands/ollama-cancel"; import {OllamaGetModel} from "./ollama-get-model"; +import {ChatCommand} from "../base/chat-command"; export class OllamaChat extends ChatCommand { - command = ["ollama", "ollamathink"]; + command = ["ollamaThink", "ollama"]; argsMode = "required" as const; title = "/ollama"; @@ -57,7 +57,7 @@ export class OllamaChat extends ChatCommand { if (!think && imagesCount) { try { - const modelInfo = await chatCommands.find(c => c instanceof OllamaGetModel).loadImageModelInfo(); + const modelInfo = await commands.find(c => c instanceof OllamaGetModel).loadImageModelInfo(); if (modelInfo) { const caps = modelInfo.capabilities || []; if (!caps.includes("vision")) { @@ -75,7 +75,7 @@ export class OllamaChat extends ChatCommand { if (think) { try { - const modelInfo = await chatCommands.find(c => c instanceof OllamaGetModel).loadThinkModelInfo(); + const modelInfo = await commands.find(c => c instanceof OllamaGetModel).loadThinkModelInfo(); if (modelInfo) { const caps = modelInfo.capabilities || []; if (!caps.includes("thinking")) { diff --git a/src/commands/ollama-get-model.ts b/src/commands/ollama-get-model.ts index 414cbbd..89cc80c 100644 --- a/src/commands/ollama-get-model.ts +++ b/src/commands/ollama-get-model.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {boolToEmoji, logError, replyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; import {ollama} from "../index"; import {ShowResponse} from "ollama"; -export class OllamaGetModel extends ChatCommand { +export class OllamaGetModel extends Command { title = "/ollamaGetModel"; description = "Ollama model info"; diff --git a/src/commands/ollama-list-models.ts b/src/commands/ollama-list-models.ts index 4c29963..cc3e974 100644 --- a/src/commands/ollama-list-models.ts +++ b/src/commands/ollama-list-models.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {ollama} from "../index"; import {logError, oldReplyToMessage, replyToMessage} from "../util/utils"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; -export class OllamaListModels extends ChatCommand { +export class OllamaListModels extends Command { title = "/ollamaListModels"; description = "List all Ollama models"; diff --git a/src/commands/ollama-prompt.ts b/src/commands/ollama-prompt.ts index 095ac64..aaedd5c 100644 --- a/src/commands/ollama-prompt.ts +++ b/src/commands/ollama-prompt.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {abortOllamaRequest, bot, getOllamaRequest, ollama, ollamaRequests} from "../index"; import {escapeMarkdownV2Text, logError, oldReplyToMessage, startIntervalEditor} from "../util/utils"; @@ -9,7 +9,7 @@ import {Cancel} from "../callback_commands/cancel"; import {OllamaCancel} from "../callback_commands/ollama-cancel"; import {MessageStore} from "../common/message-store"; -export class OllamaPrompt extends ChatCommand { +export class OllamaPrompt extends Command { command = "ollamaPrompt"; argsMode = "required" as const; diff --git a/src/commands/ollama-search.ts b/src/commands/ollama-search.ts index de7821f..2d1a47e 100644 --- a/src/commands/ollama-search.ts +++ b/src/commands/ollama-search.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; @@ -7,7 +7,7 @@ import {WebSearchResponse} from "../model/web-search-response"; import {editMessageText, logError} from "../util/utils"; import {Environment} from "../common/environment"; -export class OllamaSearch extends ChatCommand { +export class OllamaSearch extends Command { command = ["s", "search"]; argsMode = "required" as const; diff --git a/src/commands/ollama-set-model.ts b/src/commands/ollama-set-model.ts index a1e836d..42ea5de 100644 --- a/src/commands/ollama-set-model.ts +++ b/src/commands/ollama-set-model.ts @@ -1,12 +1,12 @@ import {Message} from "typescript-telegram-bot-api"; -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Environment} from "../common/environment"; import {logError, replyToMessage} from "../util/utils"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {ollama} from "../index"; -export class OllamaSetModel extends ChatCommand { +export class OllamaSetModel extends Command { argsMode = "required" as const; title = "/ollamaSetModel"; diff --git a/src/commands/openai-chat.ts b/src/commands/openai-chat.ts new file mode 100644 index 0000000..cbd1e20 --- /dev/null +++ b/src/commands/openai-chat.ts @@ -0,0 +1,160 @@ +import {Message} from "typescript-telegram-bot-api"; +import {MessageStore} from "../common/message-store"; +import { + collectReplyChainText, + escapeMarkdownV2Text, + logError, + replyToMessage, + startIntervalEditor +} from "../util/utils"; +import {Environment} from "../common/environment"; +import {bot, openAi} from "../index"; +import {Requirements} from "../base/requirements"; +import {Requirement} from "../base/requirement"; +import {ChatCommand} from "../base/chat-command"; + +export class OpenAIChat extends ChatCommand { + command = ["openai", "chatgpt"]; + argsMode = "required" as const; + + requirements = Requirements.Build(Requirement.BOT_CREATOR); + + async execute(msg: Message, match?: RegExpExecArray): Promise { + console.log("OpenAI Chat: ", match); + return this.executeOpenAI(msg, match?.[3]); + } + + async executeOpenAI(msg: Message, text: string): Promise { + if (!text || text.trim().length === 0) return; + + const chatId = msg.chat.id; + + const storedMsg = await MessageStore.get(chatId, msg.message_id); + const messageParts = await collectReplyChainText(storedMsg); + console.log("MESSAGE PARTS", messageParts); + + const chatMessages = messageParts.map(part => { + const content = []; + content.push({ + type: "input_text", + text: (Environment.USE_NAMES_IN_PROMPT && !part.bot ? `MESSAGE FROM USER "${part.name}":\n` : "") + part.content, + }); + + // TODO: 03/02/2026, Danil Nikolaev: upload file then add here + // for (const image of part.images) { + // content.push({ + // type: "image_url", + // imageUrl: "data:image/jpeg;base64," + image + // }); + // } + + return { + role: part.bot ? "assistant" : "user", + content: content, + type: "message", + }; + }); + chatMessages.reverse(); + chatMessages.unshift({ + role: "system", + content: [{type: "input_text", text: Environment.SYSTEM_PROMPT}], + type: "message" + }); + + let waitMessage: Message; + + const startTime = Date.now(); + + try { + waitMessage = await bot.sendMessage({ + chat_id: chatId, + text: Environment.waitText, + reply_parameters: { + chat_id: chatId, + message_id: msg.message_id + } + }); + + const stream = await openAi.responses.create({ + model: Environment.OPENAI_MODEL, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + input: chatMessages as any, + stream: true + }); + + let currentText = ""; + let shouldBreak = false; + + const editor = startIntervalEditor({ + intervalMs: 4500, + getText: () => currentText, + editFn: async (text) => { + await bot.editMessageText( + { + chat_id: chatId, + message_id: waitMessage.message_id, + text: escapeMarkdownV2Text(text), + parse_mode: "Markdown" + } + ).catch(logError); + + console.log("editMessageText", text); + + waitMessage.reply_to_message = msg; + waitMessage.text = text; + await MessageStore.put(waitMessage); + }, + onStop: async () => { + } + }); + await editor.tick(); + + try { + for await (const chunk of stream) { + console.log("chunk", chunk); + + if (chunk.type === "response.output_text.delta") { + const text = chunk.delta; + currentText += text; + + if (currentText.length > 4096) { + currentText = currentText.slice(0, 4093) + "..."; + shouldBreak = true; + } + + console.log("messageText", currentText); + console.log("length", currentText.length); + + if (shouldBreak) { + console.log("break", true); + break; + } + } + + + } + } finally { + await editor.tick(); + await editor.stop(); + + if (!shouldBreak) { + console.log("ended", true); + } + + const diff = Math.abs(Date.now() - startTime) / 1000.0; + console.log("time", diff); + + waitMessage.reply_to_message = msg; + waitMessage.text = currentText; + await MessageStore.put(waitMessage); + await replyToMessage({message: waitMessage, text: `⏱️ ${diff}s`}); + } + } catch (error) { + logError(error); + await replyToMessage({ + message: waitMessage, + text: `Произошла ошибка!\n${error.toString()}` + }).catch(logError); + } + } +} \ No newline at end of file diff --git a/src/commands/openai-get-model.ts b/src/commands/openai-get-model.ts new file mode 100644 index 0000000..e0642d3 --- /dev/null +++ b/src/commands/openai-get-model.ts @@ -0,0 +1,13 @@ +import {Command} from "../base/command"; +import {Message} from "typescript-telegram-bot-api"; +import {logError, replyToMessage} from "../util/utils"; +import {Environment} from "../common/environment"; + +export class OpenAIGetModel extends Command { + title = "/openAIGetModel"; + description = "Get current OpenAI model"; + + async execute(msg: Message): Promise { + await replyToMessage({message: msg, text: `Текущая модель: "${Environment.OPENAI_MODEL}"`}).catch(logError); + } +} \ No newline at end of file diff --git a/src/commands/openai-list-models.ts b/src/commands/openai-list-models.ts new file mode 100644 index 0000000..1760204 --- /dev/null +++ b/src/commands/openai-list-models.ts @@ -0,0 +1,37 @@ +import {Command} from "../base/command"; +import {Requirements} from "../base/requirements"; +import {Requirement} from "../base/requirement"; +import {Message} from "typescript-telegram-bot-api"; +import {openAi} from "../index"; +import {logError, replyToMessage} from "../util/utils"; + +export class OpenAIListModels extends Command { + title = "/openAIListModels"; + description = "List all OpenAI models"; + + requirements = Requirements.Build(Requirement.BOT_CREATOR); + + async execute(msg: Message): Promise { + try { + const listResponse = await openAi.models.list(); + console.log(listResponse); + + const modelsString = listResponse.data + .map(e => `${e.id}`) + .sort((a, b) => a.localeCompare(b)) + .join("\n") + .substring(0, 4000); + + const text = "Доступные модели:\n\n" + "
" + modelsString + "
"; + + await replyToMessage({ + message: msg, + text: text, + parse_mode: "HTML" + }); + } catch (e) { + logError(e); + await replyToMessage({message: msg, text: "Не получилось загрузить список моделей"}).catch(logError); + } + } +} \ No newline at end of file diff --git a/src/commands/openai-set-model.ts b/src/commands/openai-set-model.ts new file mode 100644 index 0000000..e2ba962 --- /dev/null +++ b/src/commands/openai-set-model.ts @@ -0,0 +1,25 @@ +import {Command} from "../base/command"; +import {Requirements} from "../base/requirements"; +import {Requirement} from "../base/requirement"; +import {Message} from "typescript-telegram-bot-api"; +import {Environment} from "../common/environment"; +import {logError, replyToMessage} from "../util/utils"; + +export class OpenAISetModel extends Command { + argsMode = "required" as const; + + title = "/openAISetModel"; + description = "Set OpenAI model"; + + requirements = Requirements.Build(Requirement.BOT_CREATOR); + + async execute(msg: Message, match?: RegExpExecArray | null): Promise { + const newModel = match?.[3]; + Environment.setOpenAIModel(newModel || Environment.OPENAI_MODEL); + + const text = newModel ? `Выбрана модель "${newModel}"` + : `Модель не задана. Будет использоваться стандартная модель "${Environment.OPENAI_MODEL}".`; + + await replyToMessage({message: msg, text: text}).catch(logError); + } +} \ No newline at end of file diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 34549ed..ed453c6 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,8 +1,8 @@ import {logError, sendMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; -export class Ping extends ChatCommand { +export class Ping extends Command { title = "/ping"; description = "Ping between received and sent message"; diff --git a/src/commands/prefix-response.ts b/src/commands/prefix-response.ts index 3c4a7bc..f4f7c3b 100644 --- a/src/commands/prefix-response.ts +++ b/src/commands/prefix-response.ts @@ -1,9 +1,9 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, randomValue, replyToMessage} from "../util/utils"; import {Environment} from "../common/environment"; -export class PrefixResponse extends ChatCommand { +export class PrefixResponse extends Command { async execute(msg: Message): Promise { await replyToMessage({message: msg, text: randomValue(Environment.ANSWERS.prefix)}).catch(logError); } diff --git a/src/commands/qr.ts b/src/commands/qr.ts index 4685395..51b20bb 100644 --- a/src/commands/qr.ts +++ b/src/commands/qr.ts @@ -1,10 +1,10 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {extractMessagePayload, logError, replyToMessage} from "../util/utils"; import {bot, botUser} from "../index"; import QRCode from "qrcode"; -export class Qr extends ChatCommand { +export class Qr extends Command { argsMode = "optional" as const; diff --git a/src/commands/quote.ts b/src/commands/quote.ts index 68b7826..655ffd0 100644 --- a/src/commands/quote.ts +++ b/src/commands/quote.ts @@ -4,7 +4,7 @@ import emojiRegex from "emoji-regex"; import {createCanvas, GlobalFonts, type Image as CanvasImage, loadImage, SKRSContext2D} from "@napi-rs/canvas"; import {Message, MessageEntity, PhotoSize} from "typescript-telegram-bot-api"; -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {bot, botUser} from "../index"; import { getChatAvatar, @@ -37,7 +37,7 @@ try { logError(e); } -export class Quote extends ChatCommand { +export class Quote extends Command { command = ["cit", "citation", "q", "quote"]; argsMode = "none" as const; diff --git a/src/commands/random-int.ts b/src/commands/random-int.ts index cf18ab2..2bac8ee 100644 --- a/src/commands/random-int.ts +++ b/src/commands/random-int.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {getRandomInt, getRangedRandomInt, logError, oldSendMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; -export class RandomInt extends ChatCommand { +export class RandomInt extends Command { argsMode = "optional" as const; title = "/randomInt"; diff --git a/src/commands/random-string.ts b/src/commands/random-string.ts index 49e5185..2a7f78c 100644 --- a/src/commands/random-string.ts +++ b/src/commands/random-string.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {getRandomInt, logError, replyToMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; -export class RandomString extends ChatCommand { +export class RandomString extends Command { argsMode = "optional" as const; title = "/randomString"; diff --git a/src/commands/shutdown.ts b/src/commands/shutdown.ts index 9cef187..ca57c7a 100644 --- a/src/commands/shutdown.ts +++ b/src/commands/shutdown.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; @@ -15,7 +15,7 @@ const texts = [ const timings = [1500, 2500]; const timer = [3, 2, 1]; -export class Shutdown extends ChatCommand { +export class Shutdown extends Command { title = "/shutdown"; description = "Self-destruction sequence for bot (shutdown)"; diff --git a/src/commands/start.ts b/src/commands/start.ts index 9ba6d2a..b0fa959 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -1,13 +1,13 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; -import {chatCommands} from "../index"; +import {commands} from "../index"; import {Help} from "./help"; -export class Start extends ChatCommand { +export class Start extends Command { title = "/start"; description = "Start the bot"; async execute(msg: Message): Promise { - await chatCommands.find(e => e instanceof Help).execute(msg); + await commands.find(e => e instanceof Help).execute(msg); } } \ No newline at end of file diff --git a/src/commands/system-info.ts b/src/commands/system-info.ts index 6ffc084..53a7809 100644 --- a/src/commands/system-info.ts +++ b/src/commands/system-info.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {logError, replyToMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; -export class SystemInfo extends ChatCommand { +export class SystemInfo extends Command { title = "/systemInfo"; description = "System information"; diff --git a/src/commands/test.ts b/src/commands/test.ts index d774dcd..fe052cd 100644 --- a/src/commands/test.ts +++ b/src/commands/test.ts @@ -1,9 +1,9 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage, randomValue} from "../util/utils"; import {Environment} from "../common/environment"; -export class Test extends ChatCommand { +export class Test extends Command { regexp = /^(test|тест|еуые|ntcn|инноке(нтий|ш|нтич))/i; title = "тест"; description = "System functionality check"; diff --git a/src/commands/title.ts b/src/commands/title.ts index 3f745c7..2b76b1a 100644 --- a/src/commands/title.ts +++ b/src/commands/title.ts @@ -1,11 +1,11 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {logError, oldReplyToMessage} from "../util/utils"; import {bot} from "../index"; -export class Title extends ChatCommand { +export class Title extends Command { command = "title"; argsMode = "required" as const; diff --git a/src/commands/transliteration.ts b/src/commands/transliteration.ts index ff76946..ab4e564 100644 --- a/src/commands/transliteration.ts +++ b/src/commands/transliteration.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, oldReplyToMessage, randomValue} from "../util/utils"; @@ -74,7 +74,7 @@ export function fixLayoutAuto( return text; } -export class Transliteration extends ChatCommand { +export class Transliteration extends Command { command = ["transliteration", "tr"]; title = "/tr [text or reply]"; diff --git a/src/commands/unban.ts b/src/commands/unban.ts index 4a258a3..978069c 100644 --- a/src/commands/unban.ts +++ b/src/commands/unban.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {Message} from "typescript-telegram-bot-api"; @@ -6,7 +6,7 @@ import {bot, botUser} from "../index"; import {fullName, logError, oldReplyToMessage, oldSendMessage} from "../util/utils"; import {Environment} from "../common/environment"; -export class Unban extends ChatCommand { +export class Unban extends Command { title = "/unban [reply]"; description = "unban user from chat"; diff --git a/src/commands/unignore.ts b/src/commands/unignore.ts index 6b7bfd9..7c6025e 100644 --- a/src/commands/unignore.ts +++ b/src/commands/unignore.ts @@ -1,4 +1,4 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Requirements} from "../base/requirements"; import {Requirement} from "../base/requirement"; import {fullName, logError, oldSendMessage} from "../util/utils"; @@ -6,7 +6,7 @@ import {Message} from "typescript-telegram-bot-api"; import {botUser} from "../index"; import {Environment} from "../common/environment"; -export class Unignore extends ChatCommand { +export class Unignore extends Command { title = "/unignore"; description = "Bot will start responding to the user"; requirements = Requirements.Build( diff --git a/src/commands/uptime.ts b/src/commands/uptime.ts index e204be8..c5aa0ea 100644 --- a/src/commands/uptime.ts +++ b/src/commands/uptime.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {getUptime, logError, oldSendMessage} from "../util/utils"; -export class Uptime extends ChatCommand { +export class Uptime extends Command { title = "/uptime"; description = "Bot's uptime"; diff --git a/src/commands/what-better.ts b/src/commands/what-better.ts index 974d6ae..e559c1a 100644 --- a/src/commands/what-better.ts +++ b/src/commands/what-better.ts @@ -1,9 +1,9 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {logError, oldSendMessage, randomValue} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; import {Environment} from "../common/environment"; -export class WhatBetter extends ChatCommand { +export class WhatBetter extends Command { command = ["what", "что"]; argsMode = "required" as const; diff --git a/src/commands/when.ts b/src/commands/when.ts index f6d03f6..9a43523 100644 --- a/src/commands/when.ts +++ b/src/commands/when.ts @@ -1,8 +1,8 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {getRandomInt, getRangedRandomInt, logError, oldReplyToMessage} from "../util/utils"; import {Message} from "typescript-telegram-bot-api"; -export class When extends ChatCommand { +export class When extends Command { command = ["when", "когда"]; argsMode = "required" as const; diff --git a/src/commands/youtube-download.ts b/src/commands/youtube-download.ts index 576f070..b5adff3 100644 --- a/src/commands/youtube-download.ts +++ b/src/commands/youtube-download.ts @@ -1,10 +1,10 @@ -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {Message} from "typescript-telegram-bot-api"; import {logError, replyToMessage} from "../util/utils"; import {bot} from "../index"; import {downloadVideoFromYouTube} from "../util/ytdl"; -export class YouTubeDownload extends ChatCommand { +export class YouTubeDownload extends Command { command = ["ytdl", "youtube"]; argsMode = "required" as const; diff --git a/src/common/environment.ts b/src/common/environment.ts index 86e8038..cb88564 100644 --- a/src/common/environment.ts +++ b/src/common/environment.ts @@ -39,6 +39,9 @@ export class Environment { static MISTRAL_API_KEY?: string; static MISTRAL_MODEL: string; + static OPENAI_API_KEY?: string; + static OPENAI_MODEL: string; + static waitText = "⏳ Дайте-ка подумать..."; static analyzingPictureText = "🔍 Внимательно изучаю изображение..."; static analyzingPicturesText = "🔍 Внимательно изучаю изображения..."; @@ -75,6 +78,9 @@ export class Environment { Environment.MISTRAL_API_KEY = process.env.MISTRAL_API_KEY; Environment.MISTRAL_MODEL = process.env.MISTRAL_MODEL || "mistral-small-latest"; + + Environment.OPENAI_API_KEY = process.env.OPENAI_API_KEY; + Environment.OPENAI_MODEL = process.env.OPENAI_MODEL || "gpt-4o-mini"; } static setAdmins(admins: Set) { @@ -135,4 +141,8 @@ export class Environment { static setMistralModel(newModel: string) { Environment.MISTRAL_MODEL = newModel; } + + static setOpenAIModel(newModel: string) { + Environment.OPENAI_MODEL = newModel; + } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ac58b40..fab901b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import {Environment} from "./common/environment"; import {InlineQueryResult, TelegramBot, User} from "typescript-telegram-bot-api"; -import {ChatCommand} from "./base/chat-command"; +import {Command} from "./base/command"; import { delay, extractTextMessage, @@ -73,6 +73,11 @@ import fs from "node:fs"; import path from "node:path"; import {setInterval} from "node:timers"; import {clearUpVideoFolder} from "./util/files"; +import {OpenAI} from "openai"; +import {OpenAIChat} from "./commands/openai-chat"; +import {OpenAIListModels} from "./commands/openai-list-models"; +import {OpenAIGetModel} from "./commands/openai-get-model"; +import {OpenAISetModel} from "./commands/openai-set-model"; process.setUncaughtExceptionCaptureCallback(logError); @@ -87,6 +92,7 @@ export let botUser: User; export const googleAi = new GoogleGenAI({apiKey: Environment.GEMINI_API_KEY}); export const mistralAi = new Mistral({apiKey: Environment.MISTRAL_API_KEY}); +export const openAi = new OpenAI({apiKey: Environment.OPENAI_API_KEY}); export const ollama = new Ollama({ host: Environment.OLLAMA_ADDRESS, @@ -120,7 +126,7 @@ export function abortOllamaRequest(uuid: string): boolean { } } -export const chatCommands: ChatCommand[] = [ +export const commands: Command[] = [ new Start(), new Help(), new Test(), @@ -164,7 +170,7 @@ export const callbackCommands: CallbackCommand[] = [ ]; if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM_PROMPT) { - chatCommands.push( + commands.push( new OllamaChat(), new OllamaPrompt(), new OllamaListModels(), @@ -174,11 +180,11 @@ if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM } if (Environment.OLLAMA_API_KEY) { - chatCommands.push(new OllamaSearch()); + commands.push(new OllamaSearch()); } if (Environment.GEMINI_API_KEY) { - chatCommands.push( + commands.push( new GeminiChat(), new GeminiListModels(), new GeminiGetModel(), @@ -188,7 +194,7 @@ if (Environment.GEMINI_API_KEY) { } if (Environment.MISTRAL_API_KEY) { - chatCommands.push( + commands.push( new MistralChat(), new MistralListModels(), new MistralGetModel(), @@ -196,6 +202,15 @@ if (Environment.MISTRAL_API_KEY) { ); } +if (Environment.OPENAI_API_KEY) { + commands.push( + new OpenAIChat(), + new OpenAIListModels(), + new OpenAIGetModel(), + new OpenAISetModel(), + ); +} + export const photoDir = path.join(Environment.DATA_PATH, "photo"); export const videoDir = path.join(Environment.DATA_PATH, "video"); @@ -228,7 +243,7 @@ async function main() { }, 1000 * 60 * 60 * 24); }); - const commands = chatCommands.filter(cmd => { + const cmds = commands.filter(cmd => { return cmd.title && cmd.title.startsWith("/") && cmd.title.split(" ").length === 1 && cmd.description; }).map(cmd => { return { @@ -242,7 +257,7 @@ async function main() { [ initSystemSpecs(), readData(), retrieveAnswers(), bot.getMe(), - bot.setMyCommands({commands: commands, scope: {type: "default"}}) + bot.setMyCommands({commands: cmds, scope: {type: "default"}}) ] ); botUser = results[3]; diff --git a/src/util/utils.ts b/src/util/utils.ts index c462c6b..d7e67dd 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -1,5 +1,5 @@ import * as si from "systeminformation"; -import {ChatCommand} from "../base/chat-command"; +import {Command} from "../base/command"; import {CallbackCommand} from "../base/callback-command"; import { CallbackQuery, @@ -12,7 +12,7 @@ import { } from "typescript-telegram-bot-api"; import {Environment} from "../common/environment"; import {TelegramError} from "typescript-telegram-bot-api/dist/errors"; -import {bot, botUser, chatCommands, messageDao} from "../index"; +import {bot, botUser, commands, messageDao} from "../index"; import os from "os"; import axios from "axios"; import {MessagePart} from "../common/message-part"; @@ -29,6 +29,7 @@ import {PrefixResponse} from "../commands/prefix-response"; import {OllamaChat} from "../commands/ollama-chat"; import {getYouTubeVideoId} from "./ytdl"; import {YouTubeDownload} from "../commands/youtube-download"; +import {ChatCommand} from "../base/chat-command"; export const ignore = () => { }; @@ -54,10 +55,10 @@ export const errorPlaceholder = async (msg: Message) => { }; export function searchChatCommand( - commands: ChatCommand[], + commands: Command[], text: string, botUsername: string = botUser.username -): ChatCommand | null { +): Command | null { for (const command of commands) { const match = command.finalRegexp.exec(text); if (!match) continue; @@ -85,7 +86,7 @@ export function searchCallbackCommand(commands: CallbackCommand[], data: string) return null; } -export async function checkRequirements(cmd: ChatCommand | CallbackCommand | null, msg?: Message, cb?: CallbackQuery): Promise { +export async function checkRequirements(cmd: Command | CallbackCommand | null, msg?: Message, cb?: CallbackQuery): Promise { if (!cmd) return false; if (!msg && !cb) return false; @@ -202,7 +203,7 @@ export async function checkRequirements(cmd: ChatCommand | CallbackCommand | nul return true; } -export async function executeChatCommand(cmd: ChatCommand | null, msg: Message, text: string): Promise { +export async function executeChatCommand(cmd: Command | null, msg: Message, text: string): Promise { if (!cmd) return false; if (!await checkRequirements(cmd, msg)) return false; @@ -353,7 +354,7 @@ export function randomValue(list: T[]): T { return list[Math.floor(Math.random() * list.length)]; } -export function chatCommandToString(cmd: ChatCommand): string { +export function chatCommandToString(cmd: Command): string { if (!cmd.title && !cmd.description) { return ""; } @@ -469,17 +470,22 @@ export function extractTextMessage(msg: Message | StoredMessage | string): strin } export function cutPrefixes(msg: Message | StoredMessage | string): string { - const prefixes = [ - Environment.BOT_PREFIX, - `/ollamathink@${botUser.username}`, - "/ollamathink", - `/ollama@${botUser.username}`, - "/ollama", - `/gemini@${botUser.username}`, - "/gemini", - `/mistral@${botUser.username}`, - "/mistral", - ]; + const chatCommands = commands.filter(c => c instanceof ChatCommand); + + const prefixes = [Environment.BOT_PREFIX]; + const pushPrefix = (c: string) => { + prefixes.push(`/${c}@${botUser.username}`); + prefixes.push(`/${c}`); + }; + + chatCommands.forEach((cmd) => { + const command = cmd.command; + if (Array.isArray(command)) { + command.forEach(pushPrefix); + } else { + pushPrefix(command); + } + }); const text = extractTextMessage(msg); let newText = text; @@ -974,13 +980,11 @@ export function getPhotoMaxSize(photos: PhotoSize[], target: number = Environmen return photos[0]; } - const max = photos.reduce((prev, cur) => { + return photos.reduce((prev, cur) => { if (!prev) return cur; return cur.width * cur.height > prev.width * prev.height ? cur : prev; }, null); - - return max; } export async function mapPhotoSizeToMax(size: PhotoSize): Promise { @@ -1084,7 +1088,7 @@ export async function processNewMessage(msg: Message) { const then = Date.now(); - const cmd = searchChatCommand(chatCommands, cmdText); + const cmd = searchChatCommand(commands, cmdText); const executed = await executeChatCommand(cmd, msg, cmdText); const now = Date.now(); @@ -1114,7 +1118,7 @@ export async function processNewMessage(msg: Message) { try { getYouTubeVideoId(url); - const yt = chatCommands.find(e => e instanceof YouTubeDownload); + const yt = commands.find(e => e instanceof YouTubeDownload); if (await checkRequirements(yt, msg)) { await yt.downloadYouTubeVideo(msg, url); } @@ -1129,7 +1133,7 @@ export async function processNewMessage(msg: Message) { if (!startsWithPrefix && msg.chat.type !== "private") return; if (msg.chat.type === "private" && !Environment.ADMIN_IDS.has(msg.chat.id)) return; - const chat = chatCommands.find(e => e instanceof OllamaChat); + const chat = commands.find(e => e instanceof OllamaChat); if (await checkRequirements(chat, msg)) { await chat.executeOllama(msg, textToCheck); }