chatccc 0.2.30 → 0.2.32
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/package.json +1 -1
- package/src/feishu-api.ts +3 -2
- package/src/index.ts +21 -1
- package/src/session.ts +8 -0
package/package.json
CHANGED
package/src/feishu-api.ts
CHANGED
|
@@ -487,7 +487,7 @@ export async function getOrDownloadImage(token: string, messageId: string, fileK
|
|
|
487
487
|
const DELAY_NOTICE_THRESHOLD_MS = 15 * 60 * 1000; // 15 分钟
|
|
488
488
|
|
|
489
489
|
/** 消息延迟超过阈值时生成提醒文本,否则返回 null */
|
|
490
|
-
export function formatDelayNotice(createTimeMs: number, nowMs?: number): string | null {
|
|
490
|
+
export function formatDelayNotice(createTimeMs: number, messageText?: string, nowMs?: number): string | null {
|
|
491
491
|
const now = nowMs ?? Date.now();
|
|
492
492
|
const delayMs = now - createTimeMs;
|
|
493
493
|
if (delayMs < DELAY_NOTICE_THRESHOLD_MS) return null;
|
|
@@ -515,7 +515,8 @@ export function formatDelayNotice(createTimeMs: number, nowMs?: number): string
|
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
-
|
|
518
|
+
const contentLine = messageText ? `\n> 原始内容:${messageText.slice(0, 200)}` : "";
|
|
519
|
+
return `> ⚠️ 延迟送达提醒:此消息于 ${sendTimeStr} 发送,因服务离线,延迟约 ${delayStr}后送达${contentLine}`;
|
|
519
520
|
}
|
|
520
521
|
|
|
521
522
|
export async function sendTextReply(
|
package/src/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
getSessionStatus,
|
|
93
93
|
getAllSessionsStatus,
|
|
94
94
|
initClaudeSession,
|
|
95
|
+
lastMsgTimestamps,
|
|
95
96
|
processedMessages,
|
|
96
97
|
resetState,
|
|
97
98
|
resumeAndPrompt,
|
|
@@ -494,6 +495,10 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
494
495
|
cEntry.stopped = true;
|
|
495
496
|
if (cEntry.spinnerTimer) { clearInterval(cEntry.spinnerTimer); cEntry.spinnerTimer = null; }
|
|
496
497
|
cEntry.close();
|
|
498
|
+
const prevTs = lastMsgTimestamps.get(chatId);
|
|
499
|
+
if (prevTs === undefined || cEntry.msgTimestamp > prevTs) {
|
|
500
|
+
lastMsgTimestamps.set(chatId, cEntry.msgTimestamp);
|
|
501
|
+
}
|
|
497
502
|
console.log(`[${ts()}] [STOP] User sent /stop, session=${sessionId}`);
|
|
498
503
|
await sendTextReply(freshToken, chatId, "会话已停止。").catch(() => {});
|
|
499
504
|
logTrace(tid, "DONE", { outcome: "stopped" });
|
|
@@ -589,6 +594,10 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
589
594
|
existing.stopped = true;
|
|
590
595
|
if (existing.spinnerTimer) { clearInterval(existing.spinnerTimer); existing.spinnerTimer = null; }
|
|
591
596
|
existing.close();
|
|
597
|
+
const prevTs = lastMsgTimestamps.get(chatId);
|
|
598
|
+
if (prevTs === undefined || existing.msgTimestamp > prevTs) {
|
|
599
|
+
lastMsgTimestamps.set(chatId, existing.msgTimestamp);
|
|
600
|
+
}
|
|
592
601
|
chatSessionMap.delete(chatId);
|
|
593
602
|
}
|
|
594
603
|
|
|
@@ -678,6 +687,13 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
678
687
|
return;
|
|
679
688
|
}
|
|
680
689
|
|
|
690
|
+
const lastTs = lastMsgTimestamps.get(chatId);
|
|
691
|
+
if (lastTs !== undefined && msgTimestamp <= lastTs) {
|
|
692
|
+
logTrace(tid, "DONE", { outcome: "skip_old_message_no_session", msgTimestamp, lastTimestamp: lastTs });
|
|
693
|
+
console.log(`[${ts()}] [SKIP] Older message (${msgTimestamp} <= ${lastTs}), no active session, ignoring`);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
|
|
681
697
|
const existing = chatSessionMap.get(chatId);
|
|
682
698
|
if (existing && !existing.stopped) {
|
|
683
699
|
if (msgTimestamp <= existing.msgTimestamp) {
|
|
@@ -688,6 +704,10 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
688
704
|
existing.stopped = true;
|
|
689
705
|
if (existing.spinnerTimer) { clearInterval(existing.spinnerTimer); existing.spinnerTimer = null; }
|
|
690
706
|
existing.close();
|
|
707
|
+
const prevTs = lastMsgTimestamps.get(chatId);
|
|
708
|
+
if (prevTs === undefined || existing.msgTimestamp > prevTs) {
|
|
709
|
+
lastMsgTimestamps.set(chatId, existing.msgTimestamp);
|
|
710
|
+
}
|
|
691
711
|
chatSessionMap.delete(chatId);
|
|
692
712
|
console.log(`[${ts()}] [INTERRUPT] New message arrived, cancelled previous session ${sessionId}`);
|
|
693
713
|
logTrace(tid, "INTERRUPT", { oldSessionId: sessionId });
|
|
@@ -886,7 +906,7 @@ async function startBotServiceCore(): Promise<void> {
|
|
|
886
906
|
if (!text) return;
|
|
887
907
|
const msgTimestamp = parseInt(message.create_time ?? "0", 10) || Date.now();
|
|
888
908
|
logTrace(traceId, "RECV", { chatId, chatType, text: text.slice(0, 100) });
|
|
889
|
-
const delayNotice = formatDelayNotice(msgTimestamp);
|
|
909
|
+
const delayNotice = formatDelayNotice(msgTimestamp, text);
|
|
890
910
|
if (delayNotice) {
|
|
891
911
|
const delayToken = await getTenantAccessToken();
|
|
892
912
|
await sendCardReply(delayToken, chatId, "延迟送达", delayNotice, "yellow").catch(() => {});
|
package/src/session.ts
CHANGED
|
@@ -50,6 +50,9 @@ import { buildImSkillsPrompt, exportSkillSubDocs } from "./im-skills.ts";
|
|
|
50
50
|
export const processedMessages = new Set<string>();
|
|
51
51
|
export const MAX_PROCESSED = 5000;
|
|
52
52
|
|
|
53
|
+
/** 每个 chatId 上一次已处理消息的时间戳,用于拦截延迟送达的旧消息 */
|
|
54
|
+
export const lastMsgTimestamps = new Map<string, number>();
|
|
55
|
+
|
|
53
56
|
export let sessionGen = 0;
|
|
54
57
|
export const chatSessionMap = new Map<string, {
|
|
55
58
|
gen: number;
|
|
@@ -93,6 +96,7 @@ export function resetState(): void {
|
|
|
93
96
|
chatSessionMap.clear();
|
|
94
97
|
sessionInfoMap.clear();
|
|
95
98
|
processedMessages.clear();
|
|
99
|
+
lastMsgTimestamps.clear();
|
|
96
100
|
console.log(`[${ts()}] [RESET] State cleared (dedup + active sessions)`);
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -523,6 +527,10 @@ export async function resumeAndPrompt(
|
|
|
523
527
|
const cEntry = chatSessionMap.get(chatId);
|
|
524
528
|
if (!cEntry || cEntry.gen !== myGen) return;
|
|
525
529
|
const wasStopped = cEntry.stopped;
|
|
530
|
+
const prevTs = lastMsgTimestamps.get(chatId);
|
|
531
|
+
if (prevTs === undefined || cEntry.msgTimestamp > prevTs) {
|
|
532
|
+
lastMsgTimestamps.set(chatId, cEntry.msgTimestamp);
|
|
533
|
+
}
|
|
526
534
|
chatSessionMap.delete(chatId);
|
|
527
535
|
setChatAvatar(token, chatId, tool, "idle").catch(() => {});
|
|
528
536
|
|