Adjust tg bot (#8412)

* Remove notification provider func

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* Adjust tg-bot

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* Use global integration

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* Use workspace integration

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* getSocialIdBySocialKey -> findFullSocialIdBySocialKey

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* move findFullSocialIdBySocialKey to service operations

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* use listIntegrations with account token

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* Adjust api

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

* Fxi tests

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>

---------

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina
2025-04-10 12:54:53 +07:00
committed by GitHub
parent 80f67e3d66
commit 2aa8ae5243
59 changed files with 1666 additions and 1183 deletions
+210 -85
View File
@@ -12,14 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
/* eslint-disable @typescript-eslint/no-unused-vars */
import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/activity'
import chunter, { ChatMessage } from '@hcengineering/chunter'
import contact, { Channel, ChannelProvider, Contact, Employee, formatName, Person } from '@hcengineering/contact'
import {
import contact, { Channel, Person } from '@hcengineering/contact'
import core, {
PersonId,
Class,
concatLink,
Doc,
DocumentQuery,
FindOptions,
@@ -28,26 +26,33 @@ import {
Ref,
Tx,
TxCreateDoc,
TxProcessor
TxProcessor,
TxCUD,
TxUpdateDoc
} from '@hcengineering/core'
import notification, {
BaseNotificationType,
ActivityInboxNotification,
InboxNotification,
MentionInboxNotification,
NotificationProviderSetting,
NotificationType
} from '@hcengineering/notification'
import { getMetadata, getResource, translate } from '@hcengineering/platform'
import { TriggerControl } from '@hcengineering/server-core'
import { NotificationProviderFunc, ReceiverInfo, SenderInfo } from '@hcengineering/server-notification'
import { getResource, translate } from '@hcengineering/platform'
import { getAccountBySocialId, getPerson } from '@hcengineering/server-contact'
import { PlatformQueueProducer, QueueTopic, TriggerControl } from '@hcengineering/server-core'
import {
getNotificationLink,
getTextPresenter,
getTranslatedNotificationContent
getTranslatedNotificationContent,
AvailableProvidersCache,
AvailableProvidersCacheKey
} from '@hcengineering/server-notification-resources'
import serverTelegram from '@hcengineering/server-telegram'
import { generateToken } from '@hcengineering/server-token'
import setting, { Integration } from '@hcengineering/setting'
import telegram, { TelegramMessage, TelegramNotificationRequest } from '@hcengineering/telegram'
import {
type TelegramNotificationQueueMessage,
TelegramQueueMessageType,
TelegramQueueMessage
} from '@hcengineering/server-telegram'
import telegram, { TelegramMessage } from '@hcengineering/telegram'
import { jsonToHTML, markupToJSON } from '@hcengineering/text'
/**
@@ -149,20 +154,20 @@ export async function GetIntegrationOwnerTG (
return undefined
}
async function getContactChannel (
control: TriggerControl,
value: Contact,
provider: Ref<ChannelProvider>
): Promise<string | undefined> {
if (value === undefined) return
const res = (
await control.findAll(control.ctx, contact.class.Channel, {
attachedTo: value._id,
provider
})
)[0]
return res?.value ?? ''
}
// async function getContactChannel (
// control: TriggerControl,
// value: Contact,
// provider: Ref<ChannelProvider>
// ): Promise<string | undefined> {
// if (value === undefined) return
// const res = (
// await control.findAll(control.ctx, contact.class.Channel, {
// attachedTo: value._id,
// provider
// })
// )[0]
// return res?.value ?? ''
// }
async function activityMessageToHtml (control: TriggerControl, message: ActivityMessage): Promise<string | undefined> {
const { hierarchy } = control
@@ -252,66 +257,185 @@ function hasAttachments (doc: ActivityMessage | undefined, hierarchy: Hierarchy)
return false
}
const telegramNotificationKey = 'telegram.notification.reported'
const SendTelegramNotifications: NotificationProviderFunc = async (
const telegramNotificationCacheKey = 'telegram.notification.cache'
async function NotificationsHandler (txes: TxCreateDoc<InboxNotification>[], control: TriggerControl): Promise<Tx[]> {
const queue = control.queue
if (queue === undefined) {
return []
}
const availableProviders: AvailableProvidersCache = control.contextCache.get(AvailableProvidersCacheKey) ?? new Map()
const all: InboxNotification[] = txes
.map((tx) => TxProcessor.createDoc2Doc(tx))
.filter(
(it) =>
availableProviders.get(it._id)?.find((p) => p === telegram.providers.TelegramNotificationProvider) !== undefined
)
if (all.length === 0) {
return []
}
const result: Tx[] = []
const producer = queue.createProducer(control.ctx, QueueTopic.TelegramBot)
try {
for (const inboxNotification of all) {
result.push(...(await processNotification(inboxNotification, control, producer)))
}
} finally {
await producer.close()
}
return result
}
async function getNotificationMessage (
n: InboxNotification,
control: TriggerControl,
types: BaseNotificationType[],
doc: Doc,
data: InboxNotification,
receiver: ReceiverInfo,
sender: SenderInfo,
message?: ActivityMessage
): Promise<Tx[]> => {
// TODO: FIXME
// if (types.length === 0) {
// return []
// }
cache: Map<Ref<Doc>, Doc>
): Promise<ActivityMessage | undefined> {
const { hierarchy } = control
if (hierarchy.isDerived(n._class, notification.class.ActivityInboxNotification)) {
const activityNotification = n as ActivityInboxNotification
const message =
cache.get(activityNotification.attachedTo) ??
(
await control.findAll(control.ctx, activityNotification.attachedToClass, {
_id: activityNotification.attachedTo
})
)[0]
return message as ActivityMessage
} else if (hierarchy.isDerived(n._class, notification.class.MentionInboxNotification)) {
const mentionNotification = n as MentionInboxNotification
if (hierarchy.isDerived(mentionNotification.mentionedInClass, activity.class.ActivityMessage)) {
const message =
cache.get(mentionNotification.mentionedIn) ??
(
await control.findAll(control.ctx, mentionNotification.mentionedInClass, {
_id: mentionNotification.mentionedIn
})
)[0]
return message as ActivityMessage
}
}
// const botUrl = getMetadata(serverTelegram.metadata.BotUrl)
return undefined
}
// if (botUrl === undefined || botUrl === '') {
// const reported = control.cache.get(telegramNotificationKey)
// if (reported === undefined) {
// control.ctx.error('Please provide telegram bot service url to enable telegram notifications.')
// control.cache.set(telegramNotificationKey, true)
// }
// return []
// }
async function getSenderName (n: InboxNotification, control: TriggerControl): Promise<string> {
const inlineName = n.intlParams?.senderName
if (inlineName != null && inlineName !== '') {
return inlineName.toString()
}
const senderPerson = await getPerson(control, n.createdBy ?? n.modifiedBy)
return senderPerson?.name ?? 'System'
}
// if (!receiver.person.active) {
// return []
// }
async function processNotification (
n: InboxNotification,
control: TriggerControl,
producer: PlatformQueueProducer<TelegramQueueMessage>
): Promise<Tx[]> {
try {
const cache: Map<Ref<Doc>, Doc> = control.contextCache.get(telegramNotificationCacheKey) ?? new Map()
const doc = cache.get(n.objectId) ?? (await control.findAll(control.ctx, n.objectClass, { _id: n.objectId }))[0]
if (doc === undefined) return []
const message = await getNotificationMessage(n, control, cache)
// try {
// const { title, body, quote, link } = await getTranslatedData(data, doc, control, message)
// const record: TelegramNotificationRequest = {
// notificationId: data._id,
// messageId: message?._id,
// account: receiver._id,
// workspace: control.workspace,
// sender: data.intlParams?.senderName?.toString() ?? formatName(sender.person?.name ?? 'System'),
// attachments: hasAttachments(message, control.hierarchy),
// title,
// quote,
// body,
// link
// }
cache.set(n.objectId, doc)
control.contextCache.set(telegramNotificationCacheKey, cache)
// await fetch(concatLink(botUrl, '/notify'), {
// method: 'POST',
// headers: {
// Authorization: 'Bearer ' + generateToken(receiver.account.email, control.workspace, { service: 'telegram' }),
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify([record])
// })
// } catch (err) {
// control.ctx.error('Could not send telegram notification', {
// err,
// notificationId: data._id,
// receiver: receiver.account.email
// })
// }
const { title, body, quote, link } = await getTranslatedData(n, doc, control, message)
const record: TelegramNotificationQueueMessage = {
type: TelegramQueueMessageType.Notification,
notificationId: n._id,
messageId: message?._id,
account: n.user,
sender: await getSenderName(n, control),
attachments: hasAttachments(message, control.hierarchy),
title,
quote,
body,
link
}
await producer.send(control.workspace.uuid, [record])
} catch (err) {
control.ctx.error('Could not send telegram notification', {
err,
notificationId: n._id,
account: n.user
})
}
return []
}
async function updateWorkspaceSubscription (
producer: PlatformQueueProducer<TelegramQueueMessage>,
enabled: boolean,
socialId: PersonId,
control: TriggerControl
): Promise<void> {
const account = await getAccountBySocialId(control, socialId)
if (account == null) {
return
}
await producer.send(control.workspace.uuid, [
{
type: TelegramQueueMessageType.WorkspaceSubscription,
account,
subscribe: enabled
}
])
}
async function ProviderSettingsHandler (
txes: TxCUD<NotificationProviderSetting>[],
control: TriggerControl
): Promise<Tx[]> {
const queue = control.queue
if (queue === undefined) {
return []
}
const producer = queue.createProducer(control.ctx, QueueTopic.TelegramBot)
try {
for (const tx of txes) {
if (tx._class === core.class.TxCreateDoc) {
const createTx = tx as TxCreateDoc<NotificationProviderSetting>
const setting = TxProcessor.createDoc2Doc(createTx)
if (setting.attachedTo === telegram.providers.TelegramNotificationProvider) {
await updateWorkspaceSubscription(producer, setting.enabled, setting.createdBy ?? setting.modifiedBy, control)
}
} else if (tx._class === core.class.TxUpdateDoc) {
const updateTx = tx as TxUpdateDoc<NotificationProviderSetting>
if (updateTx.operations.enabled !== undefined) {
const setting = (
await control.findAll(control.ctx, notification.class.NotificationProviderSetting, {
_id: updateTx.objectId
})
)[0]
if (setting !== undefined && setting.attachedTo === telegram.providers.TelegramNotificationProvider) {
await updateWorkspaceSubscription(
producer,
updateTx.operations.enabled,
setting.createdBy ?? setting.modifiedBy,
control
)
}
}
}
}
} finally {
await producer.close()
}
return []
}
@@ -319,13 +443,14 @@ const SendTelegramNotifications: NotificationProviderFunc = async (
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export default async () => ({
trigger: {
OnMessageCreate
OnMessageCreate,
NotificationsHandler,
ProviderSettingsHandler
},
function: {
IsIncomingMessageTypeMatch,
FindMessages,
GetCurrentEmployeeTG,
GetIntegrationOwnerTG,
SendTelegramNotifications
GetIntegrationOwnerTG
}
})