201 lines
5.4 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"
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<Message[]> => {
// 获取服务器的时区偏移量(以分钟为单位)
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<string, string> = new Map()
const senders: Set<string> = 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