250 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { parseJsonString } from "@egg/hooks"
import { LarkEvent } from "@egg/lark-msg-tool"
import { Lark } from "@egg/net-tool"
import { Context } from "../../types"
import MapPolyfill from "../../utils/polyfill/map"
import SetPolyfill from "../../utils/polyfill/set"
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,
{
chatId,
startTime,
endTime,
senderOpenId,
mentions: targetUsers,
excludedMessageIds = [],
excludeMentions = [],
}: {
chatId: string
startTime: string
endTime: string
senderOpenId?: string
mentions?: LarkEvent.Mention[]
excludedMessageIds?: string[] // 不需要的消息 ID
excludeMentions?: string[] // 不需要的提到的人可以是openid也可以是userName
}
): Promise<{
messages: Message[]
mentions: MapPolyfill<string, string>
}> => {
// 获取服务器的时区偏移量(以分钟为单位)
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)
return {
messages: [],
mentions: new MapPolyfill(),
}
const targetUsersSet = new SetPolyfill(
targetUsers
?.filter?.((user) => user.id.user_id)
?.map?.((user) => user.id.open_id) ?? []
)
// 清洗数据
// 取出所有的被AT的人以及发送者
const mentions: MapPolyfill<string, string> = new MapPolyfill()
const senders: SetPolyfill<string> = new SetPolyfill()
// 先把提问者加进去
if (senderOpenId) senders.add(senderOpenId)
// 过滤出文本和post消息
const allowedMsgTypes = ["text", "post"]
const filteredMsg: typeof chatHistory = []
// 遍历历史消息
for (const chat of chatHistory) {
let hasExcludeMention = false
if (chat.mentions) {
for (const mention of chat.mentions) {
mentions.set(mention.id, mention.name)
// 过滤掉不需要的提到的人
if (
excludeMentions.includes(mention.id) ||
excludeMentions.includes(mention.name)
) {
hasExcludeMention = true
}
}
}
// 如果提到了不要包含的人,则跳过该消息
if (hasExcludeMention) {
continue
}
// 过滤掉不需要的消息 ID
if (excludedMessageIds.includes(chat.message_id)) {
continue
}
// 过滤掉不是文本和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 SetPolyfill(
[...senders].filter((sender) => !mentions.has(sender))
)
logger.debug("获取聊天消息提及用户信息", {
mentions: [...mentions.entries()],
})
logger.debug("获取消息发送者信息", { senders: [...senders] })
logger.debug("获取未被提及的发送者信息", {
noMentionSenders: [...noMentionSenders],
})
// 从接口获取用户名
if (noMentionSenders.size !== 0) {
try {
const {
data: { items },
} = await larkService.user.batchGet([...noMentionSenders])
logger.debug("获取用户详细信息", { items })
for (const item of items) {
mentions.set(item.open_id, item.name)
}
} catch (error) {
// 报错了可以不处理,只是没有名字而已
logger.error("获取用户信息失败", { error })
}
}
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,
mentions,
}
}
export default getChatHistory