Fix tg parsing error for inline actions (#6747)

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2024-09-26 18:47:13 +04:00 committed by GitHub
parent 398f6b477b
commit 3c63f108b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 32 deletions

View File

@ -20,7 +20,7 @@ import { htmlToMarkup, isEmptyMarkup, jsonToMarkup, MarkupNodeType } from '@hcen
import { toHTML } from '@telegraf/entity'
import { CallbackQuery, Message, Update } from 'telegraf/typings/core/types/typegram'
import { translate } from '@hcengineering/platform'
import { WithId } from 'mongodb'
import { ObjectId, WithId } from 'mongodb'
import config from '../config'
import { PlatformWorker } from '../worker'
@ -29,13 +29,13 @@ import { toTelegramFileInfo } from '../utils'
import { Command, defineCommands } from './commands'
import { ChannelRecord, MessageRecord, TelegramFileInfo, UserRecord, WorkspaceInfo } from '../types'
function encodeChannelId (workspace: string, channelId: string): string {
return `${workspace}_${channelId}`
function encodeChannelId (channelId: string): string {
return `@${channelId}`
}
function decodeChannelId (id: string): { workspace: string, channelId: string } {
const [workspace, channelId] = id.split('_')
return { workspace, channelId }
function decodeChannelId (id: string): string | undefined {
const [, channelId] = id.split('@')
return channelId
}
const getNextActionId = (workspace: string, page: number): string => `next_${workspace}_${page}`
@ -114,10 +114,9 @@ async function handleSelectChannel (
const userRecord = await worker.getUserRecord(id)
if (userRecord === undefined) return ['', false]
const { workspace, channelId } = decodeChannelId(match)
const channels = await worker.getChannels(userRecord.email, workspace)
const channel = channels.find((it) => it._id.toString() === channelId)
const channelId = decodeChannelId(match)
if (channelId === undefined || channelId === '') return ['', false]
const channel = await worker.getChannel(userRecord.email, new ObjectId(channelId))
if (channel === undefined) return ['', false]
@ -134,6 +133,14 @@ async function handleSelectChannel (
return [channel.name, await worker.sendMessage(channel, userMessage.message_id, text, file)]
}
async function showNoChannelsMessage (ctx: Context, worker: PlatformWorker, workspace: string): Promise<void> {
const ws = await worker.getWorkspaceInfo(workspace)
await ctx.editMessageText(
`No channels found in workspace <b>${ws?.name ?? workspace}</b>.\nTo sync channels call /${Command.SyncAllChannels} or /${Command.SyncStarredChannels}`,
{ parse_mode: 'HTML' }
)
}
async function createSelectChannelKeyboard (
ctx: NarrowedContext<TgContext, Update.MessageUpdate>,
worker: PlatformWorker,
@ -141,6 +148,12 @@ async function createSelectChannelKeyboard (
workspace: string
): Promise<void> {
const channels = await worker.getChannels(userRecord.email, workspace)
if (channels.length === 0) {
await showNoChannelsMessage(ctx, worker, workspace)
return
}
const hasNext = channels.length > channelsPerPage
const pageChannels = getPageChannels(channels, 0)
@ -148,9 +161,7 @@ async function createSelectChannelKeyboard (
reply_parameters: { message_id: ctx.message.message_id },
...Markup.inlineKeyboard(
[
...pageChannels.map((channel) =>
Markup.button.callback(channel.name, encodeChannelId(channel.workspace, channel._id.toString()))
),
...pageChannels.map((channel) => Markup.button.callback(channel.name, encodeChannelId(channel._id.toString()))),
...(hasNext ? [Markup.button.callback('Next>', getNextActionId(workspace, 0))] : [])
],
{ columns: 1 }
@ -283,7 +294,7 @@ export async function setUpBot (worker: PlatformWorker): Promise<Telegraf<TgCont
ctx.processingKeyboards.delete(messageId)
})
bot.action(/.+_.+/, async (ctx) => {
bot.action(/@.+/, async (ctx) => {
const messageId = ctx.callbackQuery.message?.message_id
if (messageId === undefined) return
if (ctx.processingKeyboards.has(messageId)) return
@ -341,11 +352,7 @@ const editChannelKeyboard = async (
const channels = await worker.getChannels(userRecord.email, workspace)
if (channels.length === 0) {
const ws = await worker.getWorkspaceInfo(workspace)
await ctx.editMessageText(
`No channels found in workspace <b>${ws?.name ?? workspace}</b>.\nTo add channels call /${Command.SyncAllChannels} or /${Command.SyncStarredChannels}`,
{ parse_mode: 'HTML' }
)
await showNoChannelsMessage(ctx, worker, workspace)
return
}
@ -355,9 +362,7 @@ const editChannelKeyboard = async (
await ctx.editMessageReplyMarkup({
inline_keyboard: [
...pageChannels.map((channel) => [
Markup.button.callback(channel.name, encodeChannelId(channel.workspace, channel._id.toString()))
]),
...pageChannels.map((channel) => [Markup.button.callback(channel.name, encodeChannelId(channel._id.toString()))]),
[
...(hasPrev ? [Markup.button.callback('<Prev', getPrevActionId(workspace, page))] : []),
...(hasNext ? [Markup.button.callback('Next>', getNextActionId(workspace, page))] : [])

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import type { Collection, WithId } from 'mongodb'
import type { Collection, ObjectId, WithId } from 'mongodb'
import { MeasureContext, Ref, SortingOrder, systemAccountEmail } from '@hcengineering/core'
import { InboxNotification } from '@hcengineering/notification'
import { TelegramNotificationRequest } from '@hcengineering/telegram'
@ -44,9 +44,11 @@ const closeWorkspaceTimeout = 10 * 60 * 1000 // 10 minutes
export class PlatformWorker {
private readonly workspacesClients = new Map<string, WorkspaceClient>()
private readonly closeWorkspaceTimeouts: Map<string, NodeJS.Timeout> = new Map<string, NodeJS.Timeout>()
private readonly intervalId: NodeJS.Timeout | undefined
private readonly otpIntervalId: NodeJS.Timeout | undefined
private readonly clearIntervalId: NodeJS.Timeout | undefined
private readonly channelsMap = new Map<string, WithId<ChannelRecord>[]>()
private readonly channelsByWorkspace = new Map<string, WithId<ChannelRecord>[]>()
private readonly channelById = new Map<ObjectId, WithId<ChannelRecord>>()
private readonly workspaceInfoById = new Map<string, WorkspaceInfo>()
private constructor (
@ -58,12 +60,19 @@ export class PlatformWorker {
private readonly repliesStorage: Collection<ReplyRecord>,
private readonly channelsStorage: Collection<ChannelRecord>
) {
this.intervalId = setInterval(
this.otpIntervalId = setInterval(
() => {
void otpStorage.deleteMany({ expires: { $lte: Date.now() } })
},
3 * 60 * 1000
)
this.clearIntervalId = setInterval(
() => {
this.channelsByWorkspace.clear()
this.channelById.clear()
},
60 * 60 * 1000
)
}
public async getUsersToDisconnect (): Promise<UserRecord[]> {
@ -75,8 +84,11 @@ export class PlatformWorker {
}
async close (): Promise<void> {
if (this.intervalId !== undefined) {
clearInterval(this.intervalId)
if (this.otpIntervalId !== undefined) {
clearInterval(this.otpIntervalId)
}
if (this.clearIntervalId !== undefined) {
clearInterval(this.clearIntervalId)
}
}
@ -254,14 +266,32 @@ export class PlatformWorker {
async getChannels (email: string, workspace: string): Promise<WithId<ChannelRecord>[]> {
const key = `${email}:${workspace}`
if (this.channelsMap.has(key)) {
return this.channelsMap.get(key) ?? []
if (this.channelsByWorkspace.has(key)) {
return this.channelsByWorkspace.get(key) ?? []
}
const res = await this.channelsStorage
.find({ workspace, email }, { sort: { name: SortingOrder.Ascending } })
.toArray()
this.channelsMap.set(key, res)
this.channelsByWorkspace.set(key, res)
for (const channel of res) {
this.channelById.set(channel._id, channel)
}
return res
}
async getChannel (email: string, channelId: ObjectId): Promise<WithId<ChannelRecord> | undefined> {
if (this.channelById.has(channelId)) {
const channel = this.channelById.get(channelId)
return channel !== undefined && channel.email === email ? channel : undefined
}
const res = (await this.channelsStorage.findOne({ _id: channelId, email })) ?? undefined
if (res !== undefined) {
this.channelById.set(res._id, res)
}
return res
}
@ -317,7 +347,12 @@ export class PlatformWorker {
await this.channelsStorage.deleteMany({ _id: { $in: toDelete.map((c) => c._id) } })
}
this.channelsMap.delete(`${email}:${workspace}`)
this.channelsByWorkspace.delete(`${email}:${workspace}`)
for (const [key, channel] of this.channelById.entries()) {
if (channel.email === email) {
this.channelById.delete(key)
}
}
}
async getWorkspaceInfo (workspaceId: string): Promise<WorkspaceInfo | undefined> {