300 lines
7.7 KiB
TypeScript
300 lines
7.7 KiB
TypeScript
import { LarkService } from "@egg/net-tool"
|
|
|
|
import { appMap } from "../../constant/appMap"
|
|
import { RespMessage } from "../../constant/message"
|
|
import prisma from "../../prisma"
|
|
import { Context } from "../../types"
|
|
import genContext from "../../utils/genContext"
|
|
import llm from "../../utils/llm"
|
|
import { getTimeRange } from "../../utils/time"
|
|
import getChatHistory from "./chatHistory"
|
|
|
|
/**
|
|
* 总结指定聊天记录
|
|
* @param {Context.Data} ctx - 请求上下文
|
|
* @param {string} timeScope - 时间范围
|
|
* @param {any} subscription - 订阅信息
|
|
* @returns {Promise<void>}
|
|
*/
|
|
const genReport = async (
|
|
ctx: Context.Data,
|
|
timeScope: "daily" | "weekly",
|
|
subscription: {
|
|
id: bigint
|
|
chat_id: string
|
|
robot_id: string
|
|
initiator: string
|
|
}
|
|
) => {
|
|
const { logger, requestId, larkCard } = ctx
|
|
const cardGender = larkCard.child("groupAgent")
|
|
try {
|
|
const { chat_id: chatId, robot_id: robotId } = subscription
|
|
// 获取接口信息
|
|
const appInfo = appMap[robotId]
|
|
if (!appInfo) {
|
|
logger.error(`Failed to get app info for ${robotId}`)
|
|
return
|
|
}
|
|
// 组织接口
|
|
const larkService = new LarkService({
|
|
appId: appInfo.app_id,
|
|
appSecret: appInfo.app_secret,
|
|
requestId,
|
|
})
|
|
// 获取时间范围
|
|
const { startTime, endTime } = getTimeRange(timeScope)
|
|
|
|
// 计时开始
|
|
const processStart = Date.now()
|
|
|
|
// 获取聊天记录
|
|
const { messages: chatHistory } = await getChatHistory(
|
|
{ larkService, logger } as Context.Data,
|
|
{
|
|
chatId,
|
|
startTime,
|
|
endTime,
|
|
excludeMentions: [appInfo.app_name],
|
|
}
|
|
)
|
|
if (chatHistory.length === 0) {
|
|
logger.info(`No message in chat ${chatId}`)
|
|
return
|
|
}
|
|
|
|
// 使用大模型总结消息
|
|
const llmRes = await llm.invoke(
|
|
`${timeScope}Summary`,
|
|
{
|
|
chatHistory: JSON.stringify(chatHistory),
|
|
time: new Date().toLocaleString("zh-CN", {
|
|
timeZone: "Asia/Shanghai",
|
|
}),
|
|
},
|
|
requestId
|
|
)
|
|
// 计时结束
|
|
const processEnd = Date.now()
|
|
const processingTime = ((processEnd - processStart) / 1000).toFixed(2)
|
|
logger.info(
|
|
`LLM takes time: ${processingTime}s, see detail: http://langfuse.ai.srv/project/cm1j2tkj9001gukrgdvc1swuw/sessions/${requestId}`
|
|
)
|
|
// 发送卡片消息
|
|
await larkService.message.sendCard2Chat(
|
|
chatId,
|
|
cardGender.genCard("autoReport", {
|
|
llmRes,
|
|
timeScope: timeScope === "daily" ? "今日日报" : "本周周报",
|
|
})
|
|
)
|
|
// 记录发送的卡片
|
|
await prisma.chat_agent_message_log.create({
|
|
data: {
|
|
subscription_id: subscription.id,
|
|
initiator: subscription.initiator,
|
|
langfuse_link: `http://langfuse.ai.srv/project/cm1j2tkj9001gukrgdvc1swuw/sessions/${requestId}`,
|
|
},
|
|
})
|
|
} catch (error: any) {
|
|
logger.error(
|
|
`Failed to summarize chat ${subscription.chat_id}: ${error.message}`
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 自动总结聊天记录
|
|
* @returns {Promise<void>}
|
|
*/
|
|
const genAllReport = async (timeScope: "daily" | "weekly" = "daily") => {
|
|
const ctx = await genContext(new Request("https://baidu.com"))
|
|
const { logger } = ctx
|
|
|
|
try {
|
|
// 获取全部需要自动总结的群组
|
|
const subscriptionList =
|
|
await prisma.chat_agent_summary_subscription.findMany({
|
|
where: {
|
|
terminator: "",
|
|
},
|
|
})
|
|
|
|
if (subscriptionList.length === 0) {
|
|
logger.info("No group needs to be summarized")
|
|
return
|
|
}
|
|
|
|
// 一个一个群组的总结,避免触发频率限制
|
|
for (const subscription of subscriptionList) {
|
|
await genReport(ctx, timeScope, subscription)
|
|
}
|
|
} catch (e: any) {
|
|
logger.error(`Auto summary error: ${e.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 立即生成日报或周报(测试用)
|
|
* @param {Context.Data} ctx - 请求上下文
|
|
* @returns {Promise<void>}
|
|
*/
|
|
const gen4Test = async (ctx: Context.Data, timeScope: "daily" | "weekly") => {
|
|
const {
|
|
logger,
|
|
larkCard,
|
|
larkService,
|
|
larkBody: { chatId },
|
|
} = ctx
|
|
try {
|
|
logger.info(`timeScope: ${timeScope}`)
|
|
// 获取需要总结的chatId
|
|
if (!chatId) {
|
|
logger.error("Invalid request body")
|
|
return
|
|
}
|
|
// 获取订阅信息
|
|
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
|
{
|
|
where: {
|
|
chat_id: chatId,
|
|
terminator: "",
|
|
},
|
|
}
|
|
)
|
|
// 没有订阅信息
|
|
if (!subscription) {
|
|
logger.error(`No subscription found for chat ${chatId}`)
|
|
await larkService.message.sendCard2Chat(
|
|
chatId,
|
|
larkCard.genErrorCard("本群未订阅日报、周报")
|
|
)
|
|
return
|
|
}
|
|
// 总结
|
|
await genReport(ctx, timeScope, subscription)
|
|
} catch (error: any) {
|
|
logger.error(`Failed to summarize chat ${chatId}: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 注册消息总结的订阅
|
|
* @returns
|
|
*/
|
|
const subscribe = async ({
|
|
app,
|
|
larkService,
|
|
logger,
|
|
larkBody,
|
|
larkCard,
|
|
}: Context.Data) => {
|
|
try {
|
|
const cardGender = larkCard.child("groupAgent")
|
|
// 判断是否有 chatId 和 userId
|
|
if (!larkBody.chatId || !larkBody.userId) {
|
|
logger.error(`chatId or userId is empty`)
|
|
return
|
|
}
|
|
// 先查询是否已经存在订阅
|
|
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
|
{
|
|
where: {
|
|
chat_id: larkBody.chatId,
|
|
terminator: "",
|
|
},
|
|
}
|
|
)
|
|
// 如果已经存在订阅,则返回已经注册过了
|
|
if (subscription) {
|
|
logger.info(`chatId: ${larkBody.chatId} has been registered`)
|
|
// 发送已经注册过的消息
|
|
await larkService.message.sendCard2Chat(
|
|
larkBody.chatId,
|
|
cardGender.genSuccessCard(RespMessage.hasRegistered)
|
|
)
|
|
return
|
|
}
|
|
// 注册订阅
|
|
await prisma.chat_agent_summary_subscription.create({
|
|
data: {
|
|
chat_id: larkBody.chatId,
|
|
robot_id: app,
|
|
initiator: larkBody.userId,
|
|
},
|
|
})
|
|
// 发送成功消息
|
|
await larkService.message.sendCard2Chat(
|
|
larkBody.chatId,
|
|
cardGender.genSuccessCard(RespMessage.registerSuccess)
|
|
)
|
|
} catch (e: any) {
|
|
logger.error(`Subscribe error: ${e.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 取消消息总结的订阅
|
|
* @returns
|
|
*/
|
|
const unsubscribe = async ({
|
|
logger,
|
|
larkBody,
|
|
larkService,
|
|
larkCard,
|
|
}: Context.Data) => {
|
|
try {
|
|
const cardGender = larkCard.child("groupAgent")
|
|
// 判断是否有 chatId 和 userId
|
|
if (!larkBody.chatId || !larkBody.userId) {
|
|
logger.error(`chatId or userId is empty`)
|
|
return
|
|
}
|
|
// 查找现有的订阅
|
|
const subscription = await prisma.chat_agent_summary_subscription.findFirst(
|
|
{
|
|
where: {
|
|
chat_id: larkBody.chatId,
|
|
terminator: "",
|
|
},
|
|
}
|
|
)
|
|
// 如果没有找到订阅,则返回错误
|
|
if (!subscription) {
|
|
logger.info(`chatId: ${larkBody.chatId} has not been registered`)
|
|
// 发送已经取消订阅的消息
|
|
await larkService.message.sendCard2Chat(
|
|
larkBody.chatId,
|
|
cardGender.genSuccessCard(RespMessage.cancelSuccess)
|
|
)
|
|
return
|
|
}
|
|
// 更新订阅,设置终止者和终止时间
|
|
await prisma.chat_agent_summary_subscription.update({
|
|
where: {
|
|
id: subscription.id,
|
|
},
|
|
data: {
|
|
terminator: larkBody.userId,
|
|
},
|
|
})
|
|
// 发送成功消息
|
|
await larkService.message.sendCard2Chat(
|
|
larkBody.chatId,
|
|
cardGender.genSuccessCard(RespMessage.cancelSuccess)
|
|
)
|
|
} catch (e: any) {
|
|
logger.error(`Unsubscribe error: ${e.message}`)
|
|
}
|
|
}
|
|
|
|
const report = {
|
|
genReport,
|
|
genAllReport,
|
|
gen4Test,
|
|
subscribe,
|
|
unsubscribe,
|
|
}
|
|
|
|
export default report
|