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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatccc",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
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
- return `> ⚠️ 延迟送达提醒:此消息于 ${sendTimeStr} 发送,因服务离线,延迟约 ${delayStr}后送达`;
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