@lih-x-x/kmr 1.0.32 → 1.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/dist/{chunk-RYCZGTCI.js → chunk-KAWJNGXR.js} +0 -3
- package/dist/{chunk-EIQS5WF7.js → chunk-WMK5MYOU.js} +0 -3
- package/dist/{chunk-4KKCJLHW.js → chunk-XPW2DGJX.js} +0 -11
- package/dist/{chunk-KK3DMZOA.js → chunk-XQVLJTP4.js} +0 -2
- package/dist/{claudeCode-HNZS3G7L.js → claudeCode-2OTK6CH4.js} +1 -1
- package/dist/cli.js +16 -16
- package/dist/{client-CKWRSYEN.js → client-IYEXZKNP.js} +1 -1
- package/dist/index.js +90 -40
- package/dist/{sync-E4SW7QFZ.js → sync-FJA2I3HE.js} +1 -1
- package/dist/{taskCreator-QVSOLXXU.js → taskCreator-TCI3VB5D.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
- **会议纪要提取**:发送飞书云文档链接给机器人,自动提取会议摘要、待办事项、风险项、关键共识、可复用知识
|
|
8
8
|
- **飞书任务创建**:提取待办后自动发送确认消息,回复 `/confirm` 创建飞书任务,`/reject` 取消,3 分钟超时自动取消。自动通过群成员列表解析负责人并指派任务
|
|
9
|
+
- **任务状态同步**:每小时自动从飞书拉取任务最新状态,查询时也会实时刷新。通过 `/task` 指令或 `kmr task` 命令查看所有任务详情
|
|
9
10
|
- **AI 对话**:发送非指令文本即可与 AI 自由对话,基于 acpx 持久化 session,按用户隔离,支持 claude 和 codex 两种 agent
|
|
10
11
|
- **历史会议查询**:通过 `/find` 指令用自然语言搜索历史会议记录
|
|
11
|
-
- **记录管理**:`/list` 列出所有记录,`/show <id>` 查看详情,`/del <id>`
|
|
12
|
-
-
|
|
12
|
+
- **记录管理**:`/list` 列出所有记录,`/show <id>` 查看详情,`/del <id>` 删除记录,`/task` 查看所有任务
|
|
13
|
+
- **CLI 命令**:`kmr list`、`kmr show`、`kmr task`、`kmr del`、`kmr find` 等子命令可在终端直接操作,无需启动服务
|
|
14
|
+
- **群聊智能识别**:群聊中非 @消息仅自动识别会议纪要文档链接(通过文档标题判断),其他操作需 @机器人
|
|
15
|
+
- **管理员通知**:配置 `adminOpenId` 后,服务启动和停止时自动向管理员推送通知,每天 9:30 推送任务摘要(错过则启动时补推)
|
|
16
|
+
- **版本更新检查**:启动时自动检测 npm 新版本并提示更新
|
|
13
17
|
- **本地持久化**:所有数据以 JSON 文件存储在 `~/.kmr/` 目录下,支持 grep 检索
|
|
14
18
|
- **Web 管理界面**:服务启动时自动打开浏览器,查看会议记录、AI 会话、管理配置
|
|
15
19
|
|
|
@@ -88,7 +92,8 @@ npm run dev # 启动服务
|
|
|
88
92
|
{
|
|
89
93
|
"lark": {
|
|
90
94
|
"appId": "你的飞书应用 App ID",
|
|
91
|
-
"appSecret": "你的飞书应用 App Secret"
|
|
95
|
+
"appSecret": "你的飞书应用 App Secret",
|
|
96
|
+
"adminOpenId": "管理员的 open_id(可选,用于接收服务启停通知)"
|
|
92
97
|
},
|
|
93
98
|
"agent": {
|
|
94
99
|
"provider": "claude",
|
|
@@ -137,6 +142,20 @@ https://xxx.feishu.cn/docx/abc123
|
|
|
137
142
|
|
|
138
143
|
/del meeting_1714380000 # 删除某条记录
|
|
139
144
|
删掉 meeting_1714380000 # 自然语言同样支持
|
|
145
|
+
|
|
146
|
+
/task # 查看所有已创建任务的状态
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### CLI 命令
|
|
150
|
+
|
|
151
|
+
无需启动服务,直接在终端操作:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
kmr list # 列出所有会议记录
|
|
155
|
+
kmr show <id> # 查看记录详情(JSON)
|
|
156
|
+
kmr task # 查看所有任务状态
|
|
157
|
+
kmr del <id> # 删除记录
|
|
158
|
+
kmr find <query> # 搜索会议记录
|
|
140
159
|
```
|
|
141
160
|
|
|
142
161
|
### 创建飞书任务
|
|
@@ -228,6 +247,8 @@ kmr/
|
|
|
228
247
|
│ │ └── app.js
|
|
229
248
|
│ ├── utils/
|
|
230
249
|
│ │ └── claudeEnv.ts # ~/.claude/settings.json 环境变量加载
|
|
250
|
+
│ ├── task/
|
|
251
|
+
│ │ └── sync.ts # 飞书任务状态同步
|
|
231
252
|
│ └── storage/
|
|
232
253
|
│ ├── types.ts # 数据类型定义
|
|
233
254
|
│ └── jsonStore.ts # JSON 文件存储
|
|
@@ -28,12 +28,10 @@ var TaskCreator = class {
|
|
|
28
28
|
];
|
|
29
29
|
}
|
|
30
30
|
console.log(`[task] \u521B\u5EFA\u98DE\u4E66\u4EFB\u52A1: ${params.summary}`);
|
|
31
|
-
console.log(`[task] \u8BF7\u6C42\u4F53:`, JSON.stringify(taskBody, null, 2));
|
|
32
31
|
const response = await this.client.task.v2.task.create({
|
|
33
32
|
params: { user_id_type: "open_id" },
|
|
34
33
|
data: taskBody
|
|
35
34
|
});
|
|
36
|
-
console.log(`[task] API \u8FD4\u56DE:`, JSON.stringify(response.data, null, 2));
|
|
37
35
|
const taskId = response.data?.task?.guid || "";
|
|
38
36
|
const url = response.data?.task?.url || "";
|
|
39
37
|
console.log(`[task] \u4EFB\u52A1\u521B\u5EFA\u6210\u529F: taskId=${taskId}`);
|
|
@@ -49,7 +47,6 @@ var TaskCreator = class {
|
|
|
49
47
|
});
|
|
50
48
|
const task = response.data?.task;
|
|
51
49
|
if (!task) return null;
|
|
52
|
-
console.log(`[task-sync] \u4EFB\u52A1 ${taskId} \u72B6\u6001: completed_at="${task.completed_at}"`);
|
|
53
50
|
const completedAt = task.completed_at || "";
|
|
54
51
|
const ts = parseInt(completedAt, 10);
|
|
55
52
|
const isCompleted = ts > 0;
|
|
@@ -81,9 +81,6 @@ var ClaudeCodeProvider = class {
|
|
|
81
81
|
name = "claude";
|
|
82
82
|
async extract(content) {
|
|
83
83
|
const prompt = EXTRACT_PROMPT + content;
|
|
84
|
-
console.log(`[ClaudeCodeProvider] \u63D0\u53D6\u4FE1\u606F\u7684 prompt:
|
|
85
|
-
${prompt}
|
|
86
|
-
--- End of Prompt ---`);
|
|
87
84
|
const output = await this.callAcpx(prompt);
|
|
88
85
|
const parsed = JSON.parse(output);
|
|
89
86
|
return {
|
|
@@ -13,7 +13,6 @@ async function getBotOpenId(client) {
|
|
|
13
13
|
method: "GET",
|
|
14
14
|
url: "/open-apis/bot/v3/info"
|
|
15
15
|
});
|
|
16
|
-
console.log(`[bot] bot info response:`, JSON.stringify(res?.data || res, null, 2));
|
|
17
16
|
const openId = res.data?.bot?.open_id || res.bot?.open_id || "";
|
|
18
17
|
if (openId) {
|
|
19
18
|
console.log(`[bot] \u673A\u5668\u4EBA open_id: ${openId}`);
|
|
@@ -31,26 +30,21 @@ function createEventDispatcher(botOpenId, onMessage) {
|
|
|
31
30
|
const processedMessages = /* @__PURE__ */ new Set();
|
|
32
31
|
dispatcher.register({
|
|
33
32
|
"im.message.receive_v1": async (data) => {
|
|
34
|
-
console.log(`[recv] \u6536\u5230\u98DE\u4E66\u4E8B\u4EF6:`, JSON.stringify(data, null, 2));
|
|
35
33
|
const message = data.message;
|
|
36
34
|
if (message.message_type !== "text") {
|
|
37
|
-
console.log(`[recv] \u5FFD\u7565\u975E\u6587\u672C\u6D88\u606F, type=${message.message_type}`);
|
|
38
35
|
return;
|
|
39
36
|
}
|
|
40
37
|
const messageId = message.message_id;
|
|
41
38
|
const senderType = data.sender?.sender_type;
|
|
42
39
|
if (senderType === "app") {
|
|
43
|
-
console.log(`[recv] \u5FFD\u7565\u673A\u5668\u4EBA\u81EA\u8EAB\u6D88\u606F: ${messageId}`);
|
|
44
40
|
return;
|
|
45
41
|
}
|
|
46
42
|
if (processedMessages.has(messageId)) {
|
|
47
|
-
console.log(`[dedup] \u8DF3\u8FC7\u91CD\u590D\u6D88\u606F: ${messageId}`);
|
|
48
43
|
return;
|
|
49
44
|
}
|
|
50
45
|
processedMessages.add(messageId);
|
|
51
46
|
const createTime = parseInt(message.create_time, 10);
|
|
52
47
|
if (createTime && Date.now() - createTime > 2 * 60 * 1e3) {
|
|
53
|
-
console.log(`[dedup] \u8DF3\u8FC7\u8FC7\u671F\u6D88\u606F: ${messageId}, \u521B\u5EFA\u4E8E ${new Date(createTime).toISOString()}`);
|
|
54
48
|
return;
|
|
55
49
|
}
|
|
56
50
|
if (processedMessages.size > 1e3) {
|
|
@@ -61,9 +55,6 @@ function createEventDispatcher(botOpenId, onMessage) {
|
|
|
61
55
|
let text = content.text || "";
|
|
62
56
|
const chatId = message.chat_id;
|
|
63
57
|
const mentions = message.mentions || [];
|
|
64
|
-
if (mentions.length > 0) {
|
|
65
|
-
console.log(`[recv] mentions:`, JSON.stringify(mentions));
|
|
66
|
-
}
|
|
67
58
|
const isMentioned = mentions.length > 0 && (!botOpenId || mentions.some((m) => {
|
|
68
59
|
const mentionOpenId = m.id?.open_id || m.id_str || "";
|
|
69
60
|
return mentionOpenId === botOpenId;
|
|
@@ -79,11 +70,9 @@ function createEventDispatcher(botOpenId, onMessage) {
|
|
|
79
70
|
if (isGroup && !isMentioned) {
|
|
80
71
|
const hasDocLink = /(https?:\/\/[^\s]*feishu\.cn\/[^\s]+)/.test(text);
|
|
81
72
|
if (!hasDocLink) {
|
|
82
|
-
console.log(`[recv] \u7FA4\u804A\u975E@\u673A\u5668\u4EBA\u6D88\u606F\u4E14\u975E\u6587\u6863\u94FE\u63A5, \u5FFD\u7565`);
|
|
83
73
|
return;
|
|
84
74
|
}
|
|
85
75
|
}
|
|
86
|
-
console.log(`[recv] \u89E3\u6790\u6D88\u606F: messageId=${messageId}, chatId=${chatId}, chatType=${message.chat_type}, text="${text}"`);
|
|
87
76
|
const senderId = data.sender?.sender_id?.open_id || "unknown";
|
|
88
77
|
await onMessage(messageId, text, chatId, senderId, { isGroup, isMentioned });
|
|
89
78
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// src/task/sync.ts
|
|
2
2
|
async function syncTaskStatuses(store, taskCreator) {
|
|
3
|
-
console.log("[task-sync] \u5F00\u59CB\u540C\u6B65\u98DE\u4E66\u4EFB\u52A1\u72B6\u6001...");
|
|
4
3
|
try {
|
|
5
4
|
const meetings = await store.list();
|
|
6
5
|
let updatedCount = 0;
|
|
@@ -24,7 +23,6 @@ async function syncTaskStatuses(store, taskCreator) {
|
|
|
24
23
|
updatedCount++;
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
|
-
console.log(`[task-sync] \u540C\u6B65\u5B8C\u6210, \u66F4\u65B0\u4E86 ${updatedCount} \u6761\u4F1A\u8BAE\u8BB0\u5F55`);
|
|
28
26
|
} catch (err) {
|
|
29
27
|
console.error("[task-sync] \u540C\u6B65\u5931\u8D25:", err.message);
|
|
30
28
|
}
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,9 @@ async function checkUpdate() {
|
|
|
6
6
|
try {
|
|
7
7
|
const res = await fetch(`https://registry.npmjs.org/${"@lih-x-x/kmr"}/latest`, { signal: AbortSignal.timeout(3e3) });
|
|
8
8
|
const data = await res.json();
|
|
9
|
-
if (data.version && data.version !== "1.0.
|
|
9
|
+
if (data.version && data.version !== "1.0.34") {
|
|
10
10
|
console.log(`
|
|
11
|
-
\u2B06\uFE0F \u65B0\u7248\u672C\u53EF\u7528: ${"1.0.
|
|
11
|
+
\u2B06\uFE0F \u65B0\u7248\u672C\u53EF\u7528: ${"1.0.34"} \u2192 ${data.version}`);
|
|
12
12
|
console.log(` \u8FD0\u884C npm install -g ${"@lih-x-x/kmr"} \u66F4\u65B0
|
|
13
13
|
`);
|
|
14
14
|
}
|
|
@@ -55,13 +55,13 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
55
55
|
kmr --help \u663E\u793A\u5E2E\u52A9
|
|
56
56
|
`);
|
|
57
57
|
} else if (command === "--version" || command === "-v") {
|
|
58
|
-
console.log("1.0.
|
|
58
|
+
console.log("1.0.34");
|
|
59
59
|
} else if (command === "list") {
|
|
60
60
|
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
61
61
|
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
62
|
-
const { createLarkClient } = await import("./client-
|
|
63
|
-
const { TaskCreator } = await import("./taskCreator-
|
|
64
|
-
const { syncTaskStatuses } = await import("./sync-
|
|
62
|
+
const { createLarkClient } = await import("./client-IYEXZKNP.js");
|
|
63
|
+
const { TaskCreator } = await import("./taskCreator-TCI3VB5D.js");
|
|
64
|
+
const { syncTaskStatuses } = await import("./sync-FJA2I3HE.js");
|
|
65
65
|
const config = loadConfig();
|
|
66
66
|
const store = new JsonStore(config.storage.dataDir);
|
|
67
67
|
const client = createLarkClient(config);
|
|
@@ -82,9 +82,9 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
82
82
|
}
|
|
83
83
|
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
84
84
|
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
85
|
-
const { createLarkClient } = await import("./client-
|
|
86
|
-
const { TaskCreator } = await import("./taskCreator-
|
|
87
|
-
const { syncTaskStatuses } = await import("./sync-
|
|
85
|
+
const { createLarkClient } = await import("./client-IYEXZKNP.js");
|
|
86
|
+
const { TaskCreator } = await import("./taskCreator-TCI3VB5D.js");
|
|
87
|
+
const { syncTaskStatuses } = await import("./sync-FJA2I3HE.js");
|
|
88
88
|
const config = loadConfig();
|
|
89
89
|
const store = new JsonStore(config.storage.dataDir);
|
|
90
90
|
const client = createLarkClient(config);
|
|
@@ -99,9 +99,9 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
99
99
|
} else if (command === "task") {
|
|
100
100
|
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
101
101
|
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
102
|
-
const { createLarkClient } = await import("./client-
|
|
103
|
-
const { TaskCreator } = await import("./taskCreator-
|
|
104
|
-
const { syncTaskStatuses } = await import("./sync-
|
|
102
|
+
const { createLarkClient } = await import("./client-IYEXZKNP.js");
|
|
103
|
+
const { TaskCreator } = await import("./taskCreator-TCI3VB5D.js");
|
|
104
|
+
const { syncTaskStatuses } = await import("./sync-FJA2I3HE.js");
|
|
105
105
|
const config = loadConfig();
|
|
106
106
|
const store = new JsonStore(config.storage.dataDir);
|
|
107
107
|
const client = createLarkClient(config);
|
|
@@ -140,12 +140,12 @@ KMR\uFF08Key Meetings Record\uFF09\u2014 \u4F1A\u8BAE\u6316\u6398\u673A
|
|
|
140
140
|
process.exit(1);
|
|
141
141
|
}
|
|
142
142
|
const { loadConfig } = await import("./config-L2SVVMAR.js");
|
|
143
|
-
const { ClaudeCodeProvider } = await import("./claudeCode-
|
|
143
|
+
const { ClaudeCodeProvider } = await import("./claudeCode-2OTK6CH4.js");
|
|
144
144
|
const { QueryHandler } = await import("./handler-46CQQIA2.js");
|
|
145
145
|
const { JsonStore } = await import("./jsonStore-AL73KEUG.js");
|
|
146
|
-
const { createLarkClient } = await import("./client-
|
|
147
|
-
const { TaskCreator } = await import("./taskCreator-
|
|
148
|
-
const { syncTaskStatuses } = await import("./sync-
|
|
146
|
+
const { createLarkClient } = await import("./client-IYEXZKNP.js");
|
|
147
|
+
const { TaskCreator } = await import("./taskCreator-TCI3VB5D.js");
|
|
148
|
+
const { syncTaskStatuses } = await import("./sync-FJA2I3HE.js");
|
|
149
149
|
const config = loadConfig();
|
|
150
150
|
const store = new JsonStore(config.storage.dataDir);
|
|
151
151
|
const client = createLarkClient(config);
|
package/dist/index.js
CHANGED
|
@@ -9,23 +9,27 @@ import {
|
|
|
9
9
|
createLarkClient,
|
|
10
10
|
getBotOpenId,
|
|
11
11
|
startWSClient
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XPW2DGJX.js";
|
|
13
13
|
import {
|
|
14
14
|
TaskCreator
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KAWJNGXR.js";
|
|
16
16
|
import {
|
|
17
17
|
syncTaskStatuses
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-XQVLJTP4.js";
|
|
19
19
|
import {
|
|
20
20
|
ClaudeCodeProvider,
|
|
21
21
|
getExecEnv
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-WMK5MYOU.js";
|
|
23
23
|
import {
|
|
24
24
|
KMR_DIR,
|
|
25
25
|
getAgentProvider,
|
|
26
26
|
loadConfig
|
|
27
27
|
} from "./chunk-ZXGVA5QX.js";
|
|
28
28
|
|
|
29
|
+
// src/index.ts
|
|
30
|
+
import fs3 from "fs";
|
|
31
|
+
import path3 from "path";
|
|
32
|
+
|
|
29
33
|
// src/lark/docReader.ts
|
|
30
34
|
function extractDocumentId(url) {
|
|
31
35
|
const patterns = [
|
|
@@ -71,7 +75,6 @@ var DocReader = class {
|
|
|
71
75
|
}
|
|
72
76
|
pageToken = response.data?.page_token || void 0;
|
|
73
77
|
} while (pageToken);
|
|
74
|
-
console.log(`[docReader] \u6587\u6863 ${documentId} \u5171\u63D0\u53D6 ${blocks.length} \u4E2A\u6587\u672C\u5757`);
|
|
75
78
|
return blocks.join("\n");
|
|
76
79
|
}
|
|
77
80
|
extractBlockText(block) {
|
|
@@ -596,7 +599,6 @@ var SessionManager = class {
|
|
|
596
599
|
async ensureSession(userId) {
|
|
597
600
|
const existing = this.activeSessions.get(userId);
|
|
598
601
|
if (existing) {
|
|
599
|
-
console.log(`[session] \u590D\u7528\u5DF2\u6709 session: ${existing.name}`);
|
|
600
602
|
return existing.name;
|
|
601
603
|
}
|
|
602
604
|
const sessionName = `kmr-${userId}`;
|
|
@@ -616,7 +618,6 @@ var SessionManager = class {
|
|
|
616
618
|
console.error(`[session] \u521B\u5EFA session \u5931\u8D25:`, err.message);
|
|
617
619
|
throw new Error(`\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25: ${err.message}`);
|
|
618
620
|
}
|
|
619
|
-
console.log(`[session] \u53D1\u9001\u89D2\u8272\u8BBE\u5B9A...`);
|
|
620
621
|
try {
|
|
621
622
|
await execFileAsync(
|
|
622
623
|
"acpx",
|
|
@@ -640,8 +641,6 @@ var SessionManager = class {
|
|
|
640
641
|
["--approve-all", agentCmd, "-s", sessionName, text],
|
|
641
642
|
{ timeout: this.timeout, maxBuffer: 1024 * 1024, env: getExecEnv() }
|
|
642
643
|
);
|
|
643
|
-
console.log(`[session] agent \u5B8C\u6574\u8F93\u51FA:
|
|
644
|
-
${stdout}`);
|
|
645
644
|
return this.extractReply(stdout);
|
|
646
645
|
} catch (err) {
|
|
647
646
|
if (err.killed) {
|
|
@@ -731,7 +730,6 @@ var UserResolver = class {
|
|
|
731
730
|
*/
|
|
732
731
|
async loadChatMembers(chatId) {
|
|
733
732
|
if (this.loadedChats.has(chatId)) return;
|
|
734
|
-
console.log(`[user-resolver] \u52A0\u8F7D\u7FA4\u6210\u5458: chatId=${chatId}`);
|
|
735
733
|
try {
|
|
736
734
|
let pageToken;
|
|
737
735
|
do {
|
|
@@ -752,7 +750,6 @@ var UserResolver = class {
|
|
|
752
750
|
pageToken = response.data?.has_more ? response.data?.page_token : void 0;
|
|
753
751
|
} while (pageToken);
|
|
754
752
|
this.loadedChats.add(chatId);
|
|
755
|
-
console.log(`[user-resolver] \u7FA4\u6210\u5458\u52A0\u8F7D\u5B8C\u6210, \u5F53\u524D\u6620\u5C04\u6570: ${this.nameMap.size}`);
|
|
756
753
|
} catch (err) {
|
|
757
754
|
console.error(`[user-resolver] \u52A0\u8F7D\u7FA4\u6210\u5458\u5931\u8D25:`, err.message);
|
|
758
755
|
}
|
|
@@ -781,34 +778,25 @@ async function main() {
|
|
|
781
778
|
const botOpenId = await getBotOpenId(client);
|
|
782
779
|
const dispatcher = createEventDispatcher(botOpenId, async (messageId, text, chatId, senderId, meta) => {
|
|
783
780
|
const parsed = parseMessage(text);
|
|
784
|
-
console.log(`[route]
|
|
781
|
+
console.log(`[route] type=${parsed.type}, isGroup=${meta.isGroup}, isMentioned=${meta.isMentioned}`);
|
|
785
782
|
if (meta.isGroup && !meta.isMentioned && parsed.type === "document_link" /* DOCUMENT_LINK */) {
|
|
786
783
|
const title = await docReader.getDocumentTitle(parsed.documentId);
|
|
787
|
-
console.log(`[filter] \u7FA4\u804A\u6587\u6863\u6807\u9898: "${title}"`);
|
|
788
784
|
const isMeetingNote = /纪要|会议记录|meeting/i.test(title) || /from=vc_assistant/.test(text);
|
|
789
785
|
if (!isMeetingNote) {
|
|
790
|
-
console.log(`[filter] \u6587\u6863\u6807\u9898\u4E0D\u542B\u7EAA\u8981\u5173\u952E\u8BCD, \u5FFD\u7565`);
|
|
791
786
|
return;
|
|
792
787
|
}
|
|
793
|
-
console.log(`[filter] \u786E\u8BA4\u4E3A\u4F1A\u8BAE\u7EAA\u8981, \u7EE7\u7EED\u5904\u7406`);
|
|
794
788
|
}
|
|
795
789
|
try {
|
|
796
790
|
switch (parsed.type) {
|
|
797
791
|
case "document_link" /* DOCUMENT_LINK */: {
|
|
798
|
-
console.log(`[process] \
|
|
792
|
+
console.log(`[process] \u5904\u7406\u6587\u6863\u94FE\u63A5, documentId=${parsed.documentId}`);
|
|
799
793
|
await messenger.replyText(messageId, "\u6B63\u5728\u63D0\u53D6\u4F1A\u8BAE\u5173\u952E\u4FE1\u606F\uFF0C\u8BF7\u7A0D\u5019...");
|
|
800
|
-
console.log(`[reply] \u5DF2\u53D1\u9001"\u6B63\u5728\u63D0\u53D6"\u63D0\u793A`);
|
|
801
|
-
console.log(`[process] \u8BFB\u53D6\u98DE\u4E66\u6587\u6863\u5185\u5BB9...`);
|
|
802
794
|
const content = await docReader.readDocument(parsed.documentId);
|
|
803
|
-
console.log(`[process] \u6587\u6863\u5185\u5BB9\u8BFB\u53D6\u5B8C\u6210, \u957F\u5EA6=${content.length}`);
|
|
804
|
-
console.log(`[process] \u8C03\u7528 Agent \u63D0\u53D6\u4F1A\u8BAE\u4FE1\u606F...`);
|
|
805
795
|
const record = await agent.extract(content);
|
|
806
|
-
console.log(`[process] Agent \u63D0\u53D6\u5B8C\u6210: title="${record.summary.title}"`);
|
|
807
796
|
record.documentUrl = text.match(/(https?:\/\/[^\s]*feishu\.cn\/[^\s]+)/)?.[0] || "";
|
|
808
797
|
await store.save(record);
|
|
809
798
|
console.log(`\u2705 \u4F1A\u8BAE\u63D0\u53D6\u6210\u529F: ${record.summary.title} (${record.id})`);
|
|
810
799
|
await messenger.replyMeetingSummary(messageId, record);
|
|
811
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u4F1A\u8BAE\u6458\u8981\u56DE\u590D`);
|
|
812
800
|
if (record.todos.length > 0) {
|
|
813
801
|
await userResolver.loadChatMembers(chatId);
|
|
814
802
|
const oldPending = pendingConfirmations.get(senderId);
|
|
@@ -817,7 +805,6 @@ async function main() {
|
|
|
817
805
|
const p = pendingConfirmations.get(senderId);
|
|
818
806
|
if (p && p.meetingId === record.id) {
|
|
819
807
|
pendingConfirmations.delete(senderId);
|
|
820
|
-
console.log(`[timeout] \u5F85\u529E\u786E\u8BA4\u8D85\u65F6, userId=${senderId}, meetingId=${record.id}`);
|
|
821
808
|
}
|
|
822
809
|
}, 18e4);
|
|
823
810
|
pendingConfirmations.set(senderId, {
|
|
@@ -828,26 +815,21 @@ async function main() {
|
|
|
828
815
|
timer
|
|
829
816
|
});
|
|
830
817
|
await messenger.replyTodoConfirmation(messageId, record.todos);
|
|
831
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u5F85\u529E\u786E\u8BA4\u6D88\u606F, ${record.todos.length} \u6761\u5F85\u529E`);
|
|
832
818
|
}
|
|
833
819
|
break;
|
|
834
820
|
}
|
|
835
821
|
case "find_query" /* FIND_QUERY */: {
|
|
836
|
-
console.log(`[process] \
|
|
822
|
+
console.log(`[process] \u641C\u7D22: query="${parsed.query}"`);
|
|
837
823
|
await syncTaskStatuses(store, taskCreator);
|
|
838
824
|
const results = await queryHandler.find(parsed.query);
|
|
839
|
-
console.log(`[process] \u641C\u7D22\u5B8C\u6210, \u7ED3\u679C\u6570=${results.length}`);
|
|
840
825
|
await messenger.replySearchResults(messageId, results);
|
|
841
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u641C\u7D22\u7ED3\u679C`);
|
|
842
826
|
break;
|
|
843
827
|
}
|
|
844
828
|
case "list_all" /* LIST_ALL */: {
|
|
845
829
|
console.log(`[process] \u5217\u51FA\u6240\u6709\u8BB0\u5F55`);
|
|
846
830
|
await syncTaskStatuses(store, taskCreator);
|
|
847
831
|
const all = await store.list();
|
|
848
|
-
console.log(`[process] \u5171 ${all.length} \u6761\u8BB0\u5F55`);
|
|
849
832
|
await messenger.replyRecordList(messageId, all);
|
|
850
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u8BB0\u5F55\u5217\u8868`);
|
|
851
833
|
break;
|
|
852
834
|
}
|
|
853
835
|
case "list_tasks" /* LIST_TASKS */: {
|
|
@@ -894,7 +876,6 @@ async function main() {
|
|
|
894
876
|
} else {
|
|
895
877
|
await messenger.replyText(messageId, `\u274C \u672A\u627E\u5230\u8BB0\u5F55: ${parsed.deleteId}`);
|
|
896
878
|
}
|
|
897
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u5220\u9664\u7ED3\u679C`);
|
|
898
879
|
break;
|
|
899
880
|
}
|
|
900
881
|
case "show_detail" /* SHOW_DETAIL */: {
|
|
@@ -906,7 +887,6 @@ async function main() {
|
|
|
906
887
|
} else {
|
|
907
888
|
await messenger.replyText(messageId, `\u274C \u672A\u627E\u5230\u8BB0\u5F55: ${parsed.showId}`);
|
|
908
889
|
}
|
|
909
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u8BE6\u60C5`);
|
|
910
890
|
break;
|
|
911
891
|
}
|
|
912
892
|
case "confirm_tasks" /* CONFIRM_TASKS */: {
|
|
@@ -927,9 +907,6 @@ async function main() {
|
|
|
927
907
|
for (const { todo, index } of selectedTodos) {
|
|
928
908
|
try {
|
|
929
909
|
const ownerOpenId = userResolver.resolve(todo.owner) || senderId;
|
|
930
|
-
if (ownerOpenId !== senderId) {
|
|
931
|
-
console.log(`[task] \u8D1F\u8D23\u4EBA "${todo.owner}" -> ${ownerOpenId}`);
|
|
932
|
-
}
|
|
933
910
|
const result = await taskCreator.createTask({
|
|
934
911
|
summary: `${todo.content}\uFF08${todo.owner}\uFF09`,
|
|
935
912
|
due: todo.deadline,
|
|
@@ -962,11 +939,9 @@ async function main() {
|
|
|
962
939
|
}
|
|
963
940
|
}
|
|
964
941
|
await store.save(record);
|
|
965
|
-
console.log(`[task] \u5DF2\u5C06 ${createdTasks.length} \u6761\u4EFB\u52A1\u5173\u8054\u5230\u4F1A\u8BAE ${pending.meetingId}`);
|
|
966
942
|
}
|
|
967
943
|
}
|
|
968
944
|
pendingConfirmations.delete(senderId);
|
|
969
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u4EFB\u52A1\u521B\u5EFA\u7ED3\u679C`);
|
|
970
945
|
break;
|
|
971
946
|
}
|
|
972
947
|
case "reject_tasks" /* REJECT_TASKS */: {
|
|
@@ -978,26 +953,22 @@ async function main() {
|
|
|
978
953
|
clearTimeout(pending.timer);
|
|
979
954
|
pendingConfirmations.delete(senderId);
|
|
980
955
|
await messenger.replyText(messageId, "\u2705 \u5DF2\u53D6\u6D88\u521B\u5EFA\u5F85\u529E\u4EFB\u52A1\u3002");
|
|
981
|
-
console.log(`[process] \u7528\u6237\u62D2\u7EDD\u521B\u5EFA\u4EFB\u52A1, userId=${senderId}`);
|
|
982
956
|
break;
|
|
983
957
|
}
|
|
984
958
|
case "unknown" /* UNKNOWN */: {
|
|
985
959
|
if (meta.isGroup && !meta.isMentioned) {
|
|
986
|
-
console.log(`[filter] \u7FA4\u804A\u975E@\u672A\u77E5\u6D88\u606F, \u5FFD\u7565`);
|
|
987
960
|
break;
|
|
988
961
|
}
|
|
989
962
|
console.log(`[process] \u81EA\u7531\u5BF9\u8BDD, userId=${senderId}`);
|
|
990
963
|
await messenger.replyText(messageId, "\u6B63\u5728\u601D\u8003...");
|
|
991
964
|
const reply = await sessionManager.handleMessage(senderId, text);
|
|
992
965
|
await messenger.replyText(messageId, reply);
|
|
993
|
-
console.log(`[reply] \u5DF2\u53D1\u9001 AI \u56DE\u590D`);
|
|
994
966
|
break;
|
|
995
967
|
}
|
|
996
968
|
}
|
|
997
969
|
} catch (err) {
|
|
998
970
|
console.error("[error] \u5904\u7406\u6D88\u606F\u5931\u8D25:", err);
|
|
999
971
|
await messenger.replyText(messageId, `\u5904\u7406\u5931\u8D25: ${err.message}`);
|
|
1000
|
-
console.log(`[reply] \u5DF2\u53D1\u9001\u9519\u8BEF\u56DE\u590D`);
|
|
1001
972
|
}
|
|
1002
973
|
});
|
|
1003
974
|
startWSClient(client, dispatcher);
|
|
@@ -1012,6 +983,85 @@ async function main() {
|
|
|
1012
983
|
}
|
|
1013
984
|
syncTaskStatuses(store, taskCreator);
|
|
1014
985
|
setInterval(() => syncTaskStatuses(store, taskCreator), 60 * 60 * 1e3);
|
|
986
|
+
if (config.lark.adminOpenId) {
|
|
987
|
+
const adminId = config.lark.adminOpenId;
|
|
988
|
+
const lastPushFile = path3.join(KMR_DIR, ".last_task_push");
|
|
989
|
+
const getToday = () => (/* @__PURE__ */ new Date()).toLocaleDateString("sv-SE", { timeZone: "Asia/Shanghai" });
|
|
990
|
+
const hasAlreadyPushedToday = () => {
|
|
991
|
+
try {
|
|
992
|
+
const last = fs3.readFileSync(lastPushFile, "utf-8").trim();
|
|
993
|
+
return last === getToday();
|
|
994
|
+
} catch {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
const markPushed = () => {
|
|
999
|
+
fs3.writeFileSync(lastPushFile, getToday(), "utf-8");
|
|
1000
|
+
};
|
|
1001
|
+
const pushDailyTaskSummary = async () => {
|
|
1002
|
+
if (hasAlreadyPushedToday()) return;
|
|
1003
|
+
console.log("[daily] \u63A8\u9001\u6BCF\u65E5\u4EFB\u52A1\u6458\u8981...");
|
|
1004
|
+
await syncTaskStatuses(store, taskCreator);
|
|
1005
|
+
const meetings = await store.list();
|
|
1006
|
+
const tasks = [];
|
|
1007
|
+
for (const m of meetings) {
|
|
1008
|
+
if (!m.createdTasks) continue;
|
|
1009
|
+
for (const t of m.createdTasks) {
|
|
1010
|
+
tasks.push({
|
|
1011
|
+
summary: t.summary,
|
|
1012
|
+
status: t.status || "open",
|
|
1013
|
+
createdAt: t.createdAt,
|
|
1014
|
+
completedAt: t.completedAt,
|
|
1015
|
+
meetingTitle: m.summary.title,
|
|
1016
|
+
url: t.url
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (tasks.length === 0) {
|
|
1021
|
+
markPushed();
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const openTasks = tasks.filter((t) => t.status !== "completed");
|
|
1025
|
+
const completedTasks = tasks.filter((t) => t.status === "completed");
|
|
1026
|
+
const lines = [`\u{1F4CB} \u6BCF\u65E5\u4EFB\u52A1\u6458\u8981\uFF08${getToday()}\uFF09`, ""];
|
|
1027
|
+
if (openTasks.length > 0) {
|
|
1028
|
+
lines.push(`\u23F3 \u8FDB\u884C\u4E2D\uFF08${openTasks.length}\uFF09\uFF1A`);
|
|
1029
|
+
for (const t of openTasks) {
|
|
1030
|
+
lines.push(` \u2022 ${t.summary}`);
|
|
1031
|
+
lines.push(` \u6765\u6E90\uFF1A${t.meetingTitle} | \u521B\u5EFA\uFF1A${t.createdAt}`);
|
|
1032
|
+
}
|
|
1033
|
+
lines.push("");
|
|
1034
|
+
}
|
|
1035
|
+
if (completedTasks.length > 0) {
|
|
1036
|
+
lines.push(`\u2705 \u5DF2\u5B8C\u6210\uFF08${completedTasks.length}\uFF09\uFF1A`);
|
|
1037
|
+
for (const t of completedTasks) {
|
|
1038
|
+
lines.push(` \u2022 ${t.summary}${t.completedAt ? "\uFF08" + t.completedAt + "\uFF09" : ""}`);
|
|
1039
|
+
}
|
|
1040
|
+
lines.push("");
|
|
1041
|
+
}
|
|
1042
|
+
try {
|
|
1043
|
+
await messenger.sendToUser(adminId, lines.join("\n"));
|
|
1044
|
+
markPushed();
|
|
1045
|
+
console.log("[daily] \u6BCF\u65E5\u4EFB\u52A1\u6458\u8981\u5DF2\u63A8\u9001");
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
console.error("[daily] \u63A8\u9001\u5931\u8D25:", err.message);
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
const now = /* @__PURE__ */ new Date();
|
|
1051
|
+
const shanghaiHour = parseInt(now.toLocaleString("en-US", { timeZone: "Asia/Shanghai", hour: "numeric", hour12: false }));
|
|
1052
|
+
const shanghaiMinute = parseInt(now.toLocaleString("en-US", { timeZone: "Asia/Shanghai", minute: "numeric" }));
|
|
1053
|
+
if ((shanghaiHour > 9 || shanghaiHour === 9 && shanghaiMinute >= 30) && !hasAlreadyPushedToday()) {
|
|
1054
|
+
pushDailyTaskSummary();
|
|
1055
|
+
}
|
|
1056
|
+
setInterval(() => {
|
|
1057
|
+
const n = /* @__PURE__ */ new Date();
|
|
1058
|
+
const h = parseInt(n.toLocaleString("en-US", { timeZone: "Asia/Shanghai", hour: "numeric", hour12: false }));
|
|
1059
|
+
const m = parseInt(n.toLocaleString("en-US", { timeZone: "Asia/Shanghai", minute: "numeric" }));
|
|
1060
|
+
if (h === 9 && m === 30) {
|
|
1061
|
+
pushDailyTaskSummary();
|
|
1062
|
+
}
|
|
1063
|
+
}, 6e4);
|
|
1064
|
+
}
|
|
1015
1065
|
if (config.lark.adminOpenId) {
|
|
1016
1066
|
const adminId = config.lark.adminOpenId;
|
|
1017
1067
|
let exiting = false;
|