feat(mi-chat): 优化消息返回功能
All checks were successful
Egg Server CI/CD / build-image (push) Successful in 1m9s
Egg Server CI/CD / refresh-image (push) Successful in 14s
Egg Server CI/CD / fast-deploy (push) Successful in 5s

This commit is contained in:
zhaoyingbo 2024-10-11 10:59:32 +00:00
parent 447e0f19bb
commit b44f66f6be
14 changed files with 676 additions and 152 deletions

View File

@ -27,6 +27,7 @@
"tseslint", "tseslint",
"userid", "userid",
"wlpbbgiky", "wlpbbgiky",
"Yoav" "Yoav",
"zhaoyingbo"
] ]
} }

BIN
bun.lockb

Binary file not shown.

View File

@ -36,7 +36,7 @@
"dependencies": { "dependencies": {
"@dotenvx/dotenvx": "^1.14.2", "@dotenvx/dotenvx": "^1.14.2",
"@egg/hooks": "^1.2.0", "@egg/hooks": "^1.2.0",
"@egg/lark-msg-tool": "^1.4.0", "@egg/lark-msg-tool": "^1.5.0",
"@egg/logger": "^1.4.3", "@egg/logger": "^1.4.3",
"@egg/net-tool": "^1.9.1", "@egg/net-tool": "^1.9.1",
"@egg/path-tool": "^1.4.1", "@egg/path-tool": "^1.4.1",

View File

@ -12,9 +12,8 @@ const ACTION_MAP = {
/** /**
* *
* @param {Context.Data} ctx - body, larkService和logger * @param {Context.Data} ctx - body, larkService和logger
* @returns {Promise<void>}
*/ */
const manageClick = async (ctx: Context.Data): Promise<void> => { const manageAction = async (ctx: Context.Data) => {
const { body, logger } = ctx const { body, logger } = ctx
const { action } = body?.action?.value as { const { action } = body?.action?.value as {
action: keyof typeof ACTION_MAP action: keyof typeof ACTION_MAP
@ -23,15 +22,17 @@ const manageClick = async (ctx: Context.Data): Promise<void> => {
if (!action) return if (!action) return
const func = ACTION_MAP[action] const func = ACTION_MAP[action]
if (!func) return if (!func) return
func(ctx) return func(ctx)
} }
/** /**
* Action消息 * Action消息
* @param {Context.Data} ctx - * @param {Context.Data} ctx -
*/ */
export const manageActionMsg = (ctx: Context.Data) => { export const manageActionMsg = async (ctx: Context.Data) => {
const actionType = getActionType(ctx.body) const actionType = getActionType(ctx.body)
if (actionType === "button") manageClick(ctx) if (["button", "select_static"].includes(actionType)) return ctx.genResp.ok()
if (actionType === "select_static") manageClick(ctx) const card = await manageAction(ctx)
if (card) return ctx.genResp.custom(card)
return ctx.genResp.ok()
} }

View File

@ -0,0 +1,232 @@
const functionSelectorCard = {
elements: [
{
tag: "markdown",
content: "已经选中的群聊:**${chatName}**\n",
},
{
tag: "action",
actions: [
{
tag: "select_static",
placeholder: {
tag: "plain_text",
content: "请选择功能",
},
value: {
chatId: "${chatId}",
chatName: "${chatName}",
action: "sendTimeScopeSelector",
},
options: "${functions}",
},
],
},
{
tag: "hr",
},
{
tag: "note",
elements: [
{
tag: "plain_text",
content: "🍳 功能由AI创新应用组提供支持Rid${requestId}",
},
],
},
],
header: {
template: "purple",
title: {
content: "🧑‍💻 感谢使用 Group Agent",
tag: "plain_text",
},
},
}
const timeScopeSelectorCard = {
elements: [
{
tag: "markdown",
content:
"已经选中群聊:**${chatName}**\n\n已经选择功能**${functionName}**\n\n请选择时间范围",
},
{
tag: "action",
actions: [
{
tag: "button",
text: {
tag: "plain_text",
content: "过去一天",
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
timeScope: "1",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
{
tag: "button",
text: {
tag: "plain_text",
content: "过去三天",
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
timeScope: "3",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
{
tag: "button",
text: {
tag: "plain_text",
content: "过去七天",
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
timeScope: "7",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
],
},
{
tag: "hr",
},
{
tag: "note",
elements: [
{
tag: "plain_text",
content: "🍳 功能由AI创新应用组提供支持Rid${requestId}",
},
],
},
],
header: {
template: "purple",
title: {
content: "🧑‍💻 感谢使用 Group Agent",
tag: "plain_text",
},
},
}
const resultReportCard = {
elements: [
{
tag: "markdown",
content:
"群聊:**${chatName}**,功能:**${functionName}**,总结天数:**${timeScope}**\n\n以下内容由AI模型生成耗时**${processingTime}s**",
},
{
tag: "hr",
},
{
tag: "markdown",
content: "${llmRes}",
},
{
tag: "hr",
},
{
tag: "note",
elements: [
{
tag: "plain_text",
content: "🍳 功能由AI创新应用组提供支持Rid${requestId}",
},
],
},
],
header: {
template: "green",
title: {
content: "🧑‍💻 感谢使用 Group Agent",
tag: "plain_text",
},
},
}
const successMsgCard = {
elements: [
{
tag: "markdown",
content: "${content}",
},
{
tag: "hr",
},
{
tag: "note",
elements: [
{
tag: "plain_text",
content: "🍳 功能由${author}提供支持Rid${requestId}",
},
],
},
],
header: {
template: "green",
title: {
content: "🔥 感谢使用 ${app}",
tag: "plain_text",
},
},
}
const errorMsgCard = {
elements: [
{
tag: "markdown",
content: "${content}",
},
{
tag: "hr",
},
{
tag: "note",
elements: [
{
tag: "plain_text",
content: "🍳 功能由${author}提供支持Rid${requestId}",
},
],
},
],
header: {
template: "red",
title: {
content: "⛔ ${app} 错误提示",
tag: "plain_text",
},
},
}
const cardTemplateMap = {
functionSelector: functionSelectorCard,
timeScopeSelector: timeScopeSelectorCard,
resultReport: resultReportCard,
successMsg: successMsgCard,
errorMsg: errorMsgCard,
}
export default cardTemplateMap

View File

@ -1,4 +1,5 @@
import { import {
genCardMsg,
getActionOption, getActionOption,
getActionValue, getActionValue,
getChatId, getChatId,
@ -8,6 +9,7 @@ import {
import { Context } from "../../../types" import { Context } from "../../../types"
import llm from "../../../utils/llm" import llm from "../../../utils/llm"
import cardTemplateMap from "./cards"
enum CardTemplate { enum CardTemplate {
groupSelector = "ctp_AA0LgXOkJpIn", groupSelector = "ctp_AA0LgXOkJpIn",
@ -48,10 +50,10 @@ const sendGroupSelector = async ({
/** /**
* *
* @param ctx - body和larkService * @param ctx -
*/ */
const sendFunctionSelector = async (ctx: Context.Data) => { const sendFunctionSelector = async (ctx: Context.Data) => {
const { larkService, logger, requestId } = ctx const { logger, requestId } = ctx
const body = ctx.body as LarkAction.Data const body = ctx.body as LarkAction.Data
const option = getActionOption(body) const option = getActionOption(body)
logger.debug(`Action option: ${JSON.stringify(option)}`) logger.debug(`Action option: ${JSON.stringify(option)}`)
@ -60,7 +62,14 @@ const sendFunctionSelector = async (ctx: Context.Data) => {
logger.error( logger.error(
`Invalid targetChatId or targetChatName: ${JSON.stringify(option)}` `Invalid targetChatId or targetChatName: ${JSON.stringify(option)}`
) )
return return genCardMsg(
cardTemplateMap.errorMsg,
{
content: "Invalid targetChatId or targetChatName",
requestId,
},
false
)
} }
// 组织功能数据 // 组织功能数据
const functions = [ const functions = [
@ -68,25 +77,16 @@ const sendFunctionSelector = async (ctx: Context.Data) => {
text: "总结消息", text: "总结消息",
value: "summary-gpt-4o|总结消息", value: "summary-gpt-4o|总结消息",
}, },
// {
// text: "总结消息 (deepseek-chat)",
// value: "summary-deepseek-chat|总结消息 (deepseek-chat)",
// },
// {
// text: "总结消息 (qwen2-72b-instruct-int4)",
// value:
// "summary-qwen2-72b-instruct-int4|总结消息 (qwen2-72b-instruct-int4)",
// },
] ]
larkService.message.updateTemp( return genCardMsg(
getMessageId(body), cardTemplateMap.functionSelector,
CardTemplate.functionSelector,
{ {
functions, functions,
chatId, chatId,
chatName, chatName,
requestId, requestId,
} },
false
) )
} }
@ -95,58 +95,54 @@ const sendFunctionSelector = async (ctx: Context.Data) => {
* @param ctx - body和larkService * @param ctx - body和larkService
*/ */
const sendTimeScopeSelector = async (ctx: Context.Data) => { const sendTimeScopeSelector = async (ctx: Context.Data) => {
const { larkService, logger, requestId } = ctx const { logger, requestId } = ctx
const body = ctx.body as LarkAction.Data const body = ctx.body as LarkAction.Data
const option = getActionOption(body) const option = getActionOption(body)
logger.debug(`Action option: ${JSON.stringify(option)}`) logger.debug(`Action option: ${JSON.stringify(option)}`)
const [functionId, functionName] = option.split("|") const [functionId, functionName] = option.split("|")
if (!functionId || !functionName) { if (!functionId || !functionName) {
logger.error(`Invalid functionId or functionName: ${option}`) logger.error(`Invalid functionId or functionName: ${option}`)
return return genCardMsg(
cardTemplateMap.errorMsg,
{
content: "Invalid functionId or functionName",
requestId,
},
false
)
} }
const value = getActionValue(body) const value = getActionValue(body)
logger.debug(`Action value: ${JSON.stringify(value)}`) logger.debug(`Action value: ${JSON.stringify(value)}`)
const { chatId, chatName } = value const { chatId, chatName } = value
if (!chatName || !chatId) { if (!chatName || !chatId) {
logger.error(`Invalid chatName or chatId: ${JSON.stringify(value)}`) logger.error(`Invalid chatName or chatId: ${JSON.stringify(value)}`)
return return genCardMsg(
cardTemplateMap.errorMsg,
{
content: "Invalid chatName or chatId",
requestId,
},
false
)
} }
larkService.message.updateTemp( return genCardMsg(
getMessageId(body), cardTemplateMap.timeScopeSelector,
CardTemplate.timeScopeSelector,
{ {
chatId, chatId,
chatName, chatName,
functionId, functionId,
functionName, functionName,
requestId, requestId,
} },
false
) )
} }
/** const sendGroupReport = async (ctx: Context.Data) => {
*
* @param ctx - body和larkService
*/
const manageGroupMsg = async (ctx: Context.Data) => {
const { larkService, logger, requestId } = ctx const { larkService, logger, requestId } = ctx
const body = ctx.body as LarkAction.Data const body = ctx.body as LarkAction.Data
const value = getActionValue(body) const { chatId, chatName, functionId, functionName, timeScope } =
logger.debug(`Action value: ${JSON.stringify(value)}`) getActionValue(body)
const { chatId, chatName, functionId, functionName, timeScope } = value
if (!chatId || !chatName || !functionId || !functionName || !timeScope) {
logger.error(`Invalid value: ${JSON.stringify(value)}`)
return
}
// 发送一个loading的消息
await larkService.message.updateTemp(
getMessageId(body),
CardTemplate.successMsg,
{
content: "Group Agent 正在爬楼中,请稍等...",
requestId,
}
)
// 记录发送loading消息后的时间戳 // 记录发送loading消息后的时间戳
const startTime = Date.now() const startTime = Date.now()
@ -216,6 +212,40 @@ const manageGroupMsg = async (ctx: Context.Data) => {
} }
} }
/**
*
* @param ctx - body和larkService
*/
const manageGroupMsg = async (ctx: Context.Data) => {
const { logger, requestId } = ctx
const body = ctx.body as LarkAction.Data
const value = getActionValue(body)
logger.debug(`Action value: ${JSON.stringify(value)}`)
const { chatId, chatName, functionId, functionName, timeScope } = value
if (!chatId || !chatName || !functionId || !functionName || !timeScope) {
logger.error(`Invalid value: ${JSON.stringify(value)}`)
return genCardMsg(
cardTemplateMap.errorMsg,
{
content: "Invalid value",
requestId,
},
false
)
}
// 处理群组消息
sendGroupReport(ctx)
// 发送一个loading的消息
return genCardMsg(
cardTemplateMap.successMsg,
{
content: "Group Agent 正在爬楼中,请稍等...",
requestId,
},
false
)
}
const groupAgent = { const groupAgent = {
sendGroupSelector, sendGroupSelector,
sendFunctionSelector, sendFunctionSelector,

View File

@ -26,7 +26,7 @@ export const manageBotReq = async (ctx: Context.Data): Promise<Response> => {
// 处理Event消息 // 处理Event消息
if (getIsEventMsg(body)) manageEventMsg(ctx) if (getIsEventMsg(body)) manageEventMsg(ctx)
// 处理Action消息 // 处理Action消息
if (getIsActionMsg(body)) manageActionMsg(ctx) if (getIsActionMsg(body)) return await manageActionMsg(ctx)
// 返回成功响应 // 返回成功响应
return ctx.genResp.custom("{}") return ctx.genResp.custom("{}")

View File

@ -1,7 +1,7 @@
import { getChatId, getChatType, getUserId } from "@egg/lark-msg-tool" import { getChatId, getChatType, getUserId } from "@egg/lark-msg-tool"
import { Context, LarkServer } from "../../types" import { Context, LarkServer } from "../../types"
import { genSheetDbErrorMsg } from "../../utils/genMsg" import { genSheetDbErrorMsg } from "../../utils/larkMsg"
/** /**
* *
@ -101,7 +101,7 @@ const createFromEvent = async (ctx: Context.Data) => {
) )
} catch (e: any) { } catch (e: any) {
ctx.logger.error(`create KV bitable failed: ${e.message}`) ctx.logger.error(`create KV bitable failed: ${e.message}`)
const errorMsg = genSheetDbErrorMsg(e.message) const errorMsg = genSheetDbErrorMsg(e.message) as string
ctx.larkService.message.send("chat_id", chatId, "interactive", errorMsg) ctx.larkService.message.send("chat_id", chatId, "interactive", errorMsg)
} }
} }

View File

@ -1,5 +1,5 @@
import { LarkServer } from "../../types/larkServer" import { LarkServer } from "../../types/larkServer"
import { genTempMsg } from "../../utils/genMsg" import { genTempMsg } from "../../utils/larkMsg"
import LarkBaseService from "./base" import LarkBaseService from "./base"
class LarkMessageService extends LarkBaseService { class LarkMessageService extends LarkBaseService {

61
utils/Card/common.ts Normal file
View File

@ -0,0 +1,61 @@
export const commonNote = {
tag: "note",
elements: [
{
tag: "plain_text",
content: "🍳 功能由${xAuthor}提供支持Rid${requestId}",
},
],
}
export const successHeader = {
template: "green",
title: {
content: "🔥 感谢使用 ${xName}",
tag: "plain_text",
},
}
export const errorHeader = {
template: "red",
title: {
content: "⛔ ${xName} 错误提示",
tag: "plain_text",
},
}
export const pendingHeader = {
template: "purple",
title: {
content: "🔥 感谢使用 ${xName}",
tag: "plain_text",
},
}
export const baseSuccessCard = {
elements: [
{
tag: "markdown",
content: "${content}",
},
{
tag: "hr",
},
commonNote,
],
successHeader,
}
export const baseErrorCard = {
elements: [
{
tag: "markdown",
content: "${content}",
},
{
tag: "hr",
},
commonNote,
],
errorHeader,
}

View File

@ -0,0 +1,134 @@
import { commonNote, pendingHeader, successHeader } from "./common"
const functionSelector = {
elements: [
{
tag: "markdown",
content: "已经选中的群聊:**${chatName}**\n",
},
{
tag: "action",
actions: [
{
tag: "select_static",
placeholder: {
tag: "plain_text",
content: "请选择功能",
},
value: {
chatId: "${chatId}",
chatName: "${chatName}",
action: "sendTimeScopeSelector",
},
options: "${functions}",
},
],
},
{
tag: "hr",
},
commonNote,
],
pendingHeader,
}
const timeScopeSelector = {
elements: [
{
tag: "markdown",
content:
"已经选中群聊:**${chatName}**\n\n已经选择功能**${functionName}**\n\n请选择时间范围",
},
{
tag: "action",
actions: [
{
tag: "button",
text: {
tag: "plain_text",
content: "过去一天",
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
timeScope: "1",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
{
tag: "button",
text: {
tag: "plain_text",
content: "过去三天",
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
timeScope: "3",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
{
tag: "button",
text: {
tag: "plain_text",
content: "过去七天",
},
type: "default",
value: {
chatId: "${chatId}",
chatName: "${chatName}",
functionId: "${functionId}",
functionName: "${functionName}",
timeScope: "7",
requestId: "${requestId}",
action: "manageGroupMsg",
},
},
],
},
{
tag: "hr",
},
commonNote,
],
pendingHeader,
}
const resultReport = {
elements: [
{
tag: "markdown",
content:
"群聊:**${chatName}**,功能:**${functionName}**,总结天数:**${timeScope}**\n\n以下内容由AI模型生成耗时**${processingTime}s**",
},
{
tag: "hr",
},
{
tag: "markdown",
content: "${llmRes}",
},
{
tag: "hr",
},
commonNote,
],
successHeader,
}
const groupAgentCard = {
functionSelector,
timeScopeSelector,
resultReport,
}
export default groupAgentCard

32
utils/Card/index.ts Normal file
View File

@ -0,0 +1,32 @@
class LarkCard {
private requestId: string
private xName: string
private xAuthor: string
private functionMap = {
egg: {
name: "小煎蛋",
author: "zhaoyingbo",
},
groupAgent: {
name: "Group Agent",
author: "AI创新应用组",
},
sheetDB: {
name: "小煎蛋 Sheet DB",
author: "zhaoyingbo",
},
}
constructor(func: keyof typeof this.functionMap, requestId: string) {
this.requestId = requestId
this.xName = this.functionMap[func].name
this.xAuthor = this.functionMap[func].author
}
child(func: keyof typeof this.functionMap) {
return new LarkCard(func, this.requestId)
}
}
export default LarkCard

View File

@ -1,96 +0,0 @@
/**
* JSON
* @param {string} title -
* @param {string} content -
* @returns {string} JSON
*/
const genErrorMsg = (title: string, content: string) =>
JSON.stringify({
elements: [
{
tag: "markdown",
content,
},
],
header: {
title: {
content: title,
tag: "plain_text",
},
template: "red",
},
})
/**
* JSON
* @param {string} title -
* @param {string} content -
* @returns {string} JSON
*/
const genSuccessMsg = (title: string, content: string) =>
JSON.stringify({
elements: [
{
tag: "markdown",
content,
},
],
header: {
title: {
content: title,
tag: "plain_text",
},
template: "green",
},
})
/**
* JSON
* @param {string} id - ID
* @param {any} variable -
* @returns {string} JSON
*/
export const genTempMsg = (id: string, variable: any) =>
JSON.stringify({
type: "template",
data: {
config: {
update_multi: true,
enable_forward: false,
},
template_id: id,
template_variable: variable,
},
})
/**
* Sheet DB JSON
* @param {string} content -
* @returns {string} JSON
*/
export const genSheetDbErrorMsg = (content: string) =>
genErrorMsg("🍪 小煎蛋 Sheet DB 错误提醒", content)
/**
* Sheet DB JSON
* @param {string} content -
* @returns {string} JSON
*/
export const genSheetDbSuccessMsg = (content: string) =>
genSuccessMsg("🍪 感谢使用小煎蛋 Sheet DB", content)
/**
* Group Agent JSON
* @param {string} content -
* @returns {string} JSON
*/
export const genGroupAgentErrorMsg = (content: string) =>
genErrorMsg("🧑‍💻 Group Agent 错误提醒", content)
/**
* Group Agent JSON
* @param {string} content -
* @returns {string} JSON
*/
export const genGroupAgentSuccessMsg = (content: string) =>
genSuccessMsg("🧑‍💻 感谢使用 Group Agent", content)

129
utils/larkMsg.ts Normal file
View File

@ -0,0 +1,129 @@
/**
* JSON
* @param {string} title -
* @param {string} content -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
const genErrorMsg = (
title: string,
content: string,
stringify: boolean = true
) => {
const msg = {
elements: [
{
tag: "markdown",
content,
},
],
header: {
title: {
content: title,
tag: "plain_text",
},
template: "red",
},
}
return stringify ? JSON.stringify(msg) : msg
}
/**
* JSON
* @param {string} title -
* @param {string} content -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
const genSuccessMsg = (
title: string,
content: string,
stringify: boolean = true
) => {
const msg = {
elements: [
{
tag: "markdown",
content,
},
],
header: {
title: {
content: title,
tag: "plain_text",
},
template: "green",
},
}
return stringify ? JSON.stringify(msg) : msg
}
/**
* JSON
* @param {string} id - ID
* @param {any} variable -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
export const genTempMsg = (
id: string,
variable: any,
stringify: boolean = true
) => {
const msg = {
type: "template",
data: {
config: {
update_multi: true,
enable_forward: false,
},
template_id: id,
template_variable: variable,
},
}
return stringify ? JSON.stringify(msg) : msg
}
/**
* Sheet DB JSON
* @param {string} content -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
export const genSheetDbErrorMsg = (
content: string,
stringify: boolean = true
) => genErrorMsg("🍪 小煎蛋 Sheet DB 错误提醒", content, stringify)
/**
* Sheet DB JSON
* @param {string} content -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
export const genSheetDbSuccessMsg = (
content: string,
stringify: boolean = true
) => genSuccessMsg("🍪 感谢使用小煎蛋 Sheet DB", content, stringify)
/**
* Group Agent JSON
* @param {string} content -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
export const genGroupAgentErrorMsg = (
content: string,
stringify: boolean = true
) => genErrorMsg("🧑‍💻 Group Agent 错误提醒", content, stringify)
/**
* Group Agent JSON
* @param {string} content -
* @param {boolean} [stringify=true] - JSON
* @returns {string | object} JSON
*/
export const genGroupAgentSuccessMsg = (
content: string,
stringify: boolean = true
) => genSuccessMsg("🧑‍💻 感谢使用 Group Agent", content, stringify)