From 4b7ce58fe90f61720c3c1e817d8ff46b666414b0 Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 7 Aug 2024 17:22:43 +0400 Subject: [PATCH] Add telegram bot fixes (#6281) --- dev/docker-compose.yaml | 16 +++++ pods/front/src/__start.ts | 3 +- server-plugins/chunter-resources/src/index.ts | 1 - .../notification-resources/src/index.ts | 18 +++--- .../notification-resources/src/utils.ts | 32 ++++------ server-plugins/notification/package.json | 1 + server-plugins/notification/src/index.ts | 6 +- .../telegram-resources/src/index.ts | 59 +++++++++++++------ .../telegram-bot/pod-telegram-bot/src/bot.ts | 46 +++++++-------- .../pod-telegram-bot/src/config.ts | 2 - .../pod-telegram-bot/src/start.ts | 13 ++-- 11 files changed, 115 insertions(+), 82 deletions(-) diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index a7c7dd8abc..0446d5e8ba 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -231,6 +231,22 @@ services: resources: limits: memory: 300M +# telegram-bot: +# image: hardcoreeng/telegram-bot +# restart: unless-stopped +# environment: +# - PORT=4020 +# - BOT_TOKEN=token +# - MONGO_URL=mongodb://mongodb:27017 +# - MONGO_DB=telegram-bot +# - SECRET=secret +# - DOMAIN=domain +# - ACCOUNTS_URL=http://account:3000 +# - SERVICE_ID=telegram-bot-service +# deploy: +# resources: +# limits: +# memory: 300M volumes: db: files: diff --git a/pods/front/src/__start.ts b/pods/front/src/__start.ts index 1a8662e455..c552d69281 100644 --- a/pods/front/src/__start.ts +++ b/pods/front/src/__start.ts @@ -38,5 +38,6 @@ startFront(metricsContext, { POSTHOG_HOST: process.env.POSTHOG_HOST, DESKTOP_UPDATES_URL: process.env.DESKTOP_UPDATES_URL, DESKTOP_UPDATES_CHANNEL: process.env.DESKTOP_UPDATES_CHANNEL, - ANALYTICS_COLLECTOR_URL: process.env.ANALYTICS_COLLECTOR_URL + ANALYTICS_COLLECTOR_URL: process.env.ANALYTICS_COLLECTOR_URL, + TELEGRAM_BOT_URL: process.env.TELEGRAM_BOT_URL }) diff --git a/server-plugins/chunter-resources/src/index.ts b/server-plugins/chunter-resources/src/index.ts index 3d5434a826..0b1186a6d5 100644 --- a/server-plugins/chunter-resources/src/index.ts +++ b/server-plugins/chunter-resources/src/index.ts @@ -343,7 +343,6 @@ export async function getChunterNotificationContent ( return { title, body, - data: message, intlParams, intlParamsNotLocalized } diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index eb87ef3110..a881e5bd1c 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -75,7 +75,7 @@ import serverNotification, { getPersonAccount, getPersonAccountById, NOTIFICATION_BODY_SIZE, - NOTIFICATION_TITLE_SIZE, + PUSH_NOTIFICATION_TITLE_SIZE, ReceiverInfo, SenderInfo } from '@hcengineering/server-notification' @@ -373,7 +373,7 @@ async function activityInboxNotificationToText ( body = await translate(doc.body, params) } - return { ...params, title: title.substring(0, NOTIFICATION_TITLE_SIZE), body } + return { ...params, title, body } } async function commonInboxNotificationToText ( @@ -461,12 +461,14 @@ export async function createPushFromInbox ( _id: Ref, cache: Map, Doc> = new Map, Doc>() ): Promise { - const { title, body } = await getTranslatedNotificationContent(data, _class, control) + let { title, body } = await getTranslatedNotificationContent(data, _class, control) if (title === '' || body === '') { return } + title = title.slice(0, PUSH_NOTIFICATION_TITLE_SIZE) + const senderPerson = sender.person const linkProviders = control.modelDb.findAllSync(serverView.mixin.ServerLinkIdProvider, {}) const provider = linkProviders.find(({ _id }) => _id === attachedToClass) @@ -575,7 +577,7 @@ export async function pushActivityInboxNotifications ( activityMessage: ActivityMessage, shouldUpdateTimestamp: boolean ): Promise | undefined> { - const content = await getNotificationContent(originTx, receiver.account, sender, object, control, activityMessage) + const content = await getNotificationContent(originTx, receiver.account, sender, object, control) const data: Partial> = { ...content, attachedTo: activityMessage._id, @@ -606,7 +608,8 @@ export async function applyNotificationProviders ( res: Tx[], object: Doc, receiver: ReceiverInfo, - sender: SenderInfo + sender: SenderInfo, + message?: ActivityMessage ): Promise { const resources = await control.modelDb.findAll(serverNotification.class.NotificationProviderResources, {}) for (const [provider, types] of notifyResult.entries()) { @@ -635,7 +638,7 @@ export async function applyNotificationProviders ( const fn = await getResource(resource.fn) - const txes = await fn(control, types, object, data, receiver, sender) + const txes = await fn(control, types, object, data, receiver, sender, message) if (txes.length > 0) { res.push(...txes) } @@ -724,7 +727,8 @@ export async function getNotificationTxes ( res, object, receiver, - sender + sender, + message ) } } else { diff --git a/server-plugins/notification-resources/src/utils.ts b/server-plugins/notification-resources/src/utils.ts index a973aac609..dd21abf77e 100644 --- a/server-plugins/notification-resources/src/utils.ts +++ b/server-plugins/notification-resources/src/utils.ts @@ -41,7 +41,8 @@ import core, { TxProcessor, TxRemoveDoc, TxUpdateDoc, - type MeasureContext + type MeasureContext, + Markup } from '@hcengineering/core' import { getResource, IntlString, translate } from '@hcengineering/platform' import serverNotification, { @@ -52,7 +53,7 @@ import serverNotification, { SenderInfo, TextPresenter } from '@hcengineering/server-notification' -import { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity' +import { DocUpdateMessage } from '@hcengineering/activity' import { NotifyResult } from './types' @@ -347,12 +348,10 @@ async function getFallbackNotificationFullfillment ( object: Doc, originTx: TxCUD, control: TriggerControl, - sender: SenderInfo, - message?: ActivityMessage + sender: SenderInfo ): Promise { const title: IntlString = notification.string.CommonNotificationTitle let body: IntlString = notification.string.CommonNotificationBody - let data: string | undefined const intlParams: Record = {} const intlParamsNotLocalized: Record = {} @@ -363,15 +362,6 @@ async function getFallbackNotificationFullfillment ( intlParams.title = await textPresenterFunc(object, control) } - if (message !== undefined) { - const dataPresenter = getTextPresenter(message._class, control.hierarchy) - - if (dataPresenter !== undefined) { - const textPresenterFunc = await getResource(dataPresenter.presenter) - data = await textPresenterFunc(message, control) - } - } - const tx = TxProcessor.extractTx(originTx) intlParams.senderName = await getSenderName(control, sender) @@ -416,7 +406,7 @@ async function getFallbackNotificationFullfillment ( } } - return { title, body, data, intlParams, intlParamsNotLocalized } + return { title, body, intlParams, intlParamsNotLocalized } } function getNotificationPresenter (_class: Ref>, hierarchy: Hierarchy): NotificationPresenter | undefined { @@ -428,17 +418,17 @@ export async function getNotificationContent ( targetUser: PersonAccount, sender: SenderInfo, object: Doc, - control: TriggerControl, - message?: ActivityMessage + control: TriggerControl ): Promise { - let { title, body, data, intlParams, intlParamsNotLocalized } = await getFallbackNotificationFullfillment( + let { title, body, intlParams, intlParamsNotLocalized } = await getFallbackNotificationFullfillment( object, originTx, control, - sender, - message + sender ) + let data: Markup | undefined + const actualTx = TxProcessor.extractTx(originTx) as TxCUD const notificationPresenter = getNotificationPresenter(actualTx.objectClass, control.hierarchy) @@ -447,7 +437,7 @@ export async function getNotificationContent ( const updateParams = await getFuillfillmentParams(object, originTx, targetUser._id, control) title = updateParams.title body = updateParams.body - data = updateParams?.data ?? data + data = updateParams.data intlParams = { ...intlParams, ...updateParams.intlParams diff --git a/server-plugins/notification/package.json b/server-plugins/notification/package.json index 645b739363..ee57d961af 100644 --- a/server-plugins/notification/package.json +++ b/server-plugins/notification/package.json @@ -39,6 +39,7 @@ }, "dependencies": { "@hcengineering/core": "^0.6.32", + "@hcengineering/activity": "^0.6.0", "@hcengineering/platform": "^0.6.11", "@hcengineering/notification": "^0.6.23", "@hcengineering/server-core": "^0.6.1", diff --git a/server-plugins/notification/src/index.ts b/server-plugins/notification/src/index.ts index 9101638965..0d252646df 100644 --- a/server-plugins/notification/src/index.ts +++ b/server-plugins/notification/src/index.ts @@ -25,6 +25,7 @@ import { } from '@hcengineering/notification' import { Metadata, Plugin, Resource, plugin } from '@hcengineering/platform' import type { TriggerControl, TriggerFunc } from '@hcengineering/server-core' +import { ActivityMessage } from '@hcengineering/activity' /** * @public @@ -154,7 +155,8 @@ export type NotificationProviderFunc = ( object: Doc, data: InboxNotification, receiver: ReceiverInfo, - sender: SenderInfo + sender: SenderInfo, + message?: ActivityMessage ) => Promise export interface NotificationProviderResources extends Doc { @@ -163,7 +165,7 @@ export interface NotificationProviderResources extends Doc { } export const NOTIFICATION_BODY_SIZE = 50 -export const NOTIFICATION_TITLE_SIZE = 50 +export const PUSH_NOTIFICATION_TITLE_SIZE = 80 /** * @public diff --git a/server-plugins/telegram-resources/src/index.ts b/server-plugins/telegram-resources/src/index.ts index c39226baac..ea3fe59be2 100644 --- a/server-plugins/telegram-resources/src/index.ts +++ b/server-plugins/telegram-resources/src/index.ts @@ -40,7 +40,7 @@ import { getTranslatedNotificationContent, getTextPresenter } from '@hcengineeri import { generateToken } from '@hcengineering/server-token' import chunter, { ChatMessage } from '@hcengineering/chunter' import { markupToHTML } from '@hcengineering/text' -import activity from '@hcengineering/activity' +import activity, { ActivityMessage } from '@hcengineering/activity' /** * @public @@ -142,10 +142,31 @@ async function getContactChannel ( return res?.value ?? '' } +async function activityMessageToHtml (control: TriggerControl, message: ActivityMessage): Promise { + const { hierarchy } = control + if (hierarchy.isDerived(message._class, chunter.class.ChatMessage)) { + const chatMessage = message as ChatMessage + return markupToHTML(chatMessage.message) + } else { + const resource = getTextPresenter(message._class, control.hierarchy) + + if (resource !== undefined) { + const fn = await getResource(resource.presenter) + const textData = await fn(message, control) + if (textData !== undefined && textData !== '') { + return markupToHTML(textData) + } + } + } + + return undefined +} + async function getTranslatedData ( data: InboxNotification, doc: Doc, - control: TriggerControl + control: TriggerControl, + message?: ActivityMessage ): Promise<{ title: string quote: string | undefined @@ -156,23 +177,22 @@ async function getTranslatedData ( let { title, body } = await getTranslatedNotificationContent(data, data._class, control) let quote: string | undefined - if (hierarchy.isDerived(doc._class, chunter.class.ChatMessage)) { - const chatMessage = doc as ChatMessage - title = '' - quote = markupToHTML(chatMessage.message) - } else if (hierarchy.isDerived(doc._class, activity.class.ActivityMessage)) { - const resource = getTextPresenter(doc._class, control.hierarchy) - - if (resource !== undefined) { - const fn = await getResource(resource.presenter) - const textData = await fn(doc, control) - if (textData !== undefined && textData !== '') { - title = '' - quote = markupToHTML(textData) - } + if (data.data !== undefined) { + body = markupToHTML(data.data) + } else if (message !== undefined) { + const html = await activityMessageToHtml(control, message) + if (html !== undefined) { + body = html + } + } + + if (hierarchy.isDerived(doc._class, activity.class.ActivityMessage)) { + const html = await activityMessageToHtml(control, doc as ActivityMessage) + if (html !== undefined) { + title = '' + quote = html } } - body = data.data !== undefined ? `${markupToHTML(data.data)}` : body return { title, @@ -187,7 +207,8 @@ const SendTelegramNotifications: NotificationProviderFunc = async ( doc: Doc, data: InboxNotification, receiver: ReceiverInfo, - sender: SenderInfo + sender: SenderInfo, + message?: ActivityMessage ): Promise => { if (types.length === 0) { return [] @@ -205,7 +226,7 @@ const SendTelegramNotifications: NotificationProviderFunc = async ( } try { - const { title, body, quote } = await getTranslatedData(data, doc, control) + const { title, body, quote } = await getTranslatedData(data, doc, control, message) const record: TelegramNotificationRecord = { notificationId: data._id, account: receiver._id, diff --git a/services/telegram-bot/pod-telegram-bot/src/bot.ts b/services/telegram-bot/pod-telegram-bot/src/bot.ts index 34daefadd1..9bf2b556c7 100644 --- a/services/telegram-bot/pod-telegram-bot/src/bot.ts +++ b/services/telegram-bot/pod-telegram-bot/src/bot.ts @@ -13,13 +13,13 @@ // limitations under the License. // -import { Context, Telegraf, NarrowedContext } from 'telegraf' -import { Update, Message } from 'telegraf/typings/core/types/typegram' +import { Context, Telegraf } from 'telegraf' import { translate } from '@hcengineering/platform' import telegram from '@hcengineering/telegram' import { htmlToMarkup } from '@hcengineering/text' import { message } from 'telegraf/filters' import { toHTML } from '@telegraf/entity' +import { TextMessage } from '@telegraf/entity/types/types' import config from './config' import { PlatformWorker } from './worker' @@ -91,36 +91,20 @@ async function onConnect (ctx: Context, worker: PlatformWorker): Promise { await ctx.reply(`*${code}*`, { parse_mode: 'MarkdownV2' }) } -type TextMessage = Record<'text', any> & Message.TextMessage - -async function onReply ( - ctx: NarrowedContext, Update.MessageUpdate>, - worker: PlatformWorker -): Promise { - const id = ctx.chat?.id - const message = ctx.message - - if (id === undefined || message.reply_to_message === undefined) { - return - } - - const replyTo = message.reply_to_message +async function onReply (id: number, message: TextMessage, replyTo: number, worker: PlatformWorker): Promise { const userRecord = await worker.getUserRecord(id) if (userRecord === undefined) { - return + return false } - const notification = await worker.getNotificationRecord(replyTo.message_id, userRecord.email) + const notification = await worker.getNotificationRecord(replyTo, userRecord.email) if (notification === undefined) { - return + return false } - const isReplied = await worker.reply(notification, htmlToMarkup(toHTML(message))) - if (isReplied) { - await ctx.react('👍') - } + return await worker.reply(notification, htmlToMarkup(toHTML(message))) } export async function setUpBot (worker: PlatformWorker): Promise { @@ -134,8 +118,20 @@ export async function setUpBot (worker: PlatformWorker): Promise { bot.command('stop', (ctx) => onStop(ctx, worker)) bot.command('connect', (ctx) => onConnect(ctx, worker)) - bot.on(message('text'), async (ctx) => { - await onReply(ctx, worker) + bot.on(message('reply_to_message'), async (ctx) => { + const id = ctx.chat?.id + const message = ctx.message + + if (id === undefined || message.reply_to_message === undefined) { + return + } + + const replyTo = message.reply_to_message + const isReplied = await onReply(id, message as TextMessage, replyTo.message_id, worker) + + if (isReplied) { + await ctx.react('👍') + } }) const description = await translate(telegram.string.BotDescription, { app: config.App }) diff --git a/services/telegram-bot/pod-telegram-bot/src/config.ts b/services/telegram-bot/pod-telegram-bot/src/config.ts index 412c9d8231..40c92e4d21 100644 --- a/services/telegram-bot/pod-telegram-bot/src/config.ts +++ b/services/telegram-bot/pod-telegram-bot/src/config.ts @@ -16,7 +16,6 @@ export interface Config { Port: number BotToken: string - FrontUrl: string MongoURL: string MongoDB: string ServiceId: string @@ -35,7 +34,6 @@ const config: Config = (() => { const params: Partial = { Port: parseNumber(process.env.PORT) ?? 4020, BotToken: process.env.BOT_TOKEN, - FrontUrl: process.env.FRONT_URL, MongoURL: process.env.MONGO_URL, MongoDB: process.env.MONGO_DB, AccountsUrl: process.env.ACCOUNTS_URL, diff --git a/services/telegram-bot/pod-telegram-bot/src/start.ts b/services/telegram-bot/pod-telegram-bot/src/start.ts index 5c06d70a2e..ffdaab9d05 100644 --- a/services/telegram-bot/pod-telegram-bot/src/start.ts +++ b/services/telegram-bot/pod-telegram-bot/src/start.ts @@ -36,11 +36,16 @@ export const start = async (): Promise => { const bot = await setUpBot(worker) const app = createServer(bot, worker) - void bot.launch({ webhook: { domain: config.Domain, port: config.BotPort } }).then(() => { - void bot.telegram.getWebhookInfo().then((info) => { - ctx.info('Webhook info', info) + if (config.Domain === '') { + void bot.launch({ dropPendingUpdates: true }) + } else { + void bot.launch({ webhook: { domain: config.Domain, port: config.BotPort }, dropPendingUpdates: true }).then(() => { + void bot.telegram.getWebhookInfo().then((info) => { + ctx.info('Webhook info', info) + }) }) - }) + } + app.get(`/telegraf/${bot.secretPathComponent()}`, (req, res) => { res.status(200).send() })