liangzimixin 0.3.67 → 0.3.68
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/dist/index.cjs +98 -22
- package/dist/index.d.cts +25 -5
- package/dist/setup-entry.cjs +98 -22
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -17522,6 +17522,8 @@ var DEFAULT_INTERNAL_CONFIG = {
|
|
|
17522
17522
|
reconnectBaseMs: 1e3,
|
|
17523
17523
|
reconnectMaxMs: 6e4,
|
|
17524
17524
|
maxReconnectAttempts: 10,
|
|
17525
|
+
tailBackoffDelays: [12e4, 24e4, 48e4],
|
|
17526
|
+
persistentRetryIntervalMs: 3e5,
|
|
17525
17527
|
dedup: {
|
|
17526
17528
|
ttlMs: 3e5,
|
|
17527
17529
|
maxEntries: 5e3
|
|
@@ -18654,6 +18656,48 @@ var TTLMap = class {
|
|
|
18654
18656
|
}
|
|
18655
18657
|
};
|
|
18656
18658
|
|
|
18659
|
+
// src/utils/msg-type-detect.ts
|
|
18660
|
+
var MARKDOWN_PATTERNS = [
|
|
18661
|
+
// ── 块级元素 (高置信度) ──
|
|
18662
|
+
// 围栏代码块: ```language
|
|
18663
|
+
{ pattern: /^```/m, label: "fenced-code-block" },
|
|
18664
|
+
// 标题: # Title (行首,# 后必须跟空格)
|
|
18665
|
+
{ pattern: /^#{1,6}\s+\S/m, label: "heading" },
|
|
18666
|
+
// 表格: | cell | cell | (行首行尾都有 |,且至少有 2 个分隔符)
|
|
18667
|
+
{ pattern: /^\|.+\|.+\|\s*$/m, label: "table" },
|
|
18668
|
+
// 引用块: > text (行首)
|
|
18669
|
+
{ pattern: /^>\s+\S/m, label: "blockquote" },
|
|
18670
|
+
// 分隔线: --- 或 *** (独占一行,至少 3 个)
|
|
18671
|
+
{ pattern: /^[-*]{3,}\s*$/m, label: "horizontal-rule" },
|
|
18672
|
+
// ── 内联元素 (中置信度) ──
|
|
18673
|
+
// 粗体: **text** 或 __text__
|
|
18674
|
+
{ pattern: /\*\*[^*]+\*\*/, label: "bold-asterisk" },
|
|
18675
|
+
{ pattern: /__[^_]+__/, label: "bold-underscore" },
|
|
18676
|
+
// 链接: [text](url)
|
|
18677
|
+
{ pattern: /\[[^\]]+\]\([^)]+\)/, label: "link" },
|
|
18678
|
+
// 图片: 
|
|
18679
|
+
{ pattern: /!\[[^\]]*\]\([^)]+\)/, label: "image" },
|
|
18680
|
+
// 行内代码: `code` (但不是 ```)
|
|
18681
|
+
{ pattern: /(?<!`)`[^`\n]+`(?!`)/, label: "inline-code" },
|
|
18682
|
+
// ── 列表 (需行首匹配) ──
|
|
18683
|
+
// 有序列表: 1. text
|
|
18684
|
+
{ pattern: /^\d+\.\s+\S/m, label: "ordered-list" },
|
|
18685
|
+
// 无序列表: - text 或 * text (行首,后跟空格和非空字符)
|
|
18686
|
+
{ pattern: /^[-*]\s+\S/m, label: "unordered-list" }
|
|
18687
|
+
];
|
|
18688
|
+
function isMarkdownContent(text) {
|
|
18689
|
+
if (!text) return false;
|
|
18690
|
+
for (const { pattern } of MARKDOWN_PATTERNS) {
|
|
18691
|
+
if (pattern.test(text)) {
|
|
18692
|
+
return true;
|
|
18693
|
+
}
|
|
18694
|
+
}
|
|
18695
|
+
return false;
|
|
18696
|
+
}
|
|
18697
|
+
function resolveTextMsgType(text) {
|
|
18698
|
+
return isMarkdownContent(text) ? "markdown" : "text";
|
|
18699
|
+
}
|
|
18700
|
+
|
|
18657
18701
|
// src/channel/outbound.ts
|
|
18658
18702
|
var log7 = createLogger("channel/outbound");
|
|
18659
18703
|
var messageEncryptionStatus = new TTLMap({
|
|
@@ -18727,7 +18771,7 @@ var quantumImOutbound = {
|
|
|
18727
18771
|
await messagePipe.sendMessage({
|
|
18728
18772
|
chatId,
|
|
18729
18773
|
senderId: chatId,
|
|
18730
|
-
msgType:
|
|
18774
|
+
msgType: resolveTextMsgType(text),
|
|
18731
18775
|
content: JSON.stringify({ content: text }),
|
|
18732
18776
|
skipEncrypt
|
|
18733
18777
|
});
|
|
@@ -19080,7 +19124,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
19080
19124
|
await messagePipe.sendMessage({
|
|
19081
19125
|
chatId,
|
|
19082
19126
|
senderId,
|
|
19083
|
-
msgType:
|
|
19127
|
+
msgType: resolveTextMsgType(payload.text),
|
|
19084
19128
|
content: JSON.stringify({ content: payload.text }),
|
|
19085
19129
|
replyToMessageId,
|
|
19086
19130
|
skipEncrypt: shouldSkipEncrypt
|
|
@@ -19119,11 +19163,12 @@ function createQuantumImDeliverFn(deps) {
|
|
|
19119
19163
|
mediaUrl,
|
|
19120
19164
|
error: err.message
|
|
19121
19165
|
});
|
|
19166
|
+
const fallbackText = `[\u{1F4CE} \u6587\u4EF6\u94FE\u63A5](${mediaUrl})`;
|
|
19122
19167
|
await messagePipe.sendMessage({
|
|
19123
19168
|
chatId,
|
|
19124
19169
|
senderId,
|
|
19125
|
-
msgType:
|
|
19126
|
-
content: JSON.stringify({ content:
|
|
19170
|
+
msgType: resolveTextMsgType(fallbackText),
|
|
19171
|
+
content: JSON.stringify({ content: fallbackText }),
|
|
19127
19172
|
replyToMessageId,
|
|
19128
19173
|
skipEncrypt: shouldSkipEncrypt
|
|
19129
19174
|
});
|
|
@@ -19247,7 +19292,7 @@ var InboundPipeline = class {
|
|
|
19247
19292
|
await this.deps.messagePipe.sendMessage({
|
|
19248
19293
|
chatId: msg.chatId,
|
|
19249
19294
|
senderId: msg.senderId,
|
|
19250
|
-
msgType:
|
|
19295
|
+
msgType: resolveTextMsgType(text),
|
|
19251
19296
|
content: JSON.stringify({ content: text }),
|
|
19252
19297
|
skipEncrypt: cmdSkipEncrypt
|
|
19253
19298
|
});
|
|
@@ -19291,7 +19336,7 @@ var InboundPipeline = class {
|
|
|
19291
19336
|
await this.deps.messagePipe.sendMessage({
|
|
19292
19337
|
chatId: msg.chatId,
|
|
19293
19338
|
senderId: msg.senderId,
|
|
19294
|
-
msgType:
|
|
19339
|
+
msgType: resolveTextMsgType(fallbackText),
|
|
19295
19340
|
content: JSON.stringify({ content: fallbackText }),
|
|
19296
19341
|
skipEncrypt: cmdSkipEncrypt
|
|
19297
19342
|
}).catch((err) => {
|
|
@@ -21305,7 +21350,7 @@ var MessagePipe = class _MessagePipe {
|
|
|
21305
21350
|
msgUid: callbackData.msgUid,
|
|
21306
21351
|
reason
|
|
21307
21352
|
});
|
|
21308
|
-
const hintText = this.
|
|
21353
|
+
const hintText = !this.quantumAccount ? HINT_NO_QUANTUM_ACCOUNT : "\u26A0\uFE0F \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u89E3\u5BC6\u52A0\u5BC6\u6D88\u606F\u3002\u8BF7\u53D1\u9001\u660E\u6587\u6D88\u606F\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
21309
21354
|
await this.sendHintMessage(callbackData, hintText);
|
|
21310
21355
|
return;
|
|
21311
21356
|
}
|
|
@@ -21359,7 +21404,7 @@ var MessagePipe = class _MessagePipe {
|
|
|
21359
21404
|
await this.sendMessage({
|
|
21360
21405
|
chatId,
|
|
21361
21406
|
senderId,
|
|
21362
|
-
msgType:
|
|
21407
|
+
msgType: resolveTextMsgType(hintText),
|
|
21363
21408
|
content: JSON.stringify({ content: hintText }),
|
|
21364
21409
|
skipEncrypt: true
|
|
21365
21410
|
});
|
|
@@ -21424,7 +21469,9 @@ var ConnectionManager = class {
|
|
|
21424
21469
|
heartbeatTimeoutMs: options?.heartbeatTimeoutMs ?? 1e4,
|
|
21425
21470
|
reconnectBaseMs: options?.reconnectBaseMs ?? 1e3,
|
|
21426
21471
|
reconnectMaxMs: options?.reconnectMaxMs ?? 6e4,
|
|
21427
|
-
maxReconnectAttempts: options?.maxReconnectAttempts ?? 10
|
|
21472
|
+
maxReconnectAttempts: options?.maxReconnectAttempts ?? 10,
|
|
21473
|
+
tailBackoffDelays: options?.tailBackoffDelays ?? [12e4, 24e4, 48e4],
|
|
21474
|
+
persistentRetryIntervalMs: options?.persistentRetryIntervalMs ?? 3e5
|
|
21428
21475
|
};
|
|
21429
21476
|
log26.info("ConnectionManager initialized", this.options);
|
|
21430
21477
|
}
|
|
@@ -21535,14 +21582,46 @@ var ConnectionManager = class {
|
|
|
21535
21582
|
this.reconnecting = false;
|
|
21536
21583
|
});
|
|
21537
21584
|
}
|
|
21538
|
-
/**
|
|
21585
|
+
/**
|
|
21586
|
+
* 计算当前重连延迟 — 三阶段退避策略:
|
|
21587
|
+
*
|
|
21588
|
+
* Phase 1 (attempt 0 ~ tailStart-1): 指数退避 1s → 60s
|
|
21589
|
+
* Phase 2 (attempt tailStart ~ maxReconnectAttempts-1): 尾部退避 120s/240s/480s
|
|
21590
|
+
* Phase 3 (attempt >= maxReconnectAttempts): 持续重试 300s
|
|
21591
|
+
*/
|
|
21592
|
+
computeReconnectDelay() {
|
|
21593
|
+
const { reconnectBaseMs, reconnectMaxMs, maxReconnectAttempts, tailBackoffDelays, persistentRetryIntervalMs } = this.options;
|
|
21594
|
+
const attempt = this.reconnectAttempts;
|
|
21595
|
+
if (attempt >= maxReconnectAttempts) {
|
|
21596
|
+
return { delay: persistentRetryIntervalMs, phase: "persistent" };
|
|
21597
|
+
}
|
|
21598
|
+
const tailStart = maxReconnectAttempts - tailBackoffDelays.length;
|
|
21599
|
+
if (attempt >= tailStart) {
|
|
21600
|
+
return { delay: tailBackoffDelays[attempt - tailStart], phase: "tail" };
|
|
21601
|
+
}
|
|
21602
|
+
return {
|
|
21603
|
+
delay: Math.min(reconnectBaseMs * 2 ** attempt, reconnectMaxMs),
|
|
21604
|
+
phase: "exponential"
|
|
21605
|
+
};
|
|
21606
|
+
}
|
|
21607
|
+
/** 三阶段退避重连 — 永不放弃 */
|
|
21539
21608
|
async reconnect() {
|
|
21540
|
-
while (this.running
|
|
21541
|
-
const delay =
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21609
|
+
while (this.running) {
|
|
21610
|
+
const { delay, phase } = this.computeReconnectDelay();
|
|
21611
|
+
const isPersistent = phase === "persistent";
|
|
21612
|
+
if (isPersistent) {
|
|
21613
|
+
log26.warn("ws:persistent-retry", {
|
|
21614
|
+
attempt: this.reconnectAttempts + 1,
|
|
21615
|
+
delayMs: delay,
|
|
21616
|
+
message: "\u5DF2\u8D85\u8FC7\u6700\u5927\u91CD\u8FDE\u6B21\u6570\uFF0C\u8FDB\u5165\u6301\u7EED\u91CD\u8BD5\u6A21\u5F0F (\u6BCF 5 \u5206\u949F)"
|
|
21617
|
+
});
|
|
21618
|
+
} else {
|
|
21619
|
+
log26.info("ws:reconnecting", {
|
|
21620
|
+
attempt: this.reconnectAttempts + 1,
|
|
21621
|
+
delayMs: delay,
|
|
21622
|
+
phase
|
|
21623
|
+
});
|
|
21624
|
+
}
|
|
21546
21625
|
await sleep2(delay);
|
|
21547
21626
|
if (!this.running) return;
|
|
21548
21627
|
this.reconnectAttempts++;
|
|
@@ -21558,23 +21637,20 @@ var ConnectionManager = class {
|
|
|
21558
21637
|
...this.appId ? { "X-App-ID": this.appId } : {}
|
|
21559
21638
|
}
|
|
21560
21639
|
});
|
|
21640
|
+
const totalAttempts = this.reconnectAttempts;
|
|
21561
21641
|
this.reconnectAttempts = 0;
|
|
21562
21642
|
this.registerEvents();
|
|
21563
21643
|
this.startHeartbeat();
|
|
21564
|
-
log26.info("ws:reconnected", {
|
|
21644
|
+
log26.info("ws:reconnected", { totalAttempts, url: this.url });
|
|
21565
21645
|
return;
|
|
21566
21646
|
} catch (err) {
|
|
21567
21647
|
log26.warn("ws:reconnect-failed", {
|
|
21568
21648
|
attempt: this.reconnectAttempts,
|
|
21649
|
+
phase,
|
|
21569
21650
|
error: err.message
|
|
21570
21651
|
});
|
|
21571
21652
|
}
|
|
21572
21653
|
}
|
|
21573
|
-
if (this.running) {
|
|
21574
|
-
log26.error("ws:max-reconnect-reached", {
|
|
21575
|
-
maxAttempts: this.options.maxReconnectAttempts
|
|
21576
|
-
});
|
|
21577
|
-
}
|
|
21578
21654
|
}
|
|
21579
21655
|
/** 停止连接 — 清理心跳定时器 + 关闭 WS */
|
|
21580
21656
|
async stop() {
|
package/dist/index.d.cts
CHANGED
|
@@ -65,8 +65,12 @@ interface InternalConfig {
|
|
|
65
65
|
reconnectBaseMs: number;
|
|
66
66
|
/** 🟡 重连最大退避 (ms) */
|
|
67
67
|
reconnectMaxMs: number;
|
|
68
|
-
/** 🟡 最大重连尝试次数 */
|
|
68
|
+
/** 🟡 最大重连尝试次数 (Phase 1 + Phase 2) */
|
|
69
69
|
maxReconnectAttempts: number;
|
|
70
|
+
/** 🟡 尾部退避延迟序列 (ms),从 maxReconnectAttempts 末尾倒数 */
|
|
71
|
+
tailBackoffDelays: number[];
|
|
72
|
+
/** 🟡 持续重试间隔 (ms),超过 maxReconnectAttempts 后使用 */
|
|
73
|
+
persistentRetryIntervalMs: number;
|
|
70
74
|
/** 🟡 消息去重配置 */
|
|
71
75
|
dedup?: {
|
|
72
76
|
ttlMs: number;
|
|
@@ -989,7 +993,11 @@ declare class MessagePipe {
|
|
|
989
993
|
/**
|
|
990
994
|
* openclaw-liangzimixin — 连接管理器
|
|
991
995
|
*
|
|
992
|
-
* 心跳保活 (30s) +
|
|
996
|
+
* 心跳保活 (30s) + 三阶段退避重连:
|
|
997
|
+
* Phase 1: 指数退避 1s → 2s → 4s → ... → 60s (前 7 次)
|
|
998
|
+
* Phase 2: 尾部退避 120s → 240s → 480s (第 8~10 次)
|
|
999
|
+
* Phase 3: 持续重试 300s (5 分钟) 间隔,永不放弃
|
|
1000
|
+
*
|
|
993
1001
|
* Wild-Goose 离线消息: 重连后网关自动重发 (Redis 缓存 7 天),
|
|
994
1002
|
* 插件的 dedup.ts 会过滤重复消息。
|
|
995
1003
|
*/
|
|
@@ -1004,13 +1012,17 @@ interface ConnectionManagerOptions {
|
|
|
1004
1012
|
reconnectBaseMs?: number;
|
|
1005
1013
|
/** 重连最大退避 (ms),默认 60s */
|
|
1006
1014
|
reconnectMaxMs?: number;
|
|
1007
|
-
/**
|
|
1015
|
+
/** Phase 1 最大重连尝试次数 (含 Phase 2 尾部),默认 10 */
|
|
1008
1016
|
maxReconnectAttempts?: number;
|
|
1017
|
+
/** Phase 2 尾部退避延迟序列 (ms),从 maxReconnectAttempts 末尾倒数,默认 [120s, 240s, 480s] */
|
|
1018
|
+
tailBackoffDelays?: number[];
|
|
1019
|
+
/** Phase 3 持续重试间隔 (ms),超过 maxReconnectAttempts 后使用,默认 300s (5 分钟) */
|
|
1020
|
+
persistentRetryIntervalMs?: number;
|
|
1009
1021
|
}
|
|
1010
1022
|
/**
|
|
1011
1023
|
* 连接管理器 — 管理 WebSocket 的生命周期。
|
|
1012
1024
|
* - 心跳保活: 每 30s 检查连接状态
|
|
1013
|
-
* -
|
|
1025
|
+
* - 三阶段重连: 指数退避 → 尾部退避 → 持续重试 (永不放弃)
|
|
1014
1026
|
*/
|
|
1015
1027
|
declare class ConnectionManager {
|
|
1016
1028
|
private readonly client;
|
|
@@ -1051,7 +1063,15 @@ declare class ConnectionManager {
|
|
|
1051
1063
|
private stopHeartbeat;
|
|
1052
1064
|
/** 调度重连 (防止并发重连) */
|
|
1053
1065
|
private scheduleReconnect;
|
|
1054
|
-
/**
|
|
1066
|
+
/**
|
|
1067
|
+
* 计算当前重连延迟 — 三阶段退避策略:
|
|
1068
|
+
*
|
|
1069
|
+
* Phase 1 (attempt 0 ~ tailStart-1): 指数退避 1s → 60s
|
|
1070
|
+
* Phase 2 (attempt tailStart ~ maxReconnectAttempts-1): 尾部退避 120s/240s/480s
|
|
1071
|
+
* Phase 3 (attempt >= maxReconnectAttempts): 持续重试 300s
|
|
1072
|
+
*/
|
|
1073
|
+
private computeReconnectDelay;
|
|
1074
|
+
/** 三阶段退避重连 — 永不放弃 */
|
|
1055
1075
|
private reconnect;
|
|
1056
1076
|
/** 停止连接 — 清理心跳定时器 + 关闭 WS */
|
|
1057
1077
|
stop(): Promise<void>;
|
package/dist/setup-entry.cjs
CHANGED
|
@@ -4727,6 +4727,48 @@ var TTLMap = class {
|
|
|
4727
4727
|
}
|
|
4728
4728
|
};
|
|
4729
4729
|
|
|
4730
|
+
// src/utils/msg-type-detect.ts
|
|
4731
|
+
var MARKDOWN_PATTERNS = [
|
|
4732
|
+
// ── 块级元素 (高置信度) ──
|
|
4733
|
+
// 围栏代码块: ```language
|
|
4734
|
+
{ pattern: /^```/m, label: "fenced-code-block" },
|
|
4735
|
+
// 标题: # Title (行首,# 后必须跟空格)
|
|
4736
|
+
{ pattern: /^#{1,6}\s+\S/m, label: "heading" },
|
|
4737
|
+
// 表格: | cell | cell | (行首行尾都有 |,且至少有 2 个分隔符)
|
|
4738
|
+
{ pattern: /^\|.+\|.+\|\s*$/m, label: "table" },
|
|
4739
|
+
// 引用块: > text (行首)
|
|
4740
|
+
{ pattern: /^>\s+\S/m, label: "blockquote" },
|
|
4741
|
+
// 分隔线: --- 或 *** (独占一行,至少 3 个)
|
|
4742
|
+
{ pattern: /^[-*]{3,}\s*$/m, label: "horizontal-rule" },
|
|
4743
|
+
// ── 内联元素 (中置信度) ──
|
|
4744
|
+
// 粗体: **text** 或 __text__
|
|
4745
|
+
{ pattern: /\*\*[^*]+\*\*/, label: "bold-asterisk" },
|
|
4746
|
+
{ pattern: /__[^_]+__/, label: "bold-underscore" },
|
|
4747
|
+
// 链接: [text](url)
|
|
4748
|
+
{ pattern: /\[[^\]]+\]\([^)]+\)/, label: "link" },
|
|
4749
|
+
// 图片: 
|
|
4750
|
+
{ pattern: /!\[[^\]]*\]\([^)]+\)/, label: "image" },
|
|
4751
|
+
// 行内代码: `code` (但不是 ```)
|
|
4752
|
+
{ pattern: /(?<!`)`[^`\n]+`(?!`)/, label: "inline-code" },
|
|
4753
|
+
// ── 列表 (需行首匹配) ──
|
|
4754
|
+
// 有序列表: 1. text
|
|
4755
|
+
{ pattern: /^\d+\.\s+\S/m, label: "ordered-list" },
|
|
4756
|
+
// 无序列表: - text 或 * text (行首,后跟空格和非空字符)
|
|
4757
|
+
{ pattern: /^[-*]\s+\S/m, label: "unordered-list" }
|
|
4758
|
+
];
|
|
4759
|
+
function isMarkdownContent(text) {
|
|
4760
|
+
if (!text) return false;
|
|
4761
|
+
for (const { pattern } of MARKDOWN_PATTERNS) {
|
|
4762
|
+
if (pattern.test(text)) {
|
|
4763
|
+
return true;
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
return false;
|
|
4767
|
+
}
|
|
4768
|
+
function resolveTextMsgType(text) {
|
|
4769
|
+
return isMarkdownContent(text) ? "markdown" : "text";
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4730
4772
|
// src/channel/outbound.ts
|
|
4731
4773
|
var log7 = createLogger("channel/outbound");
|
|
4732
4774
|
var messageEncryptionStatus = new TTLMap({
|
|
@@ -4800,7 +4842,7 @@ var quantumImOutbound = {
|
|
|
4800
4842
|
await messagePipe.sendMessage({
|
|
4801
4843
|
chatId,
|
|
4802
4844
|
senderId: chatId,
|
|
4803
|
-
msgType:
|
|
4845
|
+
msgType: resolveTextMsgType(text),
|
|
4804
4846
|
content: JSON.stringify({ content: text }),
|
|
4805
4847
|
skipEncrypt
|
|
4806
4848
|
});
|
|
@@ -18690,6 +18732,8 @@ var DEFAULT_INTERNAL_CONFIG = {
|
|
|
18690
18732
|
reconnectBaseMs: 1e3,
|
|
18691
18733
|
reconnectMaxMs: 6e4,
|
|
18692
18734
|
maxReconnectAttempts: 10,
|
|
18735
|
+
tailBackoffDelays: [12e4, 24e4, 48e4],
|
|
18736
|
+
persistentRetryIntervalMs: 3e5,
|
|
18693
18737
|
dedup: {
|
|
18694
18738
|
ttlMs: 3e5,
|
|
18695
18739
|
maxEntries: 5e3
|
|
@@ -20227,7 +20271,7 @@ var MessagePipe = class _MessagePipe {
|
|
|
20227
20271
|
msgUid: callbackData.msgUid,
|
|
20228
20272
|
reason
|
|
20229
20273
|
});
|
|
20230
|
-
const hintText = this.
|
|
20274
|
+
const hintText = !this.quantumAccount ? HINT_NO_QUANTUM_ACCOUNT : "\u26A0\uFE0F \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u89E3\u5BC6\u52A0\u5BC6\u6D88\u606F\u3002\u8BF7\u53D1\u9001\u660E\u6587\u6D88\u606F\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
20231
20275
|
await this.sendHintMessage(callbackData, hintText);
|
|
20232
20276
|
return;
|
|
20233
20277
|
}
|
|
@@ -20281,7 +20325,7 @@ var MessagePipe = class _MessagePipe {
|
|
|
20281
20325
|
await this.sendMessage({
|
|
20282
20326
|
chatId,
|
|
20283
20327
|
senderId,
|
|
20284
|
-
msgType:
|
|
20328
|
+
msgType: resolveTextMsgType(hintText),
|
|
20285
20329
|
content: JSON.stringify({ content: hintText }),
|
|
20286
20330
|
skipEncrypt: true
|
|
20287
20331
|
});
|
|
@@ -20346,7 +20390,9 @@ var ConnectionManager = class {
|
|
|
20346
20390
|
heartbeatTimeoutMs: options?.heartbeatTimeoutMs ?? 1e4,
|
|
20347
20391
|
reconnectBaseMs: options?.reconnectBaseMs ?? 1e3,
|
|
20348
20392
|
reconnectMaxMs: options?.reconnectMaxMs ?? 6e4,
|
|
20349
|
-
maxReconnectAttempts: options?.maxReconnectAttempts ?? 10
|
|
20393
|
+
maxReconnectAttempts: options?.maxReconnectAttempts ?? 10,
|
|
20394
|
+
tailBackoffDelays: options?.tailBackoffDelays ?? [12e4, 24e4, 48e4],
|
|
20395
|
+
persistentRetryIntervalMs: options?.persistentRetryIntervalMs ?? 3e5
|
|
20350
20396
|
};
|
|
20351
20397
|
log17.info("ConnectionManager initialized", this.options);
|
|
20352
20398
|
}
|
|
@@ -20457,14 +20503,46 @@ var ConnectionManager = class {
|
|
|
20457
20503
|
this.reconnecting = false;
|
|
20458
20504
|
});
|
|
20459
20505
|
}
|
|
20460
|
-
/**
|
|
20506
|
+
/**
|
|
20507
|
+
* 计算当前重连延迟 — 三阶段退避策略:
|
|
20508
|
+
*
|
|
20509
|
+
* Phase 1 (attempt 0 ~ tailStart-1): 指数退避 1s → 60s
|
|
20510
|
+
* Phase 2 (attempt tailStart ~ maxReconnectAttempts-1): 尾部退避 120s/240s/480s
|
|
20511
|
+
* Phase 3 (attempt >= maxReconnectAttempts): 持续重试 300s
|
|
20512
|
+
*/
|
|
20513
|
+
computeReconnectDelay() {
|
|
20514
|
+
const { reconnectBaseMs, reconnectMaxMs, maxReconnectAttempts, tailBackoffDelays, persistentRetryIntervalMs } = this.options;
|
|
20515
|
+
const attempt = this.reconnectAttempts;
|
|
20516
|
+
if (attempt >= maxReconnectAttempts) {
|
|
20517
|
+
return { delay: persistentRetryIntervalMs, phase: "persistent" };
|
|
20518
|
+
}
|
|
20519
|
+
const tailStart = maxReconnectAttempts - tailBackoffDelays.length;
|
|
20520
|
+
if (attempt >= tailStart) {
|
|
20521
|
+
return { delay: tailBackoffDelays[attempt - tailStart], phase: "tail" };
|
|
20522
|
+
}
|
|
20523
|
+
return {
|
|
20524
|
+
delay: Math.min(reconnectBaseMs * 2 ** attempt, reconnectMaxMs),
|
|
20525
|
+
phase: "exponential"
|
|
20526
|
+
};
|
|
20527
|
+
}
|
|
20528
|
+
/** 三阶段退避重连 — 永不放弃 */
|
|
20461
20529
|
async reconnect() {
|
|
20462
|
-
while (this.running
|
|
20463
|
-
const delay =
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
|
|
20467
|
-
|
|
20530
|
+
while (this.running) {
|
|
20531
|
+
const { delay, phase } = this.computeReconnectDelay();
|
|
20532
|
+
const isPersistent = phase === "persistent";
|
|
20533
|
+
if (isPersistent) {
|
|
20534
|
+
log17.warn("ws:persistent-retry", {
|
|
20535
|
+
attempt: this.reconnectAttempts + 1,
|
|
20536
|
+
delayMs: delay,
|
|
20537
|
+
message: "\u5DF2\u8D85\u8FC7\u6700\u5927\u91CD\u8FDE\u6B21\u6570\uFF0C\u8FDB\u5165\u6301\u7EED\u91CD\u8BD5\u6A21\u5F0F (\u6BCF 5 \u5206\u949F)"
|
|
20538
|
+
});
|
|
20539
|
+
} else {
|
|
20540
|
+
log17.info("ws:reconnecting", {
|
|
20541
|
+
attempt: this.reconnectAttempts + 1,
|
|
20542
|
+
delayMs: delay,
|
|
20543
|
+
phase
|
|
20544
|
+
});
|
|
20545
|
+
}
|
|
20468
20546
|
await sleep2(delay);
|
|
20469
20547
|
if (!this.running) return;
|
|
20470
20548
|
this.reconnectAttempts++;
|
|
@@ -20480,23 +20558,20 @@ var ConnectionManager = class {
|
|
|
20480
20558
|
...this.appId ? { "X-App-ID": this.appId } : {}
|
|
20481
20559
|
}
|
|
20482
20560
|
});
|
|
20561
|
+
const totalAttempts = this.reconnectAttempts;
|
|
20483
20562
|
this.reconnectAttempts = 0;
|
|
20484
20563
|
this.registerEvents();
|
|
20485
20564
|
this.startHeartbeat();
|
|
20486
|
-
log17.info("ws:reconnected", {
|
|
20565
|
+
log17.info("ws:reconnected", { totalAttempts, url: this.url });
|
|
20487
20566
|
return;
|
|
20488
20567
|
} catch (err) {
|
|
20489
20568
|
log17.warn("ws:reconnect-failed", {
|
|
20490
20569
|
attempt: this.reconnectAttempts,
|
|
20570
|
+
phase,
|
|
20491
20571
|
error: err.message
|
|
20492
20572
|
});
|
|
20493
20573
|
}
|
|
20494
20574
|
}
|
|
20495
|
-
if (this.running) {
|
|
20496
|
-
log17.error("ws:max-reconnect-reached", {
|
|
20497
|
-
maxAttempts: this.options.maxReconnectAttempts
|
|
20498
|
-
});
|
|
20499
|
-
}
|
|
20500
20575
|
}
|
|
20501
20576
|
/** 停止连接 — 清理心跳定时器 + 关闭 WS */
|
|
20502
20577
|
async stop() {
|
|
@@ -21387,7 +21462,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
21387
21462
|
await messagePipe.sendMessage({
|
|
21388
21463
|
chatId,
|
|
21389
21464
|
senderId,
|
|
21390
|
-
msgType:
|
|
21465
|
+
msgType: resolveTextMsgType(payload.text),
|
|
21391
21466
|
content: JSON.stringify({ content: payload.text }),
|
|
21392
21467
|
replyToMessageId,
|
|
21393
21468
|
skipEncrypt: shouldSkipEncrypt
|
|
@@ -21426,11 +21501,12 @@ function createQuantumImDeliverFn(deps) {
|
|
|
21426
21501
|
mediaUrl,
|
|
21427
21502
|
error: err.message
|
|
21428
21503
|
});
|
|
21504
|
+
const fallbackText = `[\u{1F4CE} \u6587\u4EF6\u94FE\u63A5](${mediaUrl})`;
|
|
21429
21505
|
await messagePipe.sendMessage({
|
|
21430
21506
|
chatId,
|
|
21431
21507
|
senderId,
|
|
21432
|
-
msgType:
|
|
21433
|
-
content: JSON.stringify({ content:
|
|
21508
|
+
msgType: resolveTextMsgType(fallbackText),
|
|
21509
|
+
content: JSON.stringify({ content: fallbackText }),
|
|
21434
21510
|
replyToMessageId,
|
|
21435
21511
|
skipEncrypt: shouldSkipEncrypt
|
|
21436
21512
|
});
|
|
@@ -21542,7 +21618,7 @@ var InboundPipeline = class {
|
|
|
21542
21618
|
await this.deps.messagePipe.sendMessage({
|
|
21543
21619
|
chatId: msg.chatId,
|
|
21544
21620
|
senderId: msg.senderId,
|
|
21545
|
-
msgType:
|
|
21621
|
+
msgType: resolveTextMsgType(text),
|
|
21546
21622
|
content: JSON.stringify({ content: text }),
|
|
21547
21623
|
skipEncrypt: cmdSkipEncrypt
|
|
21548
21624
|
});
|
|
@@ -21586,7 +21662,7 @@ var InboundPipeline = class {
|
|
|
21586
21662
|
await this.deps.messagePipe.sendMessage({
|
|
21587
21663
|
chatId: msg.chatId,
|
|
21588
21664
|
senderId: msg.senderId,
|
|
21589
|
-
msgType:
|
|
21665
|
+
msgType: resolveTextMsgType(fallbackText),
|
|
21590
21666
|
content: JSON.stringify({ content: fallbackText }),
|
|
21591
21667
|
skipEncrypt: cmdSkipEncrypt
|
|
21592
21668
|
}).catch((err) => {
|