From e695a5836c55af83a2eaff4152eb6914e797d206 Mon Sep 17 00:00:00 2001 From: zhaoyingbo Date: Tue, 28 Jan 2025 13:01:39 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=80=BB=E7=BB=93=E7=94=A8=E7=9A=84sheet?= =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=8C=E5=90=8C=E6=97=B6=E6=94=AF=E6=8C=81?= =?UTF-8?q?wiiki?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lockb | Bin 168497 -> 166839 bytes controller/intentAgent/index.ts | 24 +++++-- controller/reportAgent/index.ts | 118 ++++++++++++++++++++++++-------- db/chat/index.ts | 29 ++++++-- db/index.ts | 2 + db/sheet/index.ts | 44 ++++++++++++ package.json | 6 +- routes/bot/eventMsg.ts | 8 ++- services/attach/index.ts | 2 +- test/reportAgent/setUrl.http | 4 ++ utils/string.ts | 10 +-- 11 files changed, 199 insertions(+), 48 deletions(-) create mode 100644 db/sheet/index.ts create mode 100644 test/reportAgent/setUrl.http diff --git a/bun.lockb b/bun.lockb index 4949ab6625ad9c72513e0979ab9a57c11523df0b..244d5e1e21b946c1014e468d16cd56dbe0bf249f 100755 GIT binary patch delta 8235 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 delta 8383 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 type Briefing = z.infer +type BriefingLink = z.infer type CommonResponse = z.infer export type Intent = z.infer @@ -93,20 +100,24 @@ const agent = async (ctx: Context): Promise => { } return { - intent: 13, + intent: 1, } } const isBaseIntent = (intent: Intent): intent is BaseIntent => { - return intent.intent !== 3 && intent.intent !== 12 + return intent.intent !== 5 && intent.intent !== 2 } const isBriefing = (intent: Intent): intent is Briefing => { - return intent.intent === 3 + return intent.intent === 5 +} + +const isBriefingLink = (intent: Intent): intent is BriefingLink => { + return intent.intent === 14 } const isCommonResponse = (intent: Intent): intent is CommonResponse => { - return intent.intent === 12 + return intent.intent === 2 } /** @@ -117,6 +128,7 @@ const intentAgent = { isBaseIntent, isBriefing, isCommonResponse, + isBriefingLink, } export default intentAgent diff --git a/controller/reportAgent/index.ts b/controller/reportAgent/index.ts index d6943f6..5168fe4 100644 --- a/controller/reportAgent/index.ts +++ b/controller/reportAgent/index.ts @@ -1,4 +1,5 @@ import db from "../../db" +import { SheetModel } from "../../db/sheet" import { Context } from "../../types" import llm from "../../utils/llm" import { extractSheetIds, validateLink } from "../../utils/string" @@ -65,35 +66,11 @@ const insert2Sheet = async ( } = ctx try { const chat = await db.chat.getAndCreate(ctx) - if (!chat?.webSummarySheetLink) { - logger.info("No webSummarySheetLink found, skip insert2Sheet") + if (!chat || !chat?.sheet || !chat?.expand?.sheet) { + logger.info("No sheet found, skip insert2Sheet") return "" } - let sheetToken = "" - let range = "" - if (chat.webSummarySheetLink.includes("wiki")) { - const extractRes = extractSheetIds(chat.webSummarySheetLink) - if (!extractRes) { - logger.error("Failed to extract sheetToken and range") - return "" - } - const wikiData = await larkService.wiki.getNodeInfo(extractRes.sheetToken) - if (!wikiData || wikiData.code) { - logger.error("Failed to get wiki data") - return "" - } - sheetToken = wikiData.data.obj_token - range = extractRes.range - } else { - const extractRes = extractSheetIds(chat.webSummarySheetLink) - if (!extractRes) { - logger.error("Failed to extract sheetToken and range") - return "" - } - sheetToken = extractRes.sheetToken - range = extractRes.range - } - + const { sheetToken, range, sheetUrl } = chat.expand.sheet as SheetModel await larkService.sheet.insertRows(sheetToken, range, [ [ userId || "", @@ -103,7 +80,7 @@ const insert2Sheet = async ( new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }), ], ]) - return chat.webSummarySheetLink + return sheetUrl } catch (error: any) { logger.error(`Failed to insert2Sheet: ${error}`) return "" @@ -191,8 +168,93 @@ const agent = async (ctx: Context, link: string, userDescription: string) => { } } +/** + * 解析表格信息 + * @param {Context} ctx - 上下文对象 + * @param {string} sheetUrl - 表格链接 + * @returns {Promise<{sheetToken: string, range: string}>} - 返回表格信息 + * @throws {Error} - 当解析失败时抛出错误 + */ +const parseSheetInfo = async ( + ctx: Context, + sheetUrl: string +): Promise<{ sheetToken: string; range: string }> => { + const { larkService } = ctx + const token = extractSheetIds(sheetUrl) + if (!token || !token.sheetToken) throw new Error("链接格式错误") + + const getRange = async (sheetToken: string) => { + const res = await larkService.sheet.getMetaInfo(sheetToken) + if (!res || !res?.data?.sheets?.length) + throw new Error("获取工作表信息失败") + return res.data.sheets[0].sheetId + } + // 如果不是Wiki链接,直接提取sheetToken和range + if (!sheetUrl.includes("wiki")) { + if (token.range) return token as { sheetToken: string; range: string } + // 获取第一个工作表作为range + token.range = await getRange(token.sheetToken) + return token as { sheetToken: string; range: string } + } + // 如果是Wiki链接,需要先获取sheetToken + const res = await larkService.wiki.getNodeInfo(token?.sheetToken) + if (!res || !res?.data?.node?.obj_token) throw new Error("获取Wiki信息失败") + token.sheetToken = res.data.node.obj_token + if (token.range) return token as { sheetToken: string; range: string } + token.range = await getRange(token.sheetToken) + return token as { sheetToken: string; range: string } +} + +/** + * 设置简报表格 + * @param {Context} ctx - 上下文对象 + * @param {string} sheetUrl - 表格链接 + */ +const setSheet = async (ctx: Context, sheetUrl: string) => { + const { larkCard, larkService, logger } = ctx + const cardGender = larkCard.child("webAgent") + try { + // 获取chat信息 + const chat = await db.chat.getAndCreate(ctx) + if (!chat) throw new Error("获取聊天信息失败") + // 获取是否已经有存在的sheet信息 + const existSheet = await db.sheet.getByUrl(sheetUrl) + // 如果已经存在,则直接写入chat表 + if (existSheet) { + const updateRes = await db.chat.update(chat.id, { sheet: existSheet.id }) + if (!updateRes) throw new Error("更新chat记录失败") + await larkService.message.updateOrReply( + cardGender.genSuccessCard("简报汇总表设置成功") + ) + return + } + + // 获取sheet的token和range + const sheetInfo = await parseSheetInfo(ctx, sheetUrl) + + // 创建sheet记录 + const createRes = await db.sheet.create({ + sheetToken: sheetInfo.sheetToken, + range: sheetInfo.range, + sheetUrl, + }) + if (!createRes) throw new Error("创建sheet记录失败") + const updateRes = await db.chat.update(chat.id, { sheet: createRes.id }) + if (!updateRes) throw new Error("更新chat记录失败") + await larkService.message.updateOrReply( + cardGender.genSuccessCard("简报汇总表创建成功") + ) + } catch (error: any) { + logger.error(`Failed setSheet: ${error}`) + await larkService.message.updateOrReply( + cardGender.genErrorCard(`简报汇总表设置失败: ${error.message}`) + ) + } +} + const reportAgent = { agent, + setSheet, // 添加 setSheet 到导出的对象中 } export default reportAgent diff --git a/db/chat/index.ts b/db/chat/index.ts index 638b0b1..a7a16cb 100644 --- a/db/chat/index.ts +++ b/db/chat/index.ts @@ -3,6 +3,7 @@ import { RecordModel } from "pocketbase" import { Context } from "../../types" import { managePbError } from "../../utils/pbTools" import pbClient from "../pbClient" +import { SheetModel } from "../sheet" const DB_NAME = "chat" @@ -13,19 +14,29 @@ export interface Chat { mode: "group" | "p2p" | "topic" weeklySummary: boolean dailySummary: boolean - webSummarySheetLink: string + sheet: string } export type ChatModel = Chat & RecordModel +export interface ChatModelWithExpand extends ChatModel { + expand: { + sheet: SheetModel + } +} + /** * 获取单个群组信息 * @param id * @returns */ const getOneByChatId = (chatId: string) => - managePbError(() => - pbClient.collection(DB_NAME).getFirstListItem(`chatId = "${chatId}"`) + managePbError(() => + pbClient + .collection(DB_NAME) + .getFirstListItem(`chatId = "${chatId}"`, { + expand: "sheet", + }) ) /** @@ -61,11 +72,20 @@ const getAndCreate = async ({ larkService, logger, larkBody }: Context) => { mode: chat_mode, weeklySummary: false, dailySummary: false, - webSummarySheetLink: "", + sheet: "", } return create(newChat) } +/** + * 更新群组 + * @param id + * @param chat + * @returns + */ +const update = (id: string, chat: Partial) => + managePbError(() => pbClient.collection(DB_NAME).update(id, chat)) + /** * 更新群组总结 * @param id @@ -99,6 +119,7 @@ const getNeedSummaryChats = async (timeScope: "daily" | "weekly" | "all") => { } const chat = { + update, getAndCreate, getOneByChatId, updateSummary, diff --git a/db/index.ts b/db/index.ts index bba1bf3..2989840 100644 --- a/db/index.ts +++ b/db/index.ts @@ -4,6 +4,7 @@ import gitlabProject from "./gitlabProject/index." import grpSumLog from "./grpSumLog" import log from "./log" import receiveGroup from "./receiveGroup" +import sheet from "./sheet" import soupGame from "./soupGame" import user from "./user" @@ -16,6 +17,7 @@ const db = { grpSumLog, gitlabProject, soupGame, + sheet, } export default db diff --git a/db/sheet/index.ts b/db/sheet/index.ts new file mode 100644 index 0000000..f46062a --- /dev/null +++ b/db/sheet/index.ts @@ -0,0 +1,44 @@ +import { RecordModel } from "pocketbase" + +import { managePbError } from "../../utils/pbTools" +import pbClient from "../pbClient" + +const DB_NAME = "sheet" + +export interface Sheet { + /*** + * 表格区间 + */ + range: string + /*** + * 表格Token + */ + sheetToken: string + /*** + * 表格链接 + */ + sheetUrl: string +} + +export type SheetModel = Sheet & RecordModel + +const getByUrl = (sheetUrl: string) => + managePbError(() => + pbClient.collection(DB_NAME).getFirstListItem(`sheetUrl = "${sheetUrl}"`) + ) + +const update = (id: string, sheet: Partial) => + managePbError(() => + pbClient.collection(DB_NAME).update(id, sheet) + ) + +const create = (sheet: Sheet) => + managePbError(() => pbClient.collection(DB_NAME).create(sheet)) + +const sheet = { + getByUrl, + update, + create, +} + +export default sheet diff --git a/package.json b/package.json index 16d4d46..ac06a14 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@eslint/js": "^9.19.0", "@types/node-schedule": "^2.1.7", "@types/uuid": "^10.0.0", - "bun-types": "^1.2.0", + "bun-types": "^1.2.1", "eslint": "^9.19.0", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-unused-imports": "^4.1.4", @@ -30,7 +30,7 @@ "lint-staged": "^15.4.3", "oxlint": "^0.13.2", "prettier": "^3.4.2", - "typescript-eslint": "^8.21.0" + "typescript-eslint": "^8.22.0" }, "peerDependencies": { "typescript": "^5.5.4" @@ -39,7 +39,7 @@ "@egg/hooks": "^1.2.0", "@egg/lark-msg-tool": "^1.21.0", "@egg/logger": "^1.6.0", - "@egg/net-tool": "^1.30.2", + "@egg/net-tool": "^1.31.1", "@egg/path-tool": "^1.4.1", "@langchain/core": "^0.3.36", "@langchain/langgraph": "^0.2.41", diff --git a/routes/bot/eventMsg.ts b/routes/bot/eventMsg.ts index 0e1d3bd..0b2bd32 100644 --- a/routes/bot/eventMsg.ts +++ b/routes/bot/eventMsg.ts @@ -83,7 +83,13 @@ const manageIntent = async (ctx: Context) => { const intentRes = await intentAgent.agent(ctx) logger.info(`intentRes: ${JSON.stringify(intentRes)}`) if (intentAgent.isBriefing(intentRes)) { - reportAgent.agent(ctx, intentRes.link, intentRes.userDescription) + reportAgent + .agent(ctx, intentRes.link, intentRes.userDescription) + .catch(() => null) + return + } + if (intentAgent.isBriefingLink(intentRes)) { + reportAgent.setSheet(ctx, intentRes.link).catch(() => null) return } if (intentAgent.isCommonResponse(intentRes)) { diff --git a/services/attach/index.ts b/services/attach/index.ts index e58423f..09d6b37 100644 --- a/services/attach/index.ts +++ b/services/attach/index.ts @@ -93,7 +93,7 @@ class AttachService extends NetToolBase { * @returns {Promise} 返回抓取的网页内容。 */ async crawlWeb(url: string) { - const URL = "https://lark-egg.ai.xiaomi.com/tools/web/crawl" + const URL = "https://lark-egg.ai.xiaomi.com/tools/web/crawler" return this.get>(URL, { url }).catch(() => null) } } diff --git a/test/reportAgent/setUrl.http b/test/reportAgent/setUrl.http new file mode 100644 index 0000000..af5ad4d --- /dev/null +++ b/test/reportAgent/setUrl.http @@ -0,0 +1,4 @@ +POST http://localhost:3000/bot?app=egg HTTP/1.1 +content-type: application/json + +{"schema":"2.0","header":{"event_id":"c94518fbcb9d66cc93b4144cc69e4f0c","token":"tV9djUKSjzVnekV7xTg2Od06NFTcsBnj","create_time":"1732612280153","event_type":"im.message.receive_v1","tenant_key":"2ee61fe50f4f1657","app_id":"cli_a1eff35b43b89063"},"event":{"message":{"chat_id":"oc_8c789ce8f4ecc6695bb63ca6ec4c61ea","chat_type":"group","content":"{\"text\":\"@_user_1 https://xiaomi.f.mioffice.cn/wiki/V4ZkwhDR8iRCqIk7X81k1rBj4Sc?sheet=a7a0f7\"}","create_time":"1732612279943","mentions":[{"id":{"open_id":"ou_032f507d08f9a7f28b042fcd086daef5","union_id":"on_7111660fddd8302ce47bf1999147c011","user_id":""},"key":"@_user_1","name":"小煎蛋","tenant_key":"2ee61fe50f4f1657"}],"message_id":"om_4ab57cb6e889eeb81ca061b137238189","message_type":"text","update_time":"1732612279943"},"sender":{"sender_id":{"open_id":"ou_470ac13b8b50fc472d9d8ee71e03de26","union_id":"on_9dacc59a539023df8b168492f5e5433c","user_id":"zhaoyingbo"},"sender_type":"user","tenant_key":"2ee61fe50f4f1657"}}} \ No newline at end of file diff --git a/utils/string.ts b/utils/string.ts index db6c8a3..e24ed49 100644 --- a/utils/string.ts +++ b/utils/string.ts @@ -2,17 +2,17 @@ * 从小米链接中提取 sheetToken 和 range。 * * @param {string} url - 要提取数据的URL。 - * @returns {{sheetToken: string, range: string} | null} - 包含 sheetToken 和 range 的对象,如果没有匹配则返回 null。 + * @returns {{sheetToken: string, range: string | null} | null} - 包含 sheetToken 和 range 的对象,如果没有匹配则返回 null。 */ export const extractSheetIds = (url: string) => { // 定义匹配 wiki 和 sheets 两种URL格式的正则表达式 - const pattern = - /wiki\/([\w\d]+)\?sheet=([\w\d]+)|sheets\/([\w\d]+)\?sheet=([\w\d]+)/ + const pattern = /(?:wiki|sheets)\/([\w\d]+)(?:\?sheet=([\w\d]+))?/ const match = url.match(pattern) + console.log("🚀 ~ extractSheetIds ~ match:", match) if (match) { return { - sheetToken: match[1] || match[3], // 对于第一种URL格式,sheetToken 在组1;对于第二种格式,在组3 - range: match[2] || match[4], // range 在第一种URL格式中是组2,在第二种格式是组4 + sheetToken: match[1], // sheetToken 在组1 + range: match[2] || null, // range 在组2,如果没有则返回 null } } return null // 如果没有匹配,则返回 null