mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-02 13:52:40 +00:00
Fix tg parsing error for inline actions (#6747)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
parent
398f6b477b
commit
3c63f108b2
@ -20,7 +20,7 @@ import { htmlToMarkup, isEmptyMarkup, jsonToMarkup, MarkupNodeType } from '@hcen
|
|||||||
import { toHTML } from '@telegraf/entity'
|
import { toHTML } from '@telegraf/entity'
|
||||||
import { CallbackQuery, Message, Update } from 'telegraf/typings/core/types/typegram'
|
import { CallbackQuery, Message, Update } from 'telegraf/typings/core/types/typegram'
|
||||||
import { translate } from '@hcengineering/platform'
|
import { translate } from '@hcengineering/platform'
|
||||||
import { WithId } from 'mongodb'
|
import { ObjectId, WithId } from 'mongodb'
|
||||||
|
|
||||||
import config from '../config'
|
import config from '../config'
|
||||||
import { PlatformWorker } from '../worker'
|
import { PlatformWorker } from '../worker'
|
||||||
@ -29,13 +29,13 @@ import { toTelegramFileInfo } from '../utils'
|
|||||||
import { Command, defineCommands } from './commands'
|
import { Command, defineCommands } from './commands'
|
||||||
import { ChannelRecord, MessageRecord, TelegramFileInfo, UserRecord, WorkspaceInfo } from '../types'
|
import { ChannelRecord, MessageRecord, TelegramFileInfo, UserRecord, WorkspaceInfo } from '../types'
|
||||||
|
|
||||||
function encodeChannelId (workspace: string, channelId: string): string {
|
function encodeChannelId (channelId: string): string {
|
||||||
return `${workspace}_${channelId}`
|
return `@${channelId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeChannelId (id: string): { workspace: string, channelId: string } {
|
function decodeChannelId (id: string): string | undefined {
|
||||||
const [workspace, channelId] = id.split('_')
|
const [, channelId] = id.split('@')
|
||||||
return { workspace, channelId }
|
return channelId
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNextActionId = (workspace: string, page: number): string => `next_${workspace}_${page}`
|
const getNextActionId = (workspace: string, page: number): string => `next_${workspace}_${page}`
|
||||||
@ -114,10 +114,9 @@ async function handleSelectChannel (
|
|||||||
const userRecord = await worker.getUserRecord(id)
|
const userRecord = await worker.getUserRecord(id)
|
||||||
if (userRecord === undefined) return ['', false]
|
if (userRecord === undefined) return ['', false]
|
||||||
|
|
||||||
const { workspace, channelId } = decodeChannelId(match)
|
const channelId = decodeChannelId(match)
|
||||||
|
if (channelId === undefined || channelId === '') return ['', false]
|
||||||
const channels = await worker.getChannels(userRecord.email, workspace)
|
const channel = await worker.getChannel(userRecord.email, new ObjectId(channelId))
|
||||||
const channel = channels.find((it) => it._id.toString() === channelId)
|
|
||||||
|
|
||||||
if (channel === undefined) return ['', false]
|
if (channel === undefined) return ['', false]
|
||||||
|
|
||||||
@ -134,6 +133,14 @@ async function handleSelectChannel (
|
|||||||
return [channel.name, await worker.sendMessage(channel, userMessage.message_id, text, file)]
|
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 (
|
async function createSelectChannelKeyboard (
|
||||||
ctx: NarrowedContext<TgContext, Update.MessageUpdate>,
|
ctx: NarrowedContext<TgContext, Update.MessageUpdate>,
|
||||||
worker: PlatformWorker,
|
worker: PlatformWorker,
|
||||||
@ -141,6 +148,12 @@ async function createSelectChannelKeyboard (
|
|||||||
workspace: string
|
workspace: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const channels = await worker.getChannels(userRecord.email, workspace)
|
const channels = await worker.getChannels(userRecord.email, workspace)
|
||||||
|
|
||||||
|
if (channels.length === 0) {
|
||||||
|
await showNoChannelsMessage(ctx, worker, workspace)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const hasNext = channels.length > channelsPerPage
|
const hasNext = channels.length > channelsPerPage
|
||||||
const pageChannels = getPageChannels(channels, 0)
|
const pageChannels = getPageChannels(channels, 0)
|
||||||
|
|
||||||
@ -148,9 +161,7 @@ async function createSelectChannelKeyboard (
|
|||||||
reply_parameters: { message_id: ctx.message.message_id },
|
reply_parameters: { message_id: ctx.message.message_id },
|
||||||
...Markup.inlineKeyboard(
|
...Markup.inlineKeyboard(
|
||||||
[
|
[
|
||||||
...pageChannels.map((channel) =>
|
...pageChannels.map((channel) => Markup.button.callback(channel.name, encodeChannelId(channel._id.toString()))),
|
||||||
Markup.button.callback(channel.name, encodeChannelId(channel.workspace, channel._id.toString()))
|
|
||||||
),
|
|
||||||
...(hasNext ? [Markup.button.callback('Next>', getNextActionId(workspace, 0))] : [])
|
...(hasNext ? [Markup.button.callback('Next>', getNextActionId(workspace, 0))] : [])
|
||||||
],
|
],
|
||||||
{ columns: 1 }
|
{ columns: 1 }
|
||||||
@ -283,7 +294,7 @@ export async function setUpBot (worker: PlatformWorker): Promise<Telegraf<TgCont
|
|||||||
ctx.processingKeyboards.delete(messageId)
|
ctx.processingKeyboards.delete(messageId)
|
||||||
})
|
})
|
||||||
|
|
||||||
bot.action(/.+_.+/, async (ctx) => {
|
bot.action(/@.+/, async (ctx) => {
|
||||||
const messageId = ctx.callbackQuery.message?.message_id
|
const messageId = ctx.callbackQuery.message?.message_id
|
||||||
if (messageId === undefined) return
|
if (messageId === undefined) return
|
||||||
if (ctx.processingKeyboards.has(messageId)) return
|
if (ctx.processingKeyboards.has(messageId)) return
|
||||||
@ -341,11 +352,7 @@ const editChannelKeyboard = async (
|
|||||||
const channels = await worker.getChannels(userRecord.email, workspace)
|
const channels = await worker.getChannels(userRecord.email, workspace)
|
||||||
|
|
||||||
if (channels.length === 0) {
|
if (channels.length === 0) {
|
||||||
const ws = await worker.getWorkspaceInfo(workspace)
|
await showNoChannelsMessage(ctx, worker, 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' }
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,9 +362,7 @@ const editChannelKeyboard = async (
|
|||||||
|
|
||||||
await ctx.editMessageReplyMarkup({
|
await ctx.editMessageReplyMarkup({
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
...pageChannels.map((channel) => [
|
...pageChannels.map((channel) => [Markup.button.callback(channel.name, encodeChannelId(channel._id.toString()))]),
|
||||||
Markup.button.callback(channel.name, encodeChannelId(channel.workspace, channel._id.toString()))
|
|
||||||
]),
|
|
||||||
[
|
[
|
||||||
...(hasPrev ? [Markup.button.callback('<Prev', getPrevActionId(workspace, page))] : []),
|
...(hasPrev ? [Markup.button.callback('<Prev', getPrevActionId(workspace, page))] : []),
|
||||||
...(hasNext ? [Markup.button.callback('Next>', getNextActionId(workspace, page))] : [])
|
...(hasNext ? [Markup.button.callback('Next>', getNextActionId(workspace, page))] : [])
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// 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 { MeasureContext, Ref, SortingOrder, systemAccountEmail } from '@hcengineering/core'
|
||||||
import { InboxNotification } from '@hcengineering/notification'
|
import { InboxNotification } from '@hcengineering/notification'
|
||||||
import { TelegramNotificationRequest } from '@hcengineering/telegram'
|
import { TelegramNotificationRequest } from '@hcengineering/telegram'
|
||||||
@ -44,9 +44,11 @@ const closeWorkspaceTimeout = 10 * 60 * 1000 // 10 minutes
|
|||||||
export class PlatformWorker {
|
export class PlatformWorker {
|
||||||
private readonly workspacesClients = new Map<string, WorkspaceClient>()
|
private readonly workspacesClients = new Map<string, WorkspaceClient>()
|
||||||
private readonly closeWorkspaceTimeouts: Map<string, NodeJS.Timeout> = new Map<string, NodeJS.Timeout>()
|
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 readonly workspaceInfoById = new Map<string, WorkspaceInfo>()
|
||||||
|
|
||||||
private constructor (
|
private constructor (
|
||||||
@ -58,12 +60,19 @@ export class PlatformWorker {
|
|||||||
private readonly repliesStorage: Collection<ReplyRecord>,
|
private readonly repliesStorage: Collection<ReplyRecord>,
|
||||||
private readonly channelsStorage: Collection<ChannelRecord>
|
private readonly channelsStorage: Collection<ChannelRecord>
|
||||||
) {
|
) {
|
||||||
this.intervalId = setInterval(
|
this.otpIntervalId = setInterval(
|
||||||
() => {
|
() => {
|
||||||
void otpStorage.deleteMany({ expires: { $lte: Date.now() } })
|
void otpStorage.deleteMany({ expires: { $lte: Date.now() } })
|
||||||
},
|
},
|
||||||
3 * 60 * 1000
|
3 * 60 * 1000
|
||||||
)
|
)
|
||||||
|
this.clearIntervalId = setInterval(
|
||||||
|
() => {
|
||||||
|
this.channelsByWorkspace.clear()
|
||||||
|
this.channelById.clear()
|
||||||
|
},
|
||||||
|
60 * 60 * 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUsersToDisconnect (): Promise<UserRecord[]> {
|
public async getUsersToDisconnect (): Promise<UserRecord[]> {
|
||||||
@ -75,8 +84,11 @@ export class PlatformWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async close (): Promise<void> {
|
async close (): Promise<void> {
|
||||||
if (this.intervalId !== undefined) {
|
if (this.otpIntervalId !== undefined) {
|
||||||
clearInterval(this.intervalId)
|
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>[]> {
|
async getChannels (email: string, workspace: string): Promise<WithId<ChannelRecord>[]> {
|
||||||
const key = `${email}:${workspace}`
|
const key = `${email}:${workspace}`
|
||||||
|
|
||||||
if (this.channelsMap.has(key)) {
|
if (this.channelsByWorkspace.has(key)) {
|
||||||
return this.channelsMap.get(key) ?? []
|
return this.channelsByWorkspace.get(key) ?? []
|
||||||
}
|
}
|
||||||
const res = await this.channelsStorage
|
const res = await this.channelsStorage
|
||||||
.find({ workspace, email }, { sort: { name: SortingOrder.Ascending } })
|
.find({ workspace, email }, { sort: { name: SortingOrder.Ascending } })
|
||||||
.toArray()
|
.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
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +347,12 @@ export class PlatformWorker {
|
|||||||
await this.channelsStorage.deleteMany({ _id: { $in: toDelete.map((c) => c._id) } })
|
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> {
|
async getWorkspaceInfo (workspaceId: string): Promise<WorkspaceInfo | undefined> {
|
||||||
|
Loading…
Reference in New Issue
Block a user