From 0ac5e9ce622b31b17c9d777dc40556a3c23c43df Mon Sep 17 00:00:00 2001 From: Vasilii Mikhailovskii Date: Sun, 15 Dec 2024 03:14:39 +0200 Subject: [PATCH] read tg_bot_updates from db --- .../dist/index.d.ts | 1 + .../dist/index.js | 0 .../dist/index.js.map | 0 .../dist/types.d.ts | 23 ++++ .../dist/types.js | 0 .../dist/types.js.map | 0 .../dist/updates/saveUpdate.d.ts | 8 +- .../dist/updates/saveUpdate.js | 4 +- .../dist/updates/saveUpdate.js.map | 2 +- .../eslint.config.mjs | 0 .../package.json | 2 +- .../src/index.ts | 1 + .../tg-bot-update-webhook-ddoc/src/types.ts | 28 +++++ .../src/updates/saveUpdate.ts | 19 +-- .../tsconfig.json | 0 packages/tg-bot-webhook-ddoc/dist/types.d.ts | 16 --- packages/tg-bot-webhook-ddoc/src/types.ts | 19 --- pnpm-lock.yaml | 6 +- .../migrations/1732483677017-dbs.js | 2 +- services/tg-bot/old/old_bot.ts | 109 ------------------ services/tg-bot/old/service.ts | 91 --------------- services/tg-bot/package.json | 2 +- .../forwardQrCodeMessageMiddleware.ts | 2 +- services/tg-bot/src/config/env.ts | 5 + services/tg-bot/src/dbs.ts | 42 +++---- services/tg-bot/src/service.ts | 30 +++-- .../src/services/serviceChangesTgUpdatesDb.ts | 55 +++++++++ .../setup => src/services}/setupWebHook.ts | 17 ++- .../services}/setupWebhookDesignDocument.ts | 19 +-- services/tg-bot/src/types.ts | 27 ----- .../tg-bot/src/types/TgBotUpdateDocument.ts | 12 ++ 31 files changed, 211 insertions(+), 331 deletions(-) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/index.d.ts (55%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/index.js (100%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/index.js.map (100%) create mode 100644 packages/tg-bot-update-webhook-ddoc/dist/types.d.ts rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/types.js (100%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/types.js.map (100%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/updates/saveUpdate.d.ts (60%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/updates/saveUpdate.js (89%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/dist/updates/saveUpdate.js.map (51%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/eslint.config.mjs (100%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/package.json (93%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/src/index.ts (55%) create mode 100644 packages/tg-bot-update-webhook-ddoc/src/types.ts rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/src/updates/saveUpdate.ts (77%) rename packages/{tg-bot-webhook-ddoc => tg-bot-update-webhook-ddoc}/tsconfig.json (100%) delete mode 100644 packages/tg-bot-webhook-ddoc/dist/types.d.ts delete mode 100644 packages/tg-bot-webhook-ddoc/src/types.ts delete mode 100644 services/tg-bot/old/old_bot.ts delete mode 100644 services/tg-bot/old/service.ts create mode 100644 services/tg-bot/src/services/serviceChangesTgUpdatesDb.ts rename services/tg-bot/{old/setup => src/services}/setupWebHook.ts (55%) rename services/tg-bot/{old/setup => src/services}/setupWebhookDesignDocument.ts (76%) delete mode 100644 services/tg-bot/src/types.ts create mode 100644 services/tg-bot/src/types/TgBotUpdateDocument.ts diff --git a/packages/tg-bot-webhook-ddoc/dist/index.d.ts b/packages/tg-bot-update-webhook-ddoc/dist/index.d.ts similarity index 55% rename from packages/tg-bot-webhook-ddoc/dist/index.d.ts rename to packages/tg-bot-update-webhook-ddoc/dist/index.d.ts index d45ed08..e7d6c63 100644 --- a/packages/tg-bot-webhook-ddoc/dist/index.d.ts +++ b/packages/tg-bot-update-webhook-ddoc/dist/index.d.ts @@ -1 +1,2 @@ export * from "./updates/saveUpdate"; +export type * from "./types"; diff --git a/packages/tg-bot-webhook-ddoc/dist/index.js b/packages/tg-bot-update-webhook-ddoc/dist/index.js similarity index 100% rename from packages/tg-bot-webhook-ddoc/dist/index.js rename to packages/tg-bot-update-webhook-ddoc/dist/index.js diff --git a/packages/tg-bot-webhook-ddoc/dist/index.js.map b/packages/tg-bot-update-webhook-ddoc/dist/index.js.map similarity index 100% rename from packages/tg-bot-webhook-ddoc/dist/index.js.map rename to packages/tg-bot-update-webhook-ddoc/dist/index.js.map diff --git a/packages/tg-bot-update-webhook-ddoc/dist/types.d.ts b/packages/tg-bot-update-webhook-ddoc/dist/types.d.ts new file mode 100644 index 0000000..c300654 --- /dev/null +++ b/packages/tg-bot-update-webhook-ddoc/dist/types.d.ts @@ -0,0 +1,23 @@ +import type { BaseDocument } from "@hereconnect/types"; +export interface TgBotUpdateWebhookDesignDocument extends BaseDocument { + _id: "_design/tg_bot_webhook"; + type: "_design/tg_bot_webhook"; + updated_at: BaseDocument["created_at"]; + updates: { + saveUpdate: string; + }; + constants: { + telegram_secret_token: string; + telegram_webhook_url?: string; + }; +} +export interface TgBotUpdateDocument extends BaseDocument { + /** + * Тип документа для идентификации (фиксированное значение "tg_bot_update") + */ + type: "tg_bot_update"; + /** + * Данные обновления Telegram + */ + data: unknown; +} diff --git a/packages/tg-bot-webhook-ddoc/dist/types.js b/packages/tg-bot-update-webhook-ddoc/dist/types.js similarity index 100% rename from packages/tg-bot-webhook-ddoc/dist/types.js rename to packages/tg-bot-update-webhook-ddoc/dist/types.js diff --git a/packages/tg-bot-webhook-ddoc/dist/types.js.map b/packages/tg-bot-update-webhook-ddoc/dist/types.js.map similarity index 100% rename from packages/tg-bot-webhook-ddoc/dist/types.js.map rename to packages/tg-bot-update-webhook-ddoc/dist/types.js.map diff --git a/packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.d.ts b/packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.d.ts similarity index 60% rename from packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.d.ts rename to packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.d.ts index b694212..beee2fb 100644 --- a/packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.d.ts +++ b/packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.d.ts @@ -1,5 +1,5 @@ -import { TelegramWebhookDesignDocument, TgUpdateDocument } from "../types"; -export type { TelegramWebhookDesignDocument }; +import { TgBotUpdateWebhookDesignDocument, TgBotUpdateDocument } from "../types"; +export type { TgBotUpdateWebhookDesignDocument }; type CouchRequest = { body: string; headers: Record; @@ -11,12 +11,12 @@ type CouchRequest = { }; }; type CouchResponse = [ - TgUpdateDocument | null, + TgBotUpdateDocument | null, { code: number; body: string; } | string ]; -type UpdateFunction = (doc: TgUpdateDocument | null, req: CouchRequest) => CouchResponse; +type UpdateFunction = (doc: TgBotUpdateDocument | null, req: CouchRequest) => CouchResponse; export declare const saveUpdate: UpdateFunction; export declare const saveUpdateString: string; diff --git a/packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.js b/packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.js similarity index 89% rename from packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.js rename to packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.js index f1f97d4..3b97b05 100644 --- a/packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.js +++ b/packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.js @@ -11,8 +11,8 @@ var saveUpdate = function (doc, req) { return [null, { code: 304, body: "Update already exists" }]; } doc = { - _id: "tg_update:" + data.update_id.toString(), - type: "tg_update", + _id: "tg_bot_update:" + data.update_id.toString(), + type: "tg_bot_update", data: data, created_at: new Date().toISOString(), }; diff --git a/packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.js.map b/packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.js.map similarity index 51% rename from packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.js.map rename to packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.js.map index 6f92ef3..54de4fe 100644 --- a/packages/tg-bot-webhook-ddoc/dist/updates/saveUpdate.js.map +++ b/packages/tg-bot-update-webhook-ddoc/dist/updates/saveUpdate.js.map @@ -1 +1 @@ -{"version":3,"file":"saveUpdate.js","sourceRoot":"","sources":["../../src/updates/saveUpdate.ts"],"names":[],"mappings":";;;AAyBO,IAAM,UAAU,GAAmB,UAExC,GAA4B,EAC5B,GAAiB;IAEjB,IAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC7D,IAAI,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE,CAAC;QACnD,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,GAAG,GAAG;QACJ,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE;QAC7C,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,IAAI;QACV,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;IAEF,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAChC,CAAC,CAAC;AAxBW,QAAA,UAAU,cAwBrB;AAEW,QAAA,gBAAgB,GAAG,kBAAU,CAAC,QAAQ,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"saveUpdate.js","sourceRoot":"","sources":["../../src/updates/saveUpdate.ts"],"names":[],"mappings":";;;AA4BO,IAAM,UAAU,GAAmB,UAExC,GAA+B,EAC/B,GAAiB;IAEjB,IAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC7D,IAAI,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE,CAAC;QACnD,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,GAAG,GAAG;QACJ,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE;QACjD,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,IAAI;QACV,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;IAEF,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAChC,CAAC,CAAC;AAxBW,QAAA,UAAU,cAwBrB;AAEW,QAAA,gBAAgB,GAAG,kBAAU,CAAC,QAAQ,EAAE,CAAC"} \ No newline at end of file diff --git a/packages/tg-bot-webhook-ddoc/eslint.config.mjs b/packages/tg-bot-update-webhook-ddoc/eslint.config.mjs similarity index 100% rename from packages/tg-bot-webhook-ddoc/eslint.config.mjs rename to packages/tg-bot-update-webhook-ddoc/eslint.config.mjs diff --git a/packages/tg-bot-webhook-ddoc/package.json b/packages/tg-bot-update-webhook-ddoc/package.json similarity index 93% rename from packages/tg-bot-webhook-ddoc/package.json rename to packages/tg-bot-update-webhook-ddoc/package.json index 39e0818..7f01ef4 100644 --- a/packages/tg-bot-webhook-ddoc/package.json +++ b/packages/tg-bot-update-webhook-ddoc/package.json @@ -1,5 +1,5 @@ { - "name": "@hereconnect/tg-bot-webhook-ddoc", + "name": "@hereconnect/tg-bot-update-webhook-ddoc", "version": "1.0.1", "license": "@hereconnect/license", "private": true, diff --git a/packages/tg-bot-webhook-ddoc/src/index.ts b/packages/tg-bot-update-webhook-ddoc/src/index.ts similarity index 55% rename from packages/tg-bot-webhook-ddoc/src/index.ts rename to packages/tg-bot-update-webhook-ddoc/src/index.ts index d45ed08..e7d6c63 100644 --- a/packages/tg-bot-webhook-ddoc/src/index.ts +++ b/packages/tg-bot-update-webhook-ddoc/src/index.ts @@ -1 +1,2 @@ export * from "./updates/saveUpdate"; +export type * from "./types"; diff --git a/packages/tg-bot-update-webhook-ddoc/src/types.ts b/packages/tg-bot-update-webhook-ddoc/src/types.ts new file mode 100644 index 0000000..445e7e0 --- /dev/null +++ b/packages/tg-bot-update-webhook-ddoc/src/types.ts @@ -0,0 +1,28 @@ +import type { BaseDocument } from "@hereconnect/types"; + +export interface TgBotUpdateWebhookDesignDocument extends BaseDocument { + _id: "_design/tg_bot_webhook"; + type: "_design/tg_bot_webhook"; + updated_at: BaseDocument["created_at"]; + + updates: { + saveUpdate: string; + }; + + constants: { + telegram_secret_token: string; + telegram_webhook_url?: string; + }; +} + +export interface TgBotUpdateDocument extends BaseDocument { + /** + * Тип документа для идентификации (фиксированное значение "tg_bot_update") + */ + type: "tg_bot_update"; + + /** + * Данные обновления Telegram + */ + data: unknown; +} diff --git a/packages/tg-bot-webhook-ddoc/src/updates/saveUpdate.ts b/packages/tg-bot-update-webhook-ddoc/src/updates/saveUpdate.ts similarity index 77% rename from packages/tg-bot-webhook-ddoc/src/updates/saveUpdate.ts rename to packages/tg-bot-update-webhook-ddoc/src/updates/saveUpdate.ts index 066de20..1ea5dff 100644 --- a/packages/tg-bot-webhook-ddoc/src/updates/saveUpdate.ts +++ b/packages/tg-bot-update-webhook-ddoc/src/updates/saveUpdate.ts @@ -1,6 +1,9 @@ -import { TelegramWebhookDesignDocument, TgUpdateDocument } from "../types"; +import { + TgBotUpdateWebhookDesignDocument, + TgBotUpdateDocument, +} from "../types"; -export type { TelegramWebhookDesignDocument }; +export type { TgBotUpdateWebhookDesignDocument }; type CouchRequest = { body: string; // JSON-строка @@ -14,18 +17,18 @@ type CouchRequest = { }; type CouchResponse = [ - TgUpdateDocument | null, + TgBotUpdateDocument | null, { code: number; body: string } | string, ]; type UpdateFunction = ( - doc: TgUpdateDocument | null, + doc: TgBotUpdateDocument | null, req: CouchRequest, ) => CouchResponse; export const saveUpdate: UpdateFunction = function ( - this: TelegramWebhookDesignDocument, - doc: TgUpdateDocument | null, + this: TgBotUpdateWebhookDesignDocument, + doc: TgBotUpdateDocument | null, req: CouchRequest, ): CouchResponse { const token = req.headers["X-Telegram-Bot-Api-Secret-Token"]; @@ -40,8 +43,8 @@ export const saveUpdate: UpdateFunction = function ( } doc = { - _id: "tg_update:" + data.update_id.toString(), - type: "tg_update", + _id: "tg_bot_update:" + data.update_id.toString(), + type: "tg_bot_update", data: data, created_at: new Date().toISOString(), }; diff --git a/packages/tg-bot-webhook-ddoc/tsconfig.json b/packages/tg-bot-update-webhook-ddoc/tsconfig.json similarity index 100% rename from packages/tg-bot-webhook-ddoc/tsconfig.json rename to packages/tg-bot-update-webhook-ddoc/tsconfig.json diff --git a/packages/tg-bot-webhook-ddoc/dist/types.d.ts b/packages/tg-bot-webhook-ddoc/dist/types.d.ts deleted file mode 100644 index c948f6a..0000000 --- a/packages/tg-bot-webhook-ddoc/dist/types.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { BaseDocument } from "@hereconnect/types"; -export interface TelegramWebhookDesignDocument extends BaseDocument { - _id: "_design/webhook"; - type: "_design/webhook"; - updated_at: BaseDocument["created_at"]; - updates: { - saveUpdate: string; - }; - constants: { - telegram_secret_token: string; - telegram_webhook_url?: string; - }; -} -export interface TgUpdateDocument extends BaseDocument { - data: unknown; -} diff --git a/packages/tg-bot-webhook-ddoc/src/types.ts b/packages/tg-bot-webhook-ddoc/src/types.ts deleted file mode 100644 index 3e96002..0000000 --- a/packages/tg-bot-webhook-ddoc/src/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { BaseDocument } from "@hereconnect/types"; - -export interface TelegramWebhookDesignDocument extends BaseDocument { - _id: "_design/webhook"; - type: "_design/webhook"; - updated_at: BaseDocument["created_at"]; - - updates: { - saveUpdate: string; - }; - constants: { - telegram_secret_token: string; - telegram_webhook_url?: string; - }; -} - -export interface TgUpdateDocument extends BaseDocument { - data: unknown; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 353b62b..b4c508d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,7 +238,7 @@ importers: specifier: ~5.7.2 version: 5.7.2 - packages/tg-bot-webhook-ddoc: + packages/tg-bot-update-webhook-ddoc: devDependencies: '@hereconnect/types': specifier: workspace:^ @@ -349,9 +349,9 @@ importers: '@hereconnect/lib-service-seq-tracker': specifier: workspace:^ version: link:../../packages/lib-service-seq-tracker - '@hereconnect/tg-bot-webhook-ddoc': + '@hereconnect/tg-bot-update-webhook-ddoc': specifier: workspace:^ - version: link:../../packages/tg-bot-webhook-ddoc + version: link:../../packages/tg-bot-update-webhook-ddoc '@xmldom/xmldom': specifier: ^0.9.6 version: 0.9.6 diff --git a/services/db-migrations/migrations/1732483677017-dbs.js b/services/db-migrations/migrations/1732483677017-dbs.js index d9ccc9e..1842d5f 100644 --- a/services/db-migrations/migrations/1732483677017-dbs.js +++ b/services/db-migrations/migrations/1732483677017-dbs.js @@ -33,7 +33,7 @@ const dbsSecurity = { admins: { roles: ["_admin"] }, members: { roles: [] }, }, - tg_bot: { + tg_bot_updates: { admins: { roles: ["_admin"] }, members: { roles: ["_admin"] }, }, diff --git a/services/tg-bot/old/old_bot.ts b/services/tg-bot/old/old_bot.ts deleted file mode 100644 index b9462a7..0000000 --- a/services/tg-bot/old/old_bot.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Bot, Context, InlineKeyboard } from "grammy"; -import { type AbortSignal } from "grammy/out/shim.node"; -import { qrCodePlacements } from "@hereconnect/constants"; -import type { QRCodeDocument } from "@hereconnect/types"; - -const BOT_TOKEN = process.env.BOT_TOKEN; -if (!BOT_TOKEN) { - throw new Error("Необходимо указать BOT_TOKEN в переменных окружения."); -} - -export const bot = new Bot(BOT_TOKEN); - -export const init = async (signal: AbortSignal) => { - await bot.init(signal); - - // Настройка команд для английского языка (по умолчанию) - await bot.api.setMyCommands([ - { command: "create_qr", description: "Create a QR code" }, - { command: "help", description: "Get help" }, - ]); - - // Настройка команд для русского языка - await bot.api.setMyCommands( - [ - { command: "create_qr", description: "Создать QR-код" }, - { command: "help", description: "Получить помощь" }, - ], - { language_code: "ru" }, - ); -}; - -const actionsOptions = [ - { - type: "sendMessage", - label: "Отправить сообщение", - }, - { type: "viewContactInfo", label: "Посмотреть ваши контактные данные" }, -]; - -bot.command("start", async (ctx) => { - const user = ctx.from; - - const message = `Привет${user?.first_name ? ", " + user?.first_name : ""}!\n\nЯ бот сервиса "НаСвязи". Помогу вам создать QR-код (для автомобиля, питомца, входной двери и т.д.).!`; - - // Отправляем сообщение - return ctx.reply(message, { - reply_markup: { - inline_keyboard: [ - [ - { - text: "Создать QR-код", - callback_data: "create_qr", - }, - ], - ], - }, - }); -}); - -const createQrCode = async (ctx: Context) => { - const keyboard = new InlineKeyboard(); - for (const { type, label } of qrCodePlacements) { - keyboard.text(label, `select_placement=${type}`); - } - - await ctx.reply( - "Где вы хотите разместить QR‑код?\n\nЭтот выбор поможет настроить QR‑код с подходящими действиями.", - { reply_markup: { inline_keyboard: keyboard.toFlowed(1).inline_keyboard } }, - ); -}; - -bot.command("create_qr", createQrCode); - -bot.callbackQuery("cancel", async (ctx) => { - await ctx.deleteMessage(); -}); - -bot.callbackQuery( - /^(select_action=|select_placement=|create_qr$)/, - async (ctx) => { - const selectedActions: QRCodeDocument["actions"] = []; - const keyboard = new InlineKeyboard(); - for (const { type, label } of actionsOptions) { - keyboard.text( - (selectedActions.includes(type) ? "✓ " : "") + label, - `select_action=${type}`, - ); - } - - await ctx.reply( - "Выберите доступные действия?\n\nУкажите, какие действия будут доступны для сканирующих QR‑код.\nВы сможете изменить эти настройки позже.", - { reply_markup: { inline_keyboard: keyboard.inline_keyboard } }, - ); - }, -); - -if (process.argv.includes("start")) { - const abortController = new AbortController(); - - process.on("SIGINT", async () => { - abortController.abort(); - setTimeout(() => process.exit(1), 1_000).unref(); - }); - - init(abortController.signal as AbortSignal) - .then(() => bot.start()) - .then(() => console.log("Bot started")) - .catch((error) => console.error(error)); -} diff --git a/services/tg-bot/old/service.ts b/services/tg-bot/old/service.ts deleted file mode 100644 index 76cd73d..0000000 --- a/services/tg-bot/old/service.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { startHealthServer } from "@hereconnect/lib-service-health-checkserver"; -import { ServiceSeqTracker } from "@hereconnect/lib-service-seq-tracker"; -import { type AbortSignal } from "grammy/out/shim.node"; -import { setupWebHook } from "setup/setupWebHook"; - -const heartbeat = Number(process.env.HEARTBEAT_INTERVAL ?? 30_000); - -import { CouchDBChangesStream } from "@hereconnect/couchdb-changes-stream"; -import { TG_BOT_DB_URL, serviceSeqTrackerDb, checkDatabases } from "dbs"; -import type { TgUpdateDocument } from "types"; -import { init, bot } from "old_bot"; - -const abortController = new AbortController(); - -const start = async () => { - const checkedDatabases = await checkDatabases(); - - await init(abortController.signal as AbortSignal); - await setupWebHook(); - - const serviceSeqTracker = await ServiceSeqTracker.Init( - "_local/service:tg-bot", - serviceSeqTrackerDb, - ); - - const changesStream = new CouchDBChangesStream( - TG_BOT_DB_URL, - { - abortController, - since: serviceSeqTracker.lastSeq, - feed: "continuous", - include_docs: true, - heartbeat, - filter: "_selector", - selector: { - type: "tg_update" satisfies TgUpdateDocument["type"], - }, - }, - ); - - // Gracefully handle SIGINT - process.on("SIGINT", async () => { - changesStream?.stop(); - setTimeout(() => process.exit(1), 5_000).unref(); - }); - - console.info("Service started from sequence", serviceSeqTracker.lastSeq); - if ( - parseInt(serviceSeqTracker.lastSeq.toString(), 10) === - parseInt(checkedDatabases.tgBotDb.update_seq.toString(), 10) - ) { - console.log("Since is the last seq"); - } else { - console.log("Last seq is", checkedDatabases.tgBotDb.update_seq); - } - - await startHealthServer(abortController); - - for await (const change of changesStream) { - const { seq, doc: changeDoc } = change; - - if (!changeDoc) { - console.error("Change missing document:", change); - continue; - } - - serviceSeqTracker.checkNextSeq(seq); - await bot.handleUpdate(changeDoc.data); - - // const ctx = new Context(changeDoc.data, bot.api, bot.me); - // if (ctx.hasCommand("start")) { - // } - - try { - console.log( - `Processed change: ${JSON.stringify({ _id: change.id, ref: changeDoc._rev, seq: change.seq })}`, - ); - } catch (error) { - console.error( - `Failed processing: ${JSON.stringify(error)} change: ${JSON.stringify({ id: changeDoc._id, rev: changeDoc._rev, seq: seq })}`, - ); - } finally { - await serviceSeqTracker.saveSeq(seq); - } - } -}; - -start().catch((error) => { - console.error("error", error); - process.exit(1); -}); diff --git a/services/tg-bot/package.json b/services/tg-bot/package.json index 2b9d352..1ac0d70 100644 --- a/services/tg-bot/package.json +++ b/services/tg-bot/package.json @@ -45,7 +45,7 @@ "@hereconnect/couchdb-changes-stream": "workspace:^", "@hereconnect/lib-service-health-checkserver": "workspace:^", "@hereconnect/lib-service-seq-tracker": "workspace:^", - "@hereconnect/tg-bot-webhook-ddoc": "workspace:^", + "@hereconnect/tg-bot-update-webhook-ddoc": "workspace:^", "@xmldom/xmldom": "^0.9.6", "canvas": "^2.11.2", "extensionless": "^1.9.9", diff --git a/services/tg-bot/src/bot/middleware/forwardQrCodeMessageMiddleware.ts b/services/tg-bot/src/bot/middleware/forwardQrCodeMessageMiddleware.ts index fd60d87..97943a4 100644 --- a/services/tg-bot/src/bot/middleware/forwardQrCodeMessageMiddleware.ts +++ b/services/tg-bot/src/bot/middleware/forwardQrCodeMessageMiddleware.ts @@ -91,7 +91,7 @@ export const forwardQrCodeMessageMiddleware = async ( const hashTag = `#qr${qr_code_uri}_c${chat}`; const manageMessageUrl = `${env.APP_BASE_URL}/manage/qr/${qr_code_uri}/chat/${chat}#message-${encodeURIComponent(created_at)}`; - const text = `сообщение диалог QR-кода «${escapeHtml(qrCodeDoc.name)}» ${escapeHtml(hashTag)}`; + const text = `сообщение отправлено в диалог QR-кода «${escapeHtml(qrCodeDoc.name)}» ${escapeHtml(hashTag)}`; // await ctx.react("⚡"); await ctx.reply(text, { diff --git a/services/tg-bot/src/config/env.ts b/services/tg-bot/src/config/env.ts index b596f8f..d473b4b 100644 --- a/services/tg-bot/src/config/env.ts +++ b/services/tg-bot/src/config/env.ts @@ -14,6 +14,10 @@ if (!process.env.API_URL) { throw new Error("Environment variable API_URL is not set"); } +if (!process.env.PUBLIC_API_URL) { + throw new Error("Environment variable PUBLIC_API_URL is not set"); +} + if (!process.env.HEARTBEAT_INTERVAL) { throw new Error("Environment variable HEARTBEAT_INTERVAL is not set"); } @@ -23,5 +27,6 @@ export const env = { READ_BASE_URL: process.env.READ_BASE_URL, APP_BASE_URL: process.env.APP_BASE_URL, API_URL: process.env.API_URL, + PUBLIC_API_URL: process.env.PUBLIC_API_URL, HEARTBEAT_INTERVAL: parseInt(process.env.HEARTBEAT_INTERVAL, 10), } as const; diff --git a/services/tg-bot/src/dbs.ts b/services/tg-bot/src/dbs.ts index d829050..846bb1b 100644 --- a/services/tg-bot/src/dbs.ts +++ b/services/tg-bot/src/dbs.ts @@ -11,55 +11,55 @@ import type { import type { ServiceSeqTrackerDocument } from "@hereconnect/lib-service-seq-tracker"; -import type { TelegramWebhookDesignDocument } from "@hereconnect/tg-bot-webhook-ddoc"; +import type { TgBotUpdateWebhookDesignDocument } from "@hereconnect/tg-bot-update-webhook-ddoc"; import { CouchDBSessionDocument } from "./bot/storageAdapters/CouchDBStorageAdapter"; import type { SessionData } from "./types/myContext"; +import { TgBotUpdateDocument } from "./types/TgBotUpdateDocument"; import { env } from "./config/env"; if (!env.API_URL) { throw new Error("Environment variable API_URL is not set"); } -export const TG_BOT_DB_NAME = "tg_bot"; +export const TG_BOT_UPDATES_DB_NAME = "tg_bot_updates"; export const MESSAGES_DB_NAME = "messages"; -export const TG_BOT_DB_URL = `${env.API_URL}/${TG_BOT_DB_NAME}`; +export const TG_BOT_UPDATES_DB_URL = `${env.API_URL}/${TG_BOT_UPDATES_DB_NAME}`; export const MESSAGES_DB_URL = `${env.API_URL}/${MESSAGES_DB_NAME}`; export const messagesDb = new PouchDB(MESSAGES_DB_URL, { skip_setup: true, }); export const messagesServiceSeqTrackerDb = - messagesDb satisfies PouchDB.Database as unknown as PouchDB.Database; + messagesDb satisfies PouchDB.Database as PouchDB.Database as PouchDB.Database; export const publicSettingsDb = new PouchDB( `${env.API_URL}/public_settings`, - { - skip_setup: true, - }, + { skip_setup: true }, ); export const qrDb = new PouchDB(`${env.API_URL}/qr`, { skip_setup: true, }); -export const tgBotDb = new PouchDB(TG_BOT_DB_URL, { - skip_setup: true, -}); - -export const serviceSeqTrackerDb = tgBotDb as ServiceSeqTrackerDatabase; - export const tgBotSessionsDb = new PouchDB>( `${env.API_URL}/tg_bot_sessions`, - { - skip_setup: true, - }, + { skip_setup: true }, ); +export const tgBotUpdatesDb = new PouchDB( + `${TG_BOT_UPDATES_DB_URL}`, + { skip_setup: true }, +); + +export const tgBotUpdatesDesignDocumentsDb = + tgBotUpdatesDb satisfies PouchDB.Database as PouchDB.Database as PouchDB.Database; + +export const tgBotUpdatesServiceSeqTrackerDb = + tgBotUpdatesDb satisfies PouchDB.Database as PouchDB.Database as ServiceSeqTrackerDatabase; + export const userChatSettingsDb = new PouchDB( `${env.API_URL}/user_chat_settings`, - { - skip_setup: true, - }, + { skip_setup: true }, ); export const usersDb = new PouchDB(`${env.API_URL}/_users`, { @@ -71,11 +71,11 @@ const checkDatabases = async () => { publicSettingsDb, qrDb, usersDb, - serviceSeqTrackerDb, userChatSettingsDb, - tgBotDb, + tgBotUpdatesDb, tgBotSessionsDb, messagesServiceSeqTrackerDb, + messagesDb, } as const; const databasesEntries = Object.entries(databases); diff --git a/services/tg-bot/src/service.ts b/services/tg-bot/src/service.ts index 6f33c26..b83c753 100644 --- a/services/tg-bot/src/service.ts +++ b/services/tg-bot/src/service.ts @@ -1,31 +1,45 @@ import { createHealthServer } from "@hereconnect/lib-service-health-checkserver"; import { createBot, setupCommands } from "./bot"; import { createServiceChangesMessagesDb } from "./services/serviceChangesMessagesDb"; +import { createServiceChangesTgUpdatesDb } from "./services/serviceChangesTgUpdatesDb"; import { iterateMessagesChanges } from "./services/iterateMessagesChanges"; +import { setupWebHook } from "./services/setupWebHook"; const abortController = new AbortController(); (async () => { const healthServer = createHealthServer(abortController); const bot = createBot({ abortController }); - const { changesStream, serviceSeqTracker } = - await createServiceChangesMessagesDb({ - abortController, - }); + const messagesChanges = await createServiceChangesMessagesDb({ + abortController, + }); + const tgUpdatesChanges = await createServiceChangesTgUpdatesDb({ + abortController, + }); process.on("SIGINT", async () => { - changesStream.stop(); + messagesChanges.changesStream.stop(); + tgUpdatesChanges.changesStream.stop(); bot.stop(); }); await healthServer.start(); await bot.init(); - // Устанавливаем команд + + await setupWebHook(bot); await setupCommands(bot); - bot.start(); + // bot.start(); console.log("Бот запущен!"); - iterateMessagesChanges({ changesStream, serviceSeqTracker, bot }); + iterateMessagesChanges({ bot, ...messagesChanges }); + + for await (const change of tgUpdatesChanges.changesStream) { + tgUpdatesChanges.serviceSeqTracker.checkNextSeq(change.seq); + if (change.doc) { + await bot.handleUpdate(change.doc.data); + } + await tgUpdatesChanges.serviceSeqTracker.saveSeq(change.seq); + } })(); diff --git a/services/tg-bot/src/services/serviceChangesTgUpdatesDb.ts b/services/tg-bot/src/services/serviceChangesTgUpdatesDb.ts new file mode 100644 index 0000000..5df4543 --- /dev/null +++ b/services/tg-bot/src/services/serviceChangesTgUpdatesDb.ts @@ -0,0 +1,55 @@ +import { ServiceSeqTracker } from "@hereconnect/lib-service-seq-tracker"; +import { CouchDBChangesStream } from "@hereconnect/couchdb-changes-stream"; +import type { TgBotUpdateDocument } from "../types/TgBotUpdateDocument"; +import { + TG_BOT_UPDATES_DB_URL, + tgBotUpdatesServiceSeqTrackerDb, + checkDatabasesPromise, +} from "../dbs"; +import { env } from "../config/env"; + +export const createServiceChangesTgUpdatesDb = async ({ + abortController, +}: { + abortController: AbortController; +}) => { + const { + tgBotUpdatesDb: { update_seq: lastSeq }, + } = await checkDatabasesPromise; + + const serviceSeqTracker = await ServiceSeqTracker.Init( + "_local/service:tg-bot", + tgBotUpdatesServiceSeqTrackerDb, + ); + + const changesStream = new CouchDBChangesStream( + TG_BOT_UPDATES_DB_URL, + { + abortController, + since: serviceSeqTracker.lastSeq, + feed: "continuous", + include_docs: true, + heartbeat: env.HEARTBEAT_INTERVAL, + filter: "_selector", + selector: { + type: "tg_bot_update", + } satisfies Partial, + }, + ); + + console.info( + "Service started listening changes tg_bot_update from sequence", + serviceSeqTracker.lastSeq, + ); + + if ( + parseInt(serviceSeqTracker.lastSeq.toString(), 10) === + parseInt(lastSeq.toString(), 10) + ) { + console.log("Since is the last seq"); + } else { + console.log("Last seq is", lastSeq); + } + + return { changesStream, serviceSeqTracker }; +}; diff --git a/services/tg-bot/old/setup/setupWebHook.ts b/services/tg-bot/src/services/setupWebHook.ts similarity index 55% rename from services/tg-bot/old/setup/setupWebHook.ts rename to services/tg-bot/src/services/setupWebHook.ts index ab730fe..b82cc1c 100644 --- a/services/tg-bot/old/setup/setupWebHook.ts +++ b/services/tg-bot/src/services/setupWebHook.ts @@ -1,22 +1,19 @@ import { setupWebhookDesignDocument } from "./setupWebhookDesignDocument"; -import { TG_BOT_DB_NAME, tgBotDesignDocumentsDb } from "dbs"; -import { bot } from "old_bot"; +import { TG_BOT_UPDATES_DB_NAME, tgBotUpdatesDesignDocumentsDb } from "../dbs"; +import { env } from "../config/env"; +import { Bot } from "grammy"; +import { MyContext } from "../types/myContext"; -const PUBLIC_API_URL = process.env.PUBLIC_API_URL; -if (!PUBLIC_API_URL) { - throw new Error("Необходимо указать PUBLIC_API_URL в переменных окружения."); -} - -export const setupWebHook = async () => { +export const setupWebHook = async (bot: Bot) => { const designDocument = await setupWebhookDesignDocument(); const update: keyof typeof designDocument.updates = "saveUpdate"; - const webhookUrl = `${process.env.PUBLIC_API_URL}/${TG_BOT_DB_NAME}/${designDocument._id}/_update/${update}`; + const webhookUrl = `${env.PUBLIC_API_URL}/${TG_BOT_UPDATES_DB_NAME}/${designDocument._id}/_update/${update}`; if (designDocument.constants.telegram_webhook_url !== webhookUrl) { await bot.api.setWebhook(webhookUrl, { secret_token: designDocument.constants.telegram_secret_token, }); designDocument.constants.telegram_webhook_url = webhookUrl; - const { rev } = await tgBotDesignDocumentsDb.put(designDocument); + const { rev } = await tgBotUpdatesDesignDocumentsDb.put(designDocument); designDocument._rev = rev; } return designDocument; diff --git a/services/tg-bot/old/setup/setupWebhookDesignDocument.ts b/services/tg-bot/src/services/setupWebhookDesignDocument.ts similarity index 76% rename from services/tg-bot/old/setup/setupWebhookDesignDocument.ts rename to services/tg-bot/src/services/setupWebhookDesignDocument.ts index 45b2b9f..cd621d0 100644 --- a/services/tg-bot/old/setup/setupWebhookDesignDocument.ts +++ b/services/tg-bot/src/services/setupWebhookDesignDocument.ts @@ -1,6 +1,6 @@ -import { tgBotDesignDocumentsDb } from "../dbs"; -import { saveUpdateString as saveUpdate } from "@hereconnect/tg-bot-webhook-ddoc"; -import { type TelegramWebhookDesignDocument } from "@hereconnect/tg-bot-webhook-ddoc"; +import { tgBotUpdatesDesignDocumentsDb } from "../dbs"; +import { saveUpdateString as saveUpdate } from "@hereconnect/tg-bot-update-webhook-ddoc"; +import { type TgBotUpdateWebhookDesignDocument } from "@hereconnect/tg-bot-update-webhook-ddoc"; function generateTelegramSecretToken(length = 256) { const characters = @@ -14,11 +14,11 @@ function generateTelegramSecretToken(length = 256) { } export const setupWebhookDesignDocument = async () => { - const _id = "_design/webhook"; + const _id = "_design/tg_bot_webhook"; - const result: TelegramWebhookDesignDocument = { + const result: TgBotUpdateWebhookDesignDocument = { _id, - type: "_design/webhook", + type: _id, created_at: "", updated_at: "", updates: { @@ -29,7 +29,7 @@ export const setupWebhookDesignDocument = async () => { }, }; - const { rev } = await tgBotDesignDocumentsDb.upsert(_id, (ddoc) => { + const { rev } = await tgBotUpdatesDesignDocumentsDb.upsert(_id, (ddoc) => { const isEmptyTelegramSecretToken = !ddoc.constants?.telegram_secret_token?.length; const needToSetSaveUpdateFunction = @@ -60,7 +60,10 @@ export const setupWebhookDesignDocument = async () => { } } - return Object.assign(result, ddoc) satisfies TelegramWebhookDesignDocument; + return Object.assign( + result, + ddoc, + ) satisfies TgBotUpdateWebhookDesignDocument; }); result._rev = rev; diff --git a/services/tg-bot/src/types.ts b/services/tg-bot/src/types.ts deleted file mode 100644 index d6522c9..0000000 --- a/services/tg-bot/src/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { BaseDocument } from "@hereconnect/types"; -import type { Update } from "@grammyjs/types"; - -/** - * Интерфейс для документа CouchDB, представляющего обновление Telegram - */ -export interface TgUpdateDocument extends BaseDocument { - /** - * Уникальный идентификатор документа, например, "tg_update:" - */ - _id: string; - - /** - * Ревизия документа (если документ уже существует, автоматически добавляется CouchDB) - */ - _rev?: string; - - /** - * Тип документа для идентификации (фиксированное значение "tg_update") - */ - type: "tg_update"; - - /** - * Данные обновления Telegram - */ - data: Update; -} diff --git a/services/tg-bot/src/types/TgBotUpdateDocument.ts b/services/tg-bot/src/types/TgBotUpdateDocument.ts new file mode 100644 index 0000000..1daf1ed --- /dev/null +++ b/services/tg-bot/src/types/TgBotUpdateDocument.ts @@ -0,0 +1,12 @@ +import type { Update } from "@grammyjs/types"; +import type { TgBotUpdateDocument as BaseTgBotUpdateDocument } from "@hereconnect/tg-bot-update-webhook-ddoc"; + +/** + * Интерфейс для документа CouchDB, представляющего обновление Telegram + */ +export interface TgBotUpdateDocument extends BaseTgBotUpdateDocument { + /** + * Данные обновления Telegram + */ + data: Update; +}