import { parseJsonString } from "@egg/hooks" import { LarkEvent } from "@egg/lark-msg-tool" import { Lark } from "@egg/net-tool" import { Context } from "../../types" interface Message { user: string content: string time: string } /** * 提取 JSON 数据中的文本内容 * @param data - JSON 数据 * @returns 提取的文本内容 */ const extractTextFromJson = (data: any): string => { let result = "" if (Array.isArray(data)) { // 如果是数组,遍历数组元素 for (const element of data) { result += extractTextFromJson(element) // 递归调用处理每个元素 } } else if (typeof data === "object" && data !== null) { // 如果是对象,遍历对象的键 for (const key in data) { if (key === "text" && typeof data[key] === "string") { result += data[key] // 拼接 text 值 } else { result += extractTextFromJson(data[key]) // 递归调用处理子对象 } } } return result } /** * 获取聊天历史记录 * @param context - 上下文数据 * @returns 聊天消息数组 */ const getChatHistory = async ( { larkService, logger }: Context.Data, { chatId, startTime, endTime, mentions: targetUsers, }: { chatId: string startTime: string endTime: string mentions?: LarkEvent.Mention[] } ): Promise => { // 获取服务器的时区偏移量(以分钟为单位) const serverTimezoneOffset = new Date().getTimezoneOffset() // 上海时区的偏移量(UTC+8,以分钟为单位) const shanghaiTimezoneOffset = -8 * 60 // 将startTime和endTime转换为时间戳,并调整为上海时区 const startTimeTimestamp = Math.round( new Date(startTime).getTime() / 1000 + (shanghaiTimezoneOffset - serverTimezoneOffset) * 60 ) const endTimeTimestamp = Math.round( new Date(endTime).getTime() / 1000 + (shanghaiTimezoneOffset - serverTimezoneOffset) * 60 ) // 获取群聊中的历史记录 const { data: chatHistory } = await larkService.message.getHistory( chatId, String(startTimeTimestamp), String(endTimeTimestamp) ) if (chatHistory.length === 0) return [] const targetUsersSet = new Set( targetUsers ?.filter?.((user) => user.id.user_id) ?.map?.((user) => user.id.open_id) ?? [] ) // 清洗数据 // 取出所有的被AT的人,以及发送者 const mentions: Map = new Map() const senders: Set = new Set() // 过滤出文本和post消息 const allowedMsgTypes = ["text", "post"] const filteredMsg: typeof chatHistory = [] // 遍历历史消息 for (const chat of chatHistory) { if (chat.mentions) { for (const mention of chat.mentions) { mentions.set(mention.id, mention.name) } } // 过滤掉不是文本和post消息的消息 if (!allowedMsgTypes.includes(chat.msg_type)) { continue } // 过滤掉不是用户发送的消息 if (!chat.sender || chat.sender.sender_type !== "user") { continue } // 如果目标用户集合不为空,并且消息的发送者不在目标用户集合中,并且消息中提到的所有用户都不在目标用户集合中,则跳过该消息 if ( targetUsersSet.size !== 0 && !targetUsersSet.has(chat.sender.id) && (!chat.mentions || chat.mentions.every((mention) => !targetUsersSet.has(mention.id))) ) { continue } senders.add(chat.sender.id) filteredMsg.push(chat) } // 取出没有被AT的发送者 const noMentionSenders = new Set( [...senders].filter((sender) => !mentions.has(sender)) ) logger.debug(`Mentions: ${JSON.stringify(mentions)}`) logger.debug(`Senders: ${JSON.stringify(senders)}`) logger.debug(`No mention senders: ${JSON.stringify(noMentionSenders)}`) // 从接口获取用户名 if (noMentionSenders.size !== 0) { const { data: { items }, } = await larkService.user.batchGet([...noMentionSenders]) logger.debug(`Get user info: ${JSON.stringify(items)}`) for (const item of items) { mentions.set(item.open_id, item.name) } } const messages: Message[] = [] /** * 获取文本消息内容 * @param chat - 聊天消息数据 * @returns 文本消息内容 */ const getText = (chat: Lark.MessageData) => { let { text } = parseJsonString(chat.body.content, { text: "" }) as { text: string } if (!text) return "" // 替换被AT的人 if (chat.mentions) { for (const mention of chat.mentions) { const mentionKey = mention.key const mentionName = `@${mention.name}` text = text.replace(mentionKey, mentionName) } } // 去除可能出现的标签 return text.replace(/<[^>]+>/g, "") } /** * 获取 post 消息内容 * @param chat - 聊天消息数据 * @returns post 消息内容 */ const getPost = (chat: Lark.MessageData) => { const content = parseJsonString(chat.body.content, null) if (!content) return "" return extractTextFromJson(content) } // 构建消息数组 for (const chat of filteredMsg) { // 过滤掉机器人消息 const user = mentions.get(chat.sender.id) if (!user) continue messages.push({ user: mentions.get(chat.sender.id)!, content: chat.msg_type === "text" ? getText(chat) : getPost(chat), time: new Date(Number(chat.create_time)).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai", }), }) } return messages } export default getChatHistory