1 Commits

Author SHA1 Message Date
dependabot[bot] 81f4da45a3 Bump drizzle-orm from 1.0.0-beta.9-e89174b to 1.0.0-beta.15-9485290
Bumps [drizzle-orm](https://github.com/drizzle-team/drizzle-orm) from 1.0.0-beta.9-e89174b to 1.0.0-beta.15-9485290.
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/commits)

---
updated-dependencies:
- dependency-name: drizzle-orm
  dependency-version: 1.0.0-beta.15-9485290
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-26 06:45:17 +00:00
21 changed files with 146 additions and 483 deletions
+13 -13
View File
@@ -5,18 +5,18 @@
"": { "": {
"name": "tg-chat-bot", "name": "tg-chat-bot",
"dependencies": { "dependencies": {
"@google/genai": "^1.42.0", "@google/genai": "^1.41.0",
"@libsql/client": "^0.17.0", "@libsql/client": "^0.17.0",
"@mistralai/mistralai": "^1.14.0", "@mistralai/mistralai": "^1.14.0",
"@napi-rs/canvas": "^0.1.95", "@napi-rs/canvas": "^0.1.91",
"axios": "^1.13.5", "axios": "^1.13.5",
"dotenv": "^17.3.1", "dotenv": "^17.2.4",
"drizzle-orm": "^1.0.0-beta.9-e89174b", "drizzle-orm": "^1.0.0-beta.9-e89174b",
"emoji-regex": "^10.6.0", "emoji-regex": "^10.6.0",
"fluent-ffmpeg": "^2.1.3", "fluent-ffmpeg": "^2.1.3",
"ollama": "^0.6.3", "ollama": "^0.6.3",
"openai": "^6.25.0", "openai": "^6.21.0",
"puppeteer": "^24.37.5", "puppeteer": "^24.37.2",
"puppeteer-extra": "^3.3.6", "puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2", "puppeteer-extra-plugin-stealth": "^2.11.2",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
@@ -30,12 +30,12 @@
"devDependencies": { "devDependencies": {
"@types/bun": "^1.3.9", "@types/bun": "^1.3.9",
"@types/fluent-ffmpeg": "^2.1.28", "@types/fluent-ffmpeg": "^2.1.28",
"@types/node": "^25.3.0", "@types/node": "^25.2.3",
"@types/qrcode": "^1.5.6", "@types/qrcode": "^1.5.6",
"@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/eslint-plugin": "^8.55.0",
"@typescript-eslint/parser": "^8.56.1", "@typescript-eslint/parser": "^8.55.0",
"drizzle-kit": "^1.0.0-beta.9-e89174b", "drizzle-kit": "^1.0.0-beta.9-e89174b",
"eslint": "^9.39.3", "eslint": "^9.39.2",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
}, },
@@ -156,7 +156,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=="], "@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.43.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-hklCsJNdMlDM1IwcCVcGQFBg2izY0+t5BIGbRsxi2UnKi6AGKL7pqJqmBDNRbw0bYCs4y3NA7TB+fkKfP/Nrdw=="], "@google/genai": ["@google/genai@1.42.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-+3nlMTcrQufbQ8IumGkOphxD5Pd5kKyJOzLcnY0/1IuE8upJk5aLmoexZ2BJhBp1zAjRJMEB4a2CJwKI9e2EYw=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@@ -248,7 +248,7 @@
"@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.22", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="], "@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.22", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="],
"@mistralai/mistralai": ["@mistralai/mistralai@1.14.1", "", { "dependencies": { "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ=="], "@mistralai/mistralai": ["@mistralai/mistralai@1.14.0", "", { "dependencies": { "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-6zaj2f2LCd37cRpBvCgctkDbXtYBlAC85p+u4uU/726zjtsI+sdVH34qRzkm9iE3tRb8BoaiI0/P7TD+uMvLLQ=="],
"@napi-rs/canvas": ["@napi-rs/canvas@0.1.95", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.95", "@napi-rs/canvas-darwin-arm64": "0.1.95", "@napi-rs/canvas-darwin-x64": "0.1.95", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.95", "@napi-rs/canvas-linux-arm64-gnu": "0.1.95", "@napi-rs/canvas-linux-arm64-musl": "0.1.95", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.95", "@napi-rs/canvas-linux-x64-gnu": "0.1.95", "@napi-rs/canvas-linux-x64-musl": "0.1.95", "@napi-rs/canvas-win32-arm64-msvc": "0.1.95", "@napi-rs/canvas-win32-x64-msvc": "0.1.95" } }, "sha512-lkg23ge+rgyhgUwXmlbkPEhuhHq/hUi/gXKH+4I7vO+lJrbNfEYcQdJLIGjKyXLQzgFiiyDAwh5vAe/tITAE+w=="], "@napi-rs/canvas": ["@napi-rs/canvas@0.1.95", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.95", "@napi-rs/canvas-darwin-arm64": "0.1.95", "@napi-rs/canvas-darwin-x64": "0.1.95", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.95", "@napi-rs/canvas-linux-arm64-gnu": "0.1.95", "@napi-rs/canvas-linux-arm64-musl": "0.1.95", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.95", "@napi-rs/canvas-linux-x64-gnu": "0.1.95", "@napi-rs/canvas-linux-x64-musl": "0.1.95", "@napi-rs/canvas-win32-arm64-msvc": "0.1.95", "@napi-rs/canvas-win32-x64-msvc": "0.1.95" } }, "sha512-lkg23ge+rgyhgUwXmlbkPEhuhHq/hUi/gXKH+4I7vO+lJrbNfEYcQdJLIGjKyXLQzgFiiyDAwh5vAe/tITAE+w=="],
@@ -318,7 +318,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/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.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="], "@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
"@types/qrcode": ["@types/qrcode@1.5.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="], "@types/qrcode": ["@types/qrcode@1.5.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="],
@@ -376,7 +376,7 @@
"asynckit": ["asynckit@0.4.0", "", {}, "sha1-x57Zf380y48robyXkLzDZkdLS3k="], "asynckit": ["asynckit@0.4.0", "", {}, "sha1-x57Zf380y48robyXkLzDZkdLS3k="],
"axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], "axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="],
"b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="], "b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="],
+16 -16
View File
@@ -8,11 +8,11 @@
"name": "tg-chat-bot", "name": "tg-chat-bot",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@google/genai": "^1.43.0", "@google/genai": "^1.42.0",
"@libsql/client": "^0.17.0", "@libsql/client": "^0.17.0",
"@mistralai/mistralai": "^1.14.1", "@mistralai/mistralai": "^1.14.0",
"@napi-rs/canvas": "^0.1.95", "@napi-rs/canvas": "^0.1.95",
"axios": "^1.13.6", "axios": "^1.13.5",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"drizzle-orm": "^1.0.0-beta.15-9485290", "drizzle-orm": "^1.0.0-beta.15-9485290",
"emoji-regex": "^10.6.0", "emoji-regex": "^10.6.0",
@@ -33,7 +33,7 @@
"devDependencies": { "devDependencies": {
"@types/bun": "^1.3.9", "@types/bun": "^1.3.9",
"@types/fluent-ffmpeg": "^2.1.28", "@types/fluent-ffmpeg": "^2.1.28",
"@types/node": "^25.3.3", "@types/node": "^25.3.0",
"@types/qrcode": "^1.5.6", "@types/qrcode": "^1.5.6",
"@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1", "@typescript-eslint/parser": "^8.56.1",
@@ -996,9 +996,9 @@
} }
}, },
"node_modules/@google/genai": { "node_modules/@google/genai": {
"version": "1.43.0", "version": "1.42.0",
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.43.0.tgz", "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.42.0.tgz",
"integrity": "sha512-hklCsJNdMlDM1IwcCVcGQFBg2izY0+t5BIGbRsxi2UnKi6AGKL7pqJqmBDNRbw0bYCs4y3NA7TB+fkKfP/Nrdw==", "integrity": "sha512-+3nlMTcrQufbQ8IumGkOphxD5Pd5kKyJOzLcnY0/1IuE8upJk5aLmoexZ2BJhBp1zAjRJMEB4a2CJwKI9e2EYw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"google-auth-library": "^10.3.0", "google-auth-library": "^10.3.0",
@@ -1734,9 +1734,9 @@
] ]
}, },
"node_modules/@mistralai/mistralai": { "node_modules/@mistralai/mistralai": {
"version": "1.14.1", "version": "1.14.0",
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.14.1.tgz", "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.14.0.tgz",
"integrity": "sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ==", "integrity": "sha512-6zaj2f2LCd37cRpBvCgctkDbXtYBlAC85p+u4uU/726zjtsI+sdVH34qRzkm9iE3tRb8BoaiI0/P7TD+uMvLLQ==",
"dependencies": { "dependencies": {
"ws": "^8.18.0", "ws": "^8.18.0",
"zod": "^3.25.0 || ^4.0.0", "zod": "^3.25.0 || ^4.0.0",
@@ -2276,9 +2276,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "25.3.3", "version": "25.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz",
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.18.0" "undici-types": "~7.18.0"
@@ -2743,9 +2743,9 @@
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.13.6", "version": "1.13.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.11", "follow-redirects": "^1.15.11",
+4 -4
View File
@@ -8,11 +8,11 @@
"bun:start": "bun run dist/index.js" "bun:start": "bun run dist/index.js"
}, },
"dependencies": { "dependencies": {
"@google/genai": "^1.43.0", "@google/genai": "^1.42.0",
"@libsql/client": "^0.17.0", "@libsql/client": "^0.17.0",
"@mistralai/mistralai": "^1.14.1", "@mistralai/mistralai": "^1.14.0",
"@napi-rs/canvas": "^0.1.95", "@napi-rs/canvas": "^0.1.95",
"axios": "^1.13.6", "axios": "^1.13.5",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"drizzle-orm": "^1.0.0-beta.15-9485290", "drizzle-orm": "^1.0.0-beta.15-9485290",
"emoji-regex": "^10.6.0", "emoji-regex": "^10.6.0",
@@ -32,7 +32,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.3.9", "@types/bun": "^1.3.9",
"@types/node": "^25.3.3", "@types/node": "^25.3.0",
"@types/qrcode": "^1.5.6", "@types/qrcode": "^1.5.6",
"@types/fluent-ffmpeg": "^2.1.28", "@types/fluent-ffmpeg": "^2.1.28",
"@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/eslint-plugin": "^8.56.1",
@@ -1,36 +0,0 @@
import {CallbackCommand} from "../base/callback-command";
import {CallbackQuery} from "typescript-telegram-bot-api";
import {Requirements} from "../base/requirements";
import {Requirement} from "../base/requirement";
import {commands} from "../index";
import {YouTubeDownload} from "../commands/youtube-download";
const downloadText = " 📥 Скачать";
const getFromCacheText = "📥 Загрузить из кэша";
export class DownloadYtVideo extends CallbackCommand {
data = "/ytdl";
text = " 📥 Скачать";
requirements = Requirements.Build(Requirement.SAME_USER);
constructor(text?: string, data?: string) {
super();
this.text = text || this.text;
this.data = data || this.data;
}
static withData(inCache?: boolean, data?: string): DownloadYtVideo {
return new DownloadYtVideo(inCache ? getFromCacheText : downloadText, data);
}
async execute(query: CallbackQuery): Promise<void> {
const videoId = query.data.split(" ")[1];
if (!videoId) return;
const yt = commands.find(c => c instanceof YouTubeDownload);
if (!yt) return;
await yt.downloadYouTubeVideo(query.message, {videoId: videoId});
}
}
-21
View File
@@ -1,21 +0,0 @@
import {CallbackCommand} from "../base/callback-command";
export class TryAgain extends CallbackCommand {
data = "";
text = "🔁 Повторить";
constructor(text?: string, data?: string) {
super();
this.text = text ?? this.text;
this.data = data ?? this.data;
}
static withData(data?: string): TryAgain {
return new TryAgain(null, data);
}
async execute(): Promise<void> {
return Promise.resolve();
}
}
-15
View File
@@ -1,15 +0,0 @@
import {CallbackCommand} from "../base/callback-command";
import {CallbackQuery} from "typescript-telegram-bot-api";
import {processYouTubeLink} from "../util/utils";
export class YtInfo extends CallbackCommand {
data = "/ytinfo";
text: string;
async execute(query: CallbackQuery): Promise<void> {
const videoId = query.data.split(" ")[1];
if (!videoId) return;
await processYouTubeLink(query.message, null, videoId);
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ export class GeminiChat extends ChatCommand {
try { try {
waitMessage = await bot.sendMessage({ waitMessage = await bot.sendMessage({
chat_id: chatId, chat_id: chatId,
text: Environment.waitThinkText, text: Environment.waitText,
reply_parameters: { reply_parameters: {
chat_id: chatId, chat_id: chatId,
message_id: msg.message_id message_id: msg.message_id
+1 -1
View File
@@ -53,7 +53,7 @@ export class GeminiGenerateImage extends Command {
await replyToMessage({ await replyToMessage({
message: waitMessage, message: waitMessage,
text: `Произошла ошибка!\n${e.toString()}`, text: `Произошла ошибка!\n${e.toString()}`,
link_preview_options: {is_disabled: true} disableLinkPreview: true
}).catch(logError); }).catch(logError);
} }
} }
+1 -1
View File
@@ -90,7 +90,7 @@ export class MistralChat extends ChatCommand {
chat_id: chatId, chat_id: chatId,
text: imagesCount ? text: imagesCount ?
imagesCount > 1 ? Environment.analyzingPicturesText : Environment.analyzingPictureText imagesCount > 1 ? Environment.analyzingPicturesText : Environment.analyzingPictureText
: Environment.waitThinkText, : Environment.waitText,
reply_parameters: { reply_parameters: {
chat_id: chatId, chat_id: chatId,
+1 -1
View File
@@ -96,7 +96,7 @@ export class OllamaChat extends ChatCommand {
message: msg, message: msg,
text: (!think && imagesCount) ? text: (!think && imagesCount) ?
imagesCount > 1 ? Environment.analyzingPicturesText : Environment.analyzingPictureText imagesCount > 1 ? Environment.analyzingPicturesText : Environment.analyzingPictureText
: Environment.waitThinkText : Environment.waitText
}); });
const stream = await ollama.chat({ const stream = await ollama.chat({
+1 -1
View File
@@ -37,7 +37,7 @@ export class OllamaPrompt extends Command {
waitMessage = await bot.sendMessage({ waitMessage = await bot.sendMessage({
chat_id: chatId, chat_id: chatId,
text: Environment.waitThinkText, text: Environment.waitText,
reply_parameters: { reply_parameters: {
chat_id: chatId, chat_id: chatId,
message_id: msg.message_id message_id: msg.message_id
+3 -3
View File
@@ -4,7 +4,7 @@ import {Requirement} from "../base/requirement";
import {Message} from "typescript-telegram-bot-api"; import {Message} from "typescript-telegram-bot-api";
import {bot, ollama} from "../index"; import {bot, ollama} from "../index";
import {WebSearchResponse} from "../model/web-search-response"; import {WebSearchResponse} from "../model/web-search-response";
import {oldEditMessageText, logError} from "../util/utils"; import {editMessageText, logError} from "../util/utils";
import {Environment} from "../common/environment"; import {Environment} from "../common/environment";
export class OllamaSearch extends Command { export class OllamaSearch extends Command {
@@ -23,7 +23,7 @@ export class OllamaSearch extends Command {
try { try {
const wait = await bot.sendMessage({ const wait = await bot.sendMessage({
chat_id: chatId, chat_id: chatId,
text: Environment.waitThinkText, text: Environment.waitText,
reply_parameters: { reply_parameters: {
chat_id: chatId, chat_id: chatId,
message_id: msg.message_id message_id: msg.message_id
@@ -40,7 +40,7 @@ export class OllamaSearch extends Command {
message += `${index + 1}. ${r.url}\n`; message += `${index + 1}. ${r.url}\n`;
}); });
await oldEditMessageText(chatId, wait.message_id, message); await editMessageText(chatId, wait.message_id, message);
} catch (error) { } catch (error) {
logError(error); logError(error);
} }
+1 -1
View File
@@ -71,7 +71,7 @@ export class OpenAIChat extends ChatCommand {
try { try {
waitMessage = await bot.sendMessage({ waitMessage = await bot.sendMessage({
chat_id: chatId, chat_id: chatId,
text: Environment.waitThinkText, text: Environment.waitText,
reply_parameters: { reply_parameters: {
chat_id: chatId, chat_id: chatId,
message_id: msg.message_id message_id: msg.message_id
+2 -2
View File
@@ -5,7 +5,7 @@ import {Requirement} from "../base/requirement";
import {bot, openAi, photoGenDir} from "../index"; import {bot, openAi, photoGenDir} from "../index";
import fs from "node:fs"; import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import {oldEditMessageText, logError, replyToMessage} from "../util/utils"; import {editMessageText, logError, replyToMessage} from "../util/utils";
import {Environment} from "../common/environment"; import {Environment} from "../common/environment";
import {APIError} from "openai"; import {APIError} from "openai";
@@ -102,7 +102,7 @@ export class OpenAIGenImage extends ChatCommand {
const text = "❌ Мне запрещено такое генерировать 😠"; const text = "❌ Мне запрещено такое генерировать 😠";
if (waitMessage) { if (waitMessage) {
await oldEditMessageText(msg.chat.id, waitMessage.message_id, text).catch(logError); await editMessageText(msg.chat.id, waitMessage.message_id, text).catch(logError);
} else { } else {
await replyToMessage({message: msg, text: text}).catch(logError); await replyToMessage({message: msg, text: text}).catch(logError);
} }
+11 -23
View File
@@ -1,10 +1,8 @@
import {Command} from "../base/command"; import {Command} from "../base/command";
import {Message} from "typescript-telegram-bot-api"; import {Message} from "typescript-telegram-bot-api";
import {editMessageText, logError, replyToMessage} from "../util/utils"; import {logError, replyToMessage} from "../util/utils";
import {bot, botUser} from "../index"; import {bot} from "../index";
import {DownloadOptions, downloadVideoFromYouTube, getYouTubeVideoId} from "../util/ytdl"; import {downloadVideoFromYouTube} from "../util/ytdl";
import {Environment} from "../common/environment";
import {TryAgain} from "../callback_commands/try-again";
export class YouTubeDownload extends Command { export class YouTubeDownload extends Command {
command = ["ytdl", "youtube"]; command = ["ytdl", "youtube"];
@@ -12,22 +10,16 @@ export class YouTubeDownload extends Command {
async execute(msg: Message, match?: RegExpExecArray): Promise<void> { async execute(msg: Message, match?: RegExpExecArray): Promise<void> {
const url = match?.[3]; const url = match?.[3];
return this.downloadYouTubeVideo(msg, {url: url}); return this.downloadYouTubeVideo(msg, url);
} }
async downloadYouTubeVideo(msg: Message, options: DownloadOptions): Promise<void> { async downloadYouTubeVideo(msg: Message, url: string): Promise<void> {
// TODO: 02.03.2026, Danil Nikolaev: add check for date let waitMessage: Message | null = null;
let waitMessage: Message | null = (msg.from.id === botUser.id) ? msg : null;
const videoId = "videoId" in options ? options.videoId : getYouTubeVideoId(options.url);
try { try {
if (!waitMessage) { waitMessage = await replyToMessage({message: msg, text: "⏳ Секунду..."});
waitMessage = await replyToMessage({message: msg, text: "⏳ Скачиваю видео..."});
} else {
await editMessageText({message: msg, text: "⏳ Скачиваю видео..."});
}
const {time, exists, buffer} = await downloadVideoFromYouTube({videoId: videoId}); const {time, exists, buffer} = await downloadVideoFromYouTube(url);
if (buffer) { if (buffer) {
const start = Date.now(); const start = Date.now();
waitMessage = await bot.editMessageMedia({ waitMessage = await bot.editMessageMedia({
@@ -43,7 +35,8 @@ export class YouTubeDownload extends Command {
waitMessage = await bot.editMessageCaption({ waitMessage = await bot.editMessageCaption({
chat_id: msg.chat.id, chat_id: msg.chat.id,
message_id: waitMessage.message_id, message_id: waitMessage.message_id,
caption: "✅ Видео" + (exists ? " загружено из кэша" : " успешно скачано") + " за " + (time + diff) + "мс", caption: `[Видео](${url})` + (exists ? " загружено из кэша" : " успешно скачано") + " за " + (time + diff) + "мс",
parse_mode: "MarkdownV2"
}) as Message; }) as Message;
} }
} catch (e) { } catch (e) {
@@ -53,12 +46,7 @@ export class YouTubeDownload extends Command {
await bot.editMessageText({ await bot.editMessageText({
chat_id: msg.chat.id, chat_id: msg.chat.id,
message_id: waitMessage.message_id, message_id: waitMessage.message_id,
text: Environment.errorText, text: `⚠️ Произошла ошибка.\n${e}`,
reply_markup: {
inline_keyboard: [[
TryAgain.withData("/ytdl " + videoId).asButton()
]]
}
}); });
} }
} }
+1 -7
View File
@@ -27,8 +27,6 @@ export class Environment {
static MAX_PHOTO_SIZE: number; static MAX_PHOTO_SIZE: number;
static PROCESS_LINKS: boolean;
static DEFAULT_AI_PROVIDER: AiProvider; static DEFAULT_AI_PROVIDER: AiProvider;
static SYSTEM_PROMPT?: string; static SYSTEM_PROMPT?: string;
@@ -51,9 +49,7 @@ export class Environment {
static OPENAI_MODEL: string; static OPENAI_MODEL: string;
static OPENAI_IMAGE_MODEL: string; static OPENAI_IMAGE_MODEL: string;
static errorText = "⚠️ Произошла ошибка."; static waitText = "⏳ Дайте-ка подумать...";
static waitText = "⏳ Секунду...";
static waitThinkText = "⏳ Дайте-ка подумать...";
static analyzingPictureText = "🔍 Внимательно изучаю изображение..."; static analyzingPictureText = "🔍 Внимательно изучаю изображение...";
static analyzingPicturesText = "🔍 Внимательно изучаю изображения..."; static analyzingPicturesText = "🔍 Внимательно изучаю изображения...";
static genImageText = "👨‍🎨 Генерирую изображение..."; static genImageText = "👨‍🎨 Генерирую изображение...";
@@ -77,8 +73,6 @@ export class Environment {
Environment.MAX_PHOTO_SIZE = Number(process.env.MAX_PHOTO_SIZE || "1280"); Environment.MAX_PHOTO_SIZE = Number(process.env.MAX_PHOTO_SIZE || "1280");
Environment.PROCESS_LINKS = ifTrue(process.env.PROCESS_LINKS);
const aiProvider = process.env.DEFAULT_AI_PROVIDER || "OLLAMA"; const aiProvider = process.env.DEFAULT_AI_PROVIDER || "OLLAMA";
if (Object.values(AiProvider).includes(aiProvider as AiProvider)) { if (Object.values(AiProvider).includes(aiProvider as AiProvider)) {
Environment.DEFAULT_AI_PROVIDER = aiProvider as AiProvider; Environment.DEFAULT_AI_PROVIDER = aiProvider as AiProvider;
+1 -5
View File
@@ -79,8 +79,6 @@ import {OpenAISetModel} from "./commands/openai-set-model";
import {Info} from "./commands/info"; import {Info} from "./commands/info";
import {OpenAIGenImage} from "./commands/openai-gen-image"; import {OpenAIGenImage} from "./commands/openai-gen-image";
import {clearUpFolderFromOldFiles} from "./util/files"; import {clearUpFolderFromOldFiles} from "./util/files";
import {DownloadYtVideo} from "./callback_commands/download-yt-video";
import {YtInfo} from "./callback_commands/yt-info";
process.setUncaughtExceptionCaptureCallback(logError); process.setUncaughtExceptionCaptureCallback(logError);
@@ -173,9 +171,7 @@ if (Environment.ENABLE_UNSAFE_EVAL) {
} }
export const callbackCommands: CallbackCommand[] = [ export const callbackCommands: CallbackCommand[] = [
new OllamaCancel(), new OllamaCancel()
new DownloadYtVideo(),
new YtInfo()
]; ];
if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM_PROMPT) { if (Environment.OLLAMA_ADDRESS && Environment.OLLAMA_MODEL && Environment.SYSTEM_PROMPT) {
-15
View File
@@ -1,15 +0,0 @@
import {InlineKeyboardMarkup, Message, ParseMode} from "typescript-telegram-bot-api";
import {LinkPreviewOptions, MessageEntity} from "typescript-telegram-bot-api/dist/types";
export type EditOptions = ({
message: Message
} | {
chat_id: number;
message_id: number;
}) & {
text: string;
parse_mode?: ParseMode;
entities?: MessageEntity[];
link_preview_options?: LinkPreviewOptions;
reply_markup?: InlineKeyboardMarkup;
}
-77
View File
@@ -1,77 +0,0 @@
import {InlineKeyboardMarkup, Message, ParseMode} from "typescript-telegram-bot-api";
import {
ForceReply,
LinkPreviewOptions,
MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove,
ReplyParameters,
SuggestedPostParameters
} from "typescript-telegram-bot-api/dist/types";
export type SendOptions = ({
message: Message
} | {
/**
* Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
*/
chat_id: number | string;
message_id?: number;
}) & {
/**
* Unique identifier for the target message thread (topic) of the forum; for forum supergroups only
*/
message_thread_id?: number;
/**
* Identifier of the direct messages topic to which the message will be sent; required if the message is sent to a
* direct messages chat
*/
direct_messages_topic_id?: number;
/**
* Text of the message to be sent, 1-4096 characters after entities parsing
*/
text: string;
/**
* Mode for parsing entities in the message text. See formatting options for more details.
*/
parse_mode?: ParseMode;
/**
* A JSON-serialized list of special entities that appear in message text, which can be specified instead of
* parse_mode
*/
entities?: MessageEntity[];
/**
* Link preview generation options for the message
*/
link_preview_options?: LinkPreviewOptions;
/**
* Sends the message silently. Users will receive a notification with no sound.
*/
disable_notification?: boolean;
/**
* Protects the contents of the sent message from forwarding and saving
*/
protect_content?: boolean;
/**
* Pass True to allow up to 1000 messages per second, ignoring
* [broadcasting limits](https://core.telegram.org/bots/faq#how-can-i-message-all-of-my-bot-39s-subscribers-at-once)
* for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance
*/
allow_paid_broadcast?: boolean;
/**
* Unique identifier of the message effect to be added to the message; for private chats only
*/
message_effect_id?: string;
/**
* A JSON-serialized object containing the parameters of the suggested post to send; for direct messages chats only.
* If the message is sent as a reply to another suggested post, then that suggested post is automatically declined.
*/
suggested_post_parameters?: SuggestedPostParameters;
/**
* Description of the message to reply to
*/
reply_parameters?: ReplyParameters;
/**
* Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard,
* instructions to remove a reply keyboard or to force a reply from the user
*/
reply_markup?: InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove | ForceReply;
};
+78 -188
View File
@@ -11,7 +11,6 @@ import {
Message, Message,
ParseMode, ParseMode,
PhotoSize, PhotoSize,
TelegramBot,
User User
} from "typescript-telegram-bot-api"; } from "typescript-telegram-bot-api";
import {Environment} from "../common/environment"; import {Environment} from "../common/environment";
@@ -31,7 +30,7 @@ import {MessageStore} from "../common/message-store";
import {SystemInfo} from "../commands/system-info"; import {SystemInfo} from "../commands/system-info";
import {PrefixResponse} from "../commands/prefix-response"; import {PrefixResponse} from "../commands/prefix-response";
import {OllamaChat} from "../commands/ollama-chat"; import {OllamaChat} from "../commands/ollama-chat";
import {getYouTubeVideoId, getYouTubeVideoInfo, isVideoExists} from "./ytdl"; import {getYouTubeVideoId} from "./ytdl";
import {YouTubeDownload} from "../commands/youtube-download"; import {YouTubeDownload} from "../commands/youtube-download";
import {ChatCommand} from "../base/chat-command"; import {ChatCommand} from "../base/chat-command";
import {WebSearchResponse} from "../model/web-search-response"; import {WebSearchResponse} from "../model/web-search-response";
@@ -44,11 +43,6 @@ import {OllamaGetModel} from "../commands/ollama-get-model";
import {GeminiGetModel} from "../commands/gemini-get-model"; import {GeminiGetModel} from "../commands/gemini-get-model";
import {MistralGetModel} from "../commands/mistral-get-model"; import {MistralGetModel} from "../commands/mistral-get-model";
import {OpenAIGetModel} from "../commands/openai-get-model"; import {OpenAIGetModel} from "../commands/openai-get-model";
import {SendOptions} from "../model/send-options";
import {EditOptions} from "../model/edit-options";
import VideoInfo from "youtubei.js/dist/src/parser/youtube/VideoInfo";
import {DownloadYtVideo} from "../callback_commands/download-yt-video";
import {TryAgain} from "../callback_commands/try-again";
export const ignore = () => { export const ignore = () => {
}; };
@@ -60,7 +54,7 @@ export const ignoreIfNotChanged = (e: Error | TelegramError) => {
}; };
export const ignoreIfMarkupFailed = (e: Error | TelegramError) => { export const ignoreIfMarkupFailed = (e: Error | TelegramError) => {
if (!isMarkupFailed(e)) { if (!(e instanceof TelegramError && e?.response?.description?.startsWith("Bad Request: can't parse entities"))) {
throw e; throw e;
} }
}; };
@@ -73,18 +67,6 @@ export const errorPlaceholder = async (msg: Message) => {
await sendErrorPlaceholder(msg).catch(logError); await sendErrorPlaceholder(msg).catch(logError);
}; };
export const isMarkupFailed = (e: Error | TelegramError) => {
return TelegramBot.isTelegramError(e) && e?.response?.description?.startsWith("Bad Request: can't parse entities");
};
export const isTooManyRequests = (e: Error | TelegramError) => {
return TelegramBot.isTelegramError(e) && e.response.description.includes("Too Many Requests");
};
export const isMessageTooLong = (e: Error | TelegramError) => {
return TelegramBot.isTelegramError(e) && e.response.description.includes("MESSAGE_TOO_LONG");
};
export function searchChatCommand( export function searchChatCommand(
commands: Command[], commands: Command[],
text: string, text: string,
@@ -135,7 +117,7 @@ export async function checkRequirements(cmd: Command | CallbackCommand | null, m
const cbId = cb?.id; const cbId = cb?.id;
const chatId = msg?.chat?.id || cb?.message?.chat?.id || -1; const chatId = msg?.chat?.id || cb?.message?.chat?.id || -1;
const messageId = msg?.message_id || (cb && cb.message && "reply_to_message" in cb.message ? cb.message.reply_to_message.message_id : null) || -1; const messageId = msg?.message_id || cb?.message?.message_id || -1;
const fromId = msg?.from?.id || cb?.from?.id || -1; const fromId = msg?.from?.id || cb?.from?.id || -1;
const chatType = msg?.chat?.type || cb?.message?.chat?.type || null; const chatType = msg?.chat?.type || cb?.message?.chat?.type || null;
@@ -214,8 +196,11 @@ export async function checkRequirements(cmd: Command | CallbackCommand | null, m
if (reqs.isRequiresSameUser()) { if (reqs.isRequiresSameUser()) {
let originalFromId: number | null; let originalFromId: number | null;
try { try {
const originalMessage = await MessageStore.get(chatId, messageId); const queryMessage = await MessageStore.get(chatId, messageId);
if (queryMessage && queryMessage.replyToMessageId) {
const originalMessage = await MessageStore.get(chatId, queryMessage.replyToMessageId);
originalFromId = originalMessage?.fromId; originalFromId = originalMessage?.fromId;
}
} catch (e) { } catch (e) {
logError(e); logError(e);
originalFromId = null; originalFromId = null;
@@ -254,87 +239,92 @@ export async function findAndExecuteCallbackCommand(commands: CallbackCommand[],
return true; return true;
} }
export async function oldEditMessageText(chatId: number, messageId: number, messageText: string, parseMode?: ParseMode, replyMarkup?: InlineKeyboardMarkup): Promise<boolean | Message> { export async function editMessageText(chatId: number, messageId: number, messageText: string, parseMode?: ParseMode, replyMarkup?: InlineKeyboardMarkup): Promise<void> {
return editMessageText({ if (messageText.trim().length === 0) return Promise.resolve();
try {
await bot.editMessageText({
chat_id: chatId, chat_id: chatId,
message_id: messageId, message_id: messageId,
text: messageText, text: messageText,
parse_mode: parseMode, parse_mode: parseMode,
reply_markup: replyMarkup, link_preview_options: {
link_preview_options: {is_disabled: true} is_disabled: true
}); },
} reply_markup: replyMarkup
}).catch(ignoreIfMarkupFailed);
export async function editMessageText(options: EditOptions) { return Promise.resolve();
if (options.text.trim().length === 0) return Promise.resolve(false);
try {
const message = await bot.editMessageText({
chat_id: "message" in options ? options.message.chat.id : options.chat_id,
message_id: "message" in options ? options.message.message_id : options.message_id,
text: options.text,
parse_mode: options.parse_mode,
reply_markup: options.reply_markup,
link_preview_options: options.link_preview_options,
});
return Promise.resolve(message);
} catch (e) { } catch (e) {
logError(e); logError(e);
if (isMarkupFailed(e)) { if (e instanceof TelegramError && e.response.description.includes("Too Many Requests")) {
return Promise.resolve(true);
} else if (isTooManyRequests(e)) {
const delay = Number(e.message.split("retry after ")[1]) || 30; const delay = Number(e.message.split("retry after ")[1]) || 30;
setTimeout(() => { setTimeout(() => {
return Promise.resolve(); return Promise.resolve();
}, delay * 1000); }, delay * 1000);
} else { } else if (e instanceof TelegramError && e.response.description.includes("MESSAGE_TOO_LONG")) {
return Promise.reject(e); return Promise.reject(e);
} else {
return Promise.resolve();
} }
} }
} }
export type SendOptions = {
chat_id?: number;
message?: Message,
message_id?: number;
text: string,
parse_mode?: ParseMode,
disableLinkPreview?: boolean
};
export async function oldSendMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> { export async function oldSendMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> {
return sendMessage({ const response = await bot.sendMessage({
message: message, chat_id: message.chat.id,
text: text, text: text,
parse_mode: parseMode parse_mode: parseMode
}); });
return Promise.resolve(response);
} }
export async function sendMessage(options: SendOptions): Promise<Message> { export async function sendMessage(options: SendOptions): Promise<Message> {
const response = await bot.sendMessage({ const response = await bot.sendMessage({
chat_id: "message" in options ? options.message.chat.id : options.chat_id, chat_id: options.chat_id ?? options.message?.chat?.id,
text: options.text, text: options.text,
parse_mode: options.parse_mode, parse_mode: options.parse_mode,
link_preview_options: options.link_preview_options, link_preview_options: {
reply_markup: options.reply_markup, is_disabled: options.disableLinkPreview
}
});
return Promise.resolve(response);
}
export async function replyToMessage(options: SendOptions): Promise<Message> {
const response = await bot.sendMessage({
chat_id: options.chat_id ?? options.message?.chat?.id,
text: options.text,
parse_mode: options.parse_mode,
reply_parameters: {
message_id: options.message_id || options.message?.message_id
},
link_preview_options: {
is_disabled: options.disableLinkPreview
}
}); });
return Promise.resolve(response); return Promise.resolve(response);
} }
export async function oldReplyToMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> { export async function oldReplyToMessage(message: Message, text: string, parseMode?: ParseMode): Promise<Message> {
return replyToMessage({
message: message,
text: text,
parse_mode: parseMode
});
}
export async function replyToMessage(options: SendOptions): Promise<Message> {
if (!("message" in options) && !options.message_id) {
return Promise.reject("for reply there must be message or message_id");
}
const response = await bot.sendMessage({ const response = await bot.sendMessage({
chat_id: "message" in options ? options.message.chat.id : options.chat_id, chat_id: message.chat.id,
text: options.text, text: text,
parse_mode: options.parse_mode,
reply_parameters: { reply_parameters: {
message_id: "message" in options ? options.message.message_id : options.message_id message_id: message.message_id
}, },
link_preview_options: options.link_preview_options parse_mode: parseMode,
}); });
return Promise.resolve(response); return Promise.resolve(response);
@@ -1210,8 +1200,27 @@ export async function processNewMessage(msg: Message): Promise<void> {
} }
const textToCheck = startsWithPrefix ? messageWithoutPrefix : cmdText; const textToCheck = startsWithPrefix ? messageWithoutPrefix : cmdText;
if (msg.entities) {
const urlEntities = msg.entities.filter(e => e.type === "url");
if (urlEntities.length) {
for (const e of urlEntities) {
const url = msg.text.substring(e.offset, e.offset + e.length);
// TODO: 31/01/2026, Danil Nikolaev: implement proper checking
try {
getYouTubeVideoId(url);
const yt = commands.find(e => e instanceof YouTubeDownload);
if (await checkRequirements(yt, msg)) {
await yt.downloadYouTubeVideo(msg, url);
}
return;
} catch (e) {
logError(e);
}
}
}
}
if (Environment.PROCESS_LINKS && await processYouTubeLink(msg, getFirstLink(msg))) return;
if (!startsWithPrefix && msg.chat.type !== "private") return; if (!startsWithPrefix && msg.chat.type !== "private") return;
if (msg.chat.type === "private" && !Environment.ADMIN_IDS.has(msg.chat.id)) return; if (msg.chat.type === "private" && !Environment.ADMIN_IDS.has(msg.chat.id)) return;
@@ -1235,125 +1244,6 @@ export async function processNewMessage(msg: Message): Promise<void> {
} }
} }
function getFirstLink(msg: Message): string | null {
if (msg.entities) {
const urlEntities = msg.entities.filter(e => e.type === "url");
if (urlEntities.length) {
const e = urlEntities[0];
return msg.text.substring(e.offset, e.offset + e.length);
}
}
return null;
}
export async function processYouTubeLink(msg: Message, url?: string, id?: string): Promise<boolean> {
if (!url && !id) return false;
let waitMessage: Message | null = msg.from.id === botUser.id ? msg : null;
let videoId: string | null = null;
try {
try {
videoId = id || getYouTubeVideoId(url);
} catch (e) {
logError(e);
return false;
}
const yt = commands.find(e => e instanceof YouTubeDownload);
if (await checkRequirements(yt, msg)) {
if (!waitMessage) {
waitMessage = await replyToMessage({
message: msg,
text: "⏳ Ищу информацию о видео..."
});
} else {
await editMessageText({message: msg, text: "⏳ Ищу информацию о видео..."});
}
let videoInfo: VideoInfo | null = null;
let ytError: string = null;
try {
videoInfo = await getYouTubeVideoInfo(videoId);
} catch (e) {
logError(e);
if ("version" in e) {
ytError = e.message;
}
}
console.log("VIDEO_INFO", videoInfo);
let text: string = null;
const inCache = isVideoExists({videoId: videoId});
const duration = videoInfo?.basic_info?.duration || null;
const canDownload = inCache || duration && duration <= 300;
if (videoInfo) {
text = "Видео с YouTube\n\n" +
`Название: ${videoInfo.basic_info?.title}\n` +
`Автор: ${videoInfo.secondary_info?.owner?.author?.name}\n` +
`Длительность: ${duration} сек.`;
if (!canDownload) {
text += `\n\nВидео слишком длинное (${duration} сек. > 300 сек.)`;
}
} else if (!ytError) {
text = "Информация о видео не найдена";
}
const errorButInCache = !videoInfo && ytError && inCache;
if (errorButInCache) {
text = "Я не смог получить информацию о видео, но нашёл его в кэше.";
}
if (!text && ytError) {
await editMessageText({
message: waitMessage,
text: Environment.errorText,
reply_markup: {
inline_keyboard: [[
TryAgain.withData("/ytinfo " + videoId).asButton()
]]
}
});
} else {
await editMessageText({
message: waitMessage,
text: text,
reply_markup: canDownload ? {
inline_keyboard: [[
DownloadYtVideo.withData(inCache, "/ytdl " + videoId).asButton()
]]
} : {inline_keyboard: []}
});
}
}
return true;
} catch (e) {
logError(e);
await editMessageText({
message: waitMessage,
text: Environment.errorText,
reply_markup: {
inline_keyboard: [[
TryAgain.withData("/ytinfo " + videoId).asButton()
]]
}
});
}
return false;
}
export async function processEditedMessage(msg: Message): Promise<void> { export async function processEditedMessage(msg: Message): Promise<void> {
console.log("Edited Message", msg); console.log("Edited Message", msg);
+7 -48
View File
@@ -6,21 +6,6 @@ import Innertube, {Platform, Types} from "youtubei.js";
import {Readable} from "node:stream"; import {Readable} from "node:stream";
import {logError} from "./utils"; import {logError} from "./utils";
import {performFFmpeg} from "./ffmpeg"; import {performFFmpeg} from "./ffmpeg";
import VideoInfo from "youtubei.js/dist/src/parser/youtube/VideoInfo";
let innertube: Innertube | null = null;
export async function getYT(): Promise<Innertube> {
if (innertube) {
return innertube;
} else {
innertube = await Innertube.create({
generate_session_locally: true,
retrieve_player: true
});
return innertube;
}
}
export function getYouTubeVideoId(url: string): string { export function getYouTubeVideoId(url: string): string {
const regex = /(?:(?:youtube\.com|music\.youtube\.com)\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?|shorts|clip)\/|.*[?&]v=)|youtu\.be\/)([^"&?/\s]{11})/i; const regex = /(?:(?:youtube\.com|music\.youtube\.com)\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?|shorts|clip)\/|.*[?&]v=)|youtu\.be\/)([^"&?/\s]{11})/i;
@@ -29,34 +14,7 @@ export function getYouTubeVideoId(url: string): string {
return match[1]; return match[1];
} }
export async function getYouTubeVideoInfo(videoId: string): Promise<VideoInfo> { export async function downloadVideoFromYouTube(url: string): Promise<{
try {
return (await getYT()).getInfo(videoId, {client: "ANDROID"});
} catch (e) {
logError(e);
}
}
export function isVideoExists(options: DownloadOptions): boolean {
const videoId = "videoId" in options ? options.videoId : getYouTubeVideoId(options.url);
const filePath = path.join(videoDir, `${videoId}.mp4`);
return fs.existsSync(filePath);
}
export function getVideoFromCache(videoId: string): Buffer | null {
if (!isVideoExists({videoId: videoId})) return null;
const filePath = path.join(videoDir, `${videoId}.mp4`);
return Buffer.from(fs.readFileSync(filePath));
}
export type DownloadOptions = {
url: string
} | {
videoId: string;
}
export async function downloadVideoFromYouTube(options: DownloadOptions): Promise<{
time: number, time: number,
exists?: boolean, exists?: boolean,
buffer: Buffer | null buffer: Buffer | null
@@ -65,7 +23,7 @@ export async function downloadVideoFromYouTube(options: DownloadOptions): Promis
let buffer: Buffer | null = null; let buffer: Buffer | null = null;
try { try {
const videoId = "videoId" in options ? options.videoId : getYouTubeVideoId(options.url); const videoId = getYouTubeVideoId(url);
const filePath = path.join(videoDir, `${videoId}.mp4`); const filePath = path.join(videoDir, `${videoId}.mp4`);
if (fs.existsSync(filePath)) { if (fs.existsSync(filePath)) {
const buffer = Buffer.from(fs.readFileSync(filePath)); const buffer = Buffer.from(fs.readFileSync(filePath));
@@ -84,11 +42,12 @@ export async function downloadVideoFromYouTube(options: DownloadOptions): Promis
const code = `${data.output}\nreturn { ${properties.join(", ")} }`; const code = `${data.output}\nreturn { ${properties.join(", ")} }`;
return new Function(code)(); return new Function(code)();
}; };
const yt = await Innertube.create({
const yt = await getYT(); generate_session_locally: true,
retrieve_player: true
});
const videoInfo = await yt.getInfo(videoId, {client: "ANDROID"}); const videoInfo = await yt.getInfo(videoId, {client: "ANDROID"});
console.log("Video info", videoInfo);
console.log(`Fetching metadata for: ${videoId}...`); console.log(`Fetching metadata for: ${videoId}...`);
@@ -160,7 +119,7 @@ export async function downloadVideoFromYouTube(options: DownloadOptions): Promis
const end = Date.now(); const end = Date.now();
const diff = end - start; const diff = end - start;
console.log(`Video downloaded.\ntook ${diff}ms`); console.log(`Video downloaded. URL: ${url}\ntook ${diff}ms`);
return { return {
time: diff, time: diff,