liangzimixin 0.3.69 → 0.3.71

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 CHANGED
@@ -18488,12 +18488,25 @@ async function resolveAndUploadMedia(params) {
18488
18488
  timeoutMs
18489
18489
  });
18490
18490
  } catch (err) {
18491
- log5.warn("media:upload failed, \u964D\u7EA7\u5904\u7406", { error: err.message });
18491
+ const errMsg = err.message;
18492
+ log5.warn("media:upload failed, \u964D\u7EA7\u5904\u7406", { error: errMsg });
18493
+ try {
18494
+ const tipText = "\u26A0\uFE0F \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
18495
+ await messagePipe.sendMessage({
18496
+ chatId,
18497
+ senderId: chatId,
18498
+ msgType: "text",
18499
+ content: JSON.stringify({ content: tipText }),
18500
+ skipEncrypt: true
18501
+ });
18502
+ } catch (tipErr) {
18503
+ log5.warn("media:upload-failed tip send error", { error: tipErr.message });
18504
+ }
18492
18505
  return {
18493
18506
  channel: CHANNEL_ID,
18494
18507
  messageId: "",
18495
18508
  chatId,
18496
- warning: `\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${err.message}`
18509
+ warning: `\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${errMsg}`
18497
18510
  };
18498
18511
  }
18499
18512
  const msgType = fileType;
@@ -18519,7 +18532,7 @@ async function resolveAndUploadMedia(params) {
18519
18532
  content: JSON.stringify(contentPayload),
18520
18533
  skipEncrypt: params.skipEncrypt,
18521
18534
  encryptionMeta
18522
- // 文件加密的 keyId/iv → sendMessage 用于构建 extra
18535
+ // 文件加密的 keyId/iv → sendMessage 用于构建 extraContent
18523
18536
  });
18524
18537
  log5.info("media:sent", {
18525
18538
  chatId,
@@ -18759,6 +18772,10 @@ var quantumImOutbound = {
18759
18772
  /** 发送文本消息 — 通过 messagePipe 发送 */
18760
18773
  sendText: async ({ cfg, to, text, accountId }) => {
18761
18774
  log7.info("sendText called", { to, textLength: text.length });
18775
+ if (!text?.trim()) {
18776
+ log7.warn("outbound:sendText skipped \u2014 empty text", { to });
18777
+ return { channel: CHANNEL_ID, messageId: "", chatId: to };
18778
+ }
18762
18779
  if (!messagePipeGetter) {
18763
18780
  log7.error("outbound:sendText failed, messagePipe not initialized");
18764
18781
  return { channel: CHANNEL_ID, messageId: "", chatId: to };
@@ -19121,19 +19138,27 @@ function createQuantumImDeliverFn(deps) {
19121
19138
  const hasMedia = mediaUrls.length > 0;
19122
19139
  if (!hasText && !hasMedia) return;
19123
19140
  if (hasText) {
19124
- await messagePipe.sendMessage({
19125
- chatId,
19126
- senderId,
19127
- msgType: resolveTextMsgType(payload.text),
19128
- content: JSON.stringify({ content: payload.text }),
19129
- replyToMessageId,
19130
- skipEncrypt: shouldSkipEncrypt
19131
- });
19132
- log14.info("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u5DF2\u53D1\u9001", {
19133
- chatId,
19134
- \u957F\u5EA6: payload.text.length,
19135
- \u56DE\u590D\u9884\u89C8: payload.text.slice(0, 200)
19136
- });
19141
+ try {
19142
+ await messagePipe.sendMessage({
19143
+ chatId,
19144
+ senderId,
19145
+ msgType: resolveTextMsgType(payload.text),
19146
+ content: JSON.stringify({ content: payload.text }),
19147
+ replyToMessageId,
19148
+ skipEncrypt: shouldSkipEncrypt
19149
+ });
19150
+ log14.info("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u5DF2\u53D1\u9001", {
19151
+ chatId,
19152
+ \u957F\u5EA6: payload.text.length,
19153
+ \u56DE\u590D\u9884\u89C8: payload.text.slice(0, 200)
19154
+ });
19155
+ } catch (err) {
19156
+ log14.error("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u53D1\u9001\u5931\u8D25", {
19157
+ chatId,
19158
+ error: err.message
19159
+ });
19160
+ if (!hasMedia) throw err;
19161
+ }
19137
19162
  }
19138
19163
  if (hasMedia) {
19139
19164
  for (const mediaUrl of mediaUrls) {
@@ -19425,6 +19450,21 @@ var InboundPipeline = class {
19425
19450
  stack: err.stack,
19426
19451
  durationMs
19427
19452
  });
19453
+ try {
19454
+ const errorTip = "\u26A0\uFE0F \u6D88\u606F\u5904\u7406\u9047\u5230\u95EE\u9898\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
19455
+ await this.deps.messagePipe.sendMessage({
19456
+ chatId: msg.chatId,
19457
+ senderId: msg.senderId,
19458
+ msgType: resolveTextMsgType(errorTip),
19459
+ content: JSON.stringify({ content: errorTip }),
19460
+ skipEncrypt: true
19461
+ // 系统提示消息不加密,避免加密失败导致的死循环
19462
+ });
19463
+ } catch (tipErr) {
19464
+ log15.warn("\u26A0\uFE0F \u9519\u8BEF\u63D0\u793A\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF08\u907F\u514D\u6B7B\u5FAA\u73AF\uFF0C\u4E0D\u518D\u91CD\u8BD5\uFF09", {
19465
+ error: tipErr.message
19466
+ });
19467
+ }
19428
19468
  clearInboundEncryptionStatus(msg.chatId, msg.messageId);
19429
19469
  } finally {
19430
19470
  semaphore.release();
@@ -20918,7 +20958,7 @@ var CallbackDataSchema = external_exports.object({
20918
20958
  msgUid: external_exports.string(),
20919
20959
  type: external_exports.string(),
20920
20960
  content: external_exports.record(external_exports.string(), external_exports.unknown()),
20921
- extra: external_exports.string().optional()
20961
+ extraContent: external_exports.string().optional()
20922
20962
  });
20923
20963
  var MessagePipe = class _MessagePipe {
20924
20964
  wsClient;
@@ -21035,8 +21075,8 @@ var MessagePipe = class _MessagePipe {
21035
21075
  /** 文件类消息类型集合 — 这些类型的加密消息保留原始 content(文件元数据) */
21036
21076
  static FILE_MSG_TYPES = /* @__PURE__ */ new Set(["image", "file", "voice", "video"]);
21037
21077
  /**
21038
- * 构建文件类加密消息的 extra — 完整模板格式。
21039
- * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extra 中携带会话信息。
21078
+ * 构建文件类加密消息的 extraContent — 完整模板格式。
21079
+ * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extraContent 中携带会话信息。
21040
21080
  */
21041
21081
  static buildFileEncryptExtra(iv, keyId) {
21042
21082
  return JSON.stringify({
@@ -21068,27 +21108,27 @@ var MessagePipe = class _MessagePipe {
21068
21108
  *
21069
21109
  * 文件类消息 (image/file/voice/video):
21070
21110
  * - content 保留原始文件元数据(fileId、fileName、size 等)
21071
- * - extra 使用完整模板格式,encryptMsg 为空
21111
+ * - extraContent 使用完整模板格式,encryptMsg 为空
21072
21112
  *
21073
21113
  * 文本类消息 (text/markdown):
21074
- * - content 加密后置空,密文放入 extra.encryptMsg
21114
+ * - content 加密后置空,密文放入 extraContent.encryptMsg
21075
21115
  */
21076
21116
  async sendMessage(msg) {
21077
21117
  let content = msg.content;
21078
- let extra = "";
21118
+ let extraContent = "";
21079
21119
  const isFileMessage = _MessagePipe.FILE_MSG_TYPES.has(msg.msgType);
21080
21120
  if (!msg.skipEncrypt && this.quantumAccount) {
21081
21121
  if (this.encryptionMode === "quantum_only") {
21082
21122
  if (isFileMessage && msg.encryptionMeta) {
21083
- extra = _MessagePipe.buildFileEncryptExtra(msg.encryptionMeta.iv, msg.encryptionMeta.keyId);
21123
+ extraContent = _MessagePipe.buildFileEncryptExtra(msg.encryptionMeta.iv, msg.encryptionMeta.keyId);
21084
21124
  log25.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_only)", { sessionId: msg.encryptionMeta.keyId, msgType: msg.msgType });
21085
21125
  } else if (isFileMessage) {
21086
21126
  const { keyId, iv } = await this.crypto.encrypt(msg.content);
21087
- extra = _MessagePipe.buildFileEncryptExtra(iv, keyId);
21127
+ extraContent = _MessagePipe.buildFileEncryptExtra(iv, keyId);
21088
21128
  log25.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_only, fallback)", { sessionId: keyId, msgType: msg.msgType });
21089
21129
  } else {
21090
21130
  const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
21091
- extra = JSON.stringify({
21131
+ extraContent = JSON.stringify({
21092
21132
  cryptoIv: iv,
21093
21133
  encryptMsg: ciphertext,
21094
21134
  sessionId: keyId
@@ -21099,15 +21139,15 @@ var MessagePipe = class _MessagePipe {
21099
21139
  } else {
21100
21140
  try {
21101
21141
  if (isFileMessage && msg.encryptionMeta) {
21102
- extra = _MessagePipe.buildFileEncryptExtra(msg.encryptionMeta.iv, msg.encryptionMeta.keyId);
21142
+ extraContent = _MessagePipe.buildFileEncryptExtra(msg.encryptionMeta.iv, msg.encryptionMeta.keyId);
21103
21143
  log25.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_and_plain)", { sessionId: msg.encryptionMeta.keyId, msgType: msg.msgType });
21104
21144
  } else if (isFileMessage) {
21105
21145
  const { keyId, iv } = await this.crypto.encrypt(msg.content);
21106
- extra = _MessagePipe.buildFileEncryptExtra(iv, keyId);
21146
+ extraContent = _MessagePipe.buildFileEncryptExtra(iv, keyId);
21107
21147
  log25.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_and_plain, fallback)", { sessionId: keyId, msgType: msg.msgType });
21108
21148
  } else {
21109
21149
  const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
21110
- extra = JSON.stringify({
21150
+ extraContent = JSON.stringify({
21111
21151
  cryptoIv: iv,
21112
21152
  encryptMsg: ciphertext,
21113
21153
  sessionId: keyId
@@ -21136,17 +21176,19 @@ var MessagePipe = class _MessagePipe {
21136
21176
  msg_type: msg.msgType,
21137
21177
  content
21138
21178
  };
21139
- if (extra) {
21140
- body.extra = extra;
21179
+ if (extraContent) {
21180
+ body.extraContent = extraContent;
21141
21181
  }
21142
21182
  if (msg.replyToMessageId) {
21143
21183
  body.reply_msg_id = msg.replyToMessageId;
21144
21184
  }
21145
21185
  const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
21146
- if (!result) return;
21186
+ if (!result) {
21187
+ throw new Error(`\u6D88\u606F\u53D1\u9001\u5931\u8D25: POST ${this.messageServiceBaseUrl}/messages/v1/send \u672A\u8FD4\u56DE\u6709\u6548\u54CD\u5E94`);
21188
+ }
21147
21189
  log25.info("\u{1F4E4} outbound:sent \u2192 IM \u670D\u52A1\u5668", {
21148
21190
  chatId: msg.chatId,
21149
- encrypted: Boolean(extra),
21191
+ encrypted: Boolean(extraContent),
21150
21192
  requestId: result.request_id,
21151
21193
  body
21152
21194
  });
@@ -21167,7 +21209,10 @@ var MessagePipe = class _MessagePipe {
21167
21209
  conversation_type: ""
21168
21210
  };
21169
21211
  const result = await this.callMessageApi("/messages/v1/recall", body, "recall");
21170
- if (!result) return;
21212
+ if (!result) {
21213
+ log25.warn("recall:failed \u2014 callMessageApi returned null");
21214
+ return;
21215
+ }
21171
21216
  log25.info("recall:sent", {
21172
21217
  messageId: params.messageId,
21173
21218
  requestId: result.request_id
@@ -21182,6 +21227,43 @@ var MessagePipe = class _MessagePipe {
21182
21227
  * @param body - 请求体
21183
21228
  * @param logTag - 日志标签前缀 (如 'outbound' / 'recall')
21184
21229
  */
21230
+ /** 单次 HTTP 请求封装 — callMessageApi 内部使用 */
21231
+ async _callMessageApiOnce(url2, body, logTag) {
21232
+ const apiStartMs = Date.now();
21233
+ const token = await this.tokenFn();
21234
+ const resp = await fetch(url2, {
21235
+ method: "POST",
21236
+ headers: {
21237
+ "Authorization": `Bearer ${token}`,
21238
+ "Content-Type": "application/json"
21239
+ },
21240
+ body: JSON.stringify(body)
21241
+ });
21242
+ if (!resp.ok) {
21243
+ log25.error(`${logTag}:http-error`, {
21244
+ status: resp.status,
21245
+ statusText: resp.statusText,
21246
+ url: url2
21247
+ });
21248
+ return { code: -1, msg: `HTTP ${resp.status}`, _retryable: resp.status >= 500 };
21249
+ }
21250
+ const result = await resp.json();
21251
+ if (result.code !== 0 && result.code !== 200) {
21252
+ log25.error(`${logTag}:api-error`, {
21253
+ code: result.code,
21254
+ msg: result.msg,
21255
+ requestId: result.request_id
21256
+ });
21257
+ return null;
21258
+ }
21259
+ metrics.increment("outbound.success");
21260
+ metrics.recordLatency("outbound.latency", Date.now() - apiStartMs);
21261
+ return result;
21262
+ }
21263
+ /**
21264
+ * 消息服务 API 公共调用方法 — 含 1 次重试 (仅限 5xx / 网络错误)。
21265
+ * 失败返回 null(不抛异常),错误通过 log 记录。
21266
+ */
21185
21267
  async callMessageApi(path3, body, logTag) {
21186
21268
  const url2 = `${this.messageServiceBaseUrl}${path3}`;
21187
21269
  const rateLimitResult = await this.rateLimiter.acquire();
@@ -21194,44 +21276,42 @@ var MessagePipe = class _MessagePipe {
21194
21276
  return null;
21195
21277
  }
21196
21278
  try {
21197
- const apiStartMs = Date.now();
21198
- const token = await this.tokenFn();
21199
- const resp = await fetch(url2, {
21200
- method: "POST",
21201
- headers: {
21202
- "Authorization": `Bearer ${token}`,
21203
- "Content-Type": "application/json"
21204
- },
21205
- body: JSON.stringify(body)
21206
- });
21207
- if (!resp.ok) {
21208
- log25.error(`${logTag}:http-error`, {
21209
- status: resp.status,
21210
- statusText: resp.statusText,
21211
- url: url2
21212
- });
21279
+ const result = await this._callMessageApiOnce(url2, body, logTag);
21280
+ if (result && "_retryable" in result) {
21281
+ if (result._retryable) {
21282
+ log25.warn(`${logTag}:retrying after 5xx`, { url: url2 });
21283
+ await new Promise((r) => setTimeout(r, 1e3));
21284
+ try {
21285
+ const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
21286
+ if (retryResult && !("_retryable" in retryResult)) {
21287
+ return retryResult;
21288
+ }
21289
+ } catch (retryErr) {
21290
+ log25.error(`${logTag}:retry-failed`, { error: retryErr.message });
21291
+ }
21292
+ }
21213
21293
  metrics.increment("outbound.failed");
21214
21294
  return null;
21215
21295
  }
21216
- const result = await resp.json();
21217
- if (result.code !== 0 && result.code !== 200) {
21218
- log25.error(`${logTag}:api-error`, {
21219
- code: result.code,
21220
- msg: result.msg,
21221
- requestId: result.request_id
21296
+ return result;
21297
+ } catch (err) {
21298
+ log25.warn(`${logTag}:network-error, retrying`, { url: url2, error: err.message });
21299
+ await new Promise((r) => setTimeout(r, 1e3));
21300
+ try {
21301
+ const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
21302
+ if (retryResult && !("_retryable" in retryResult)) {
21303
+ return retryResult;
21304
+ }
21305
+ metrics.increment("outbound.failed");
21306
+ return null;
21307
+ } catch (retryErr) {
21308
+ metrics.increment("outbound.failed");
21309
+ log25.error(`${logTag}:retry-network-error`, {
21310
+ url: url2,
21311
+ error: retryErr.message
21222
21312
  });
21223
21313
  return null;
21224
21314
  }
21225
- metrics.increment("outbound.success");
21226
- metrics.recordLatency("outbound.latency", Date.now() - apiStartMs);
21227
- return result;
21228
- } catch (err) {
21229
- metrics.increment("outbound.failed");
21230
- log25.error(`${logTag}:network-error`, {
21231
- url: url2,
21232
- error: err.message
21233
- });
21234
- return null;
21235
21315
  }
21236
21316
  }
21237
21317
  /**
@@ -21298,16 +21378,16 @@ var MessagePipe = class _MessagePipe {
21298
21378
  groupId: callbackData.groupId,
21299
21379
  type: callbackData.type,
21300
21380
  content: callbackData.content,
21301
- extra: callbackData.extra
21381
+ extraContent: callbackData.extraContent
21302
21382
  });
21303
- const extra = callbackData.extra;
21383
+ const extraContent = callbackData.extraContent;
21304
21384
  let isEncrypted = false;
21305
- if (typeof extra === "string" && extra.length > 0) {
21385
+ if (typeof extraContent === "string" && extraContent.length > 0) {
21306
21386
  try {
21307
- const parsed = JSON.parse(extra);
21387
+ const parsed = JSON.parse(extraContent);
21308
21388
  isEncrypted = Boolean(parsed.encryptMsg);
21309
21389
  } catch {
21310
- log25.debug("\u2139\uFE0F extra \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u89C6\u4E3A\u660E\u6587", { msgUid: callbackData.msgUid });
21390
+ log25.debug("\u2139\uFE0F extraContent \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u89C6\u4E3A\u660E\u6587", { msgUid: callbackData.msgUid });
21311
21391
  }
21312
21392
  }
21313
21393
  let contentObj;
@@ -21340,6 +21420,10 @@ var MessagePipe = class _MessagePipe {
21340
21420
  msgUid: callbackData.msgUid,
21341
21421
  error: err.message
21342
21422
  });
21423
+ await this.sendHintMessage(
21424
+ callbackData,
21425
+ "\u26A0\uFE0F \u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
21426
+ );
21343
21427
  return;
21344
21428
  }
21345
21429
  } else {
@@ -21361,6 +21445,10 @@ var MessagePipe = class _MessagePipe {
21361
21445
  msgUid: callbackData.msgUid,
21362
21446
  error: err.message
21363
21447
  });
21448
+ await this.sendHintMessage(
21449
+ callbackData,
21450
+ "\u26A0\uFE0F \u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
21451
+ );
21364
21452
  return;
21365
21453
  }
21366
21454
  } else {
@@ -21414,15 +21502,15 @@ var MessagePipe = class _MessagePipe {
21414
21502
  }
21415
21503
  }
21416
21504
  /**
21417
- * 解密 extra 中的加密内容。
21505
+ * 解密 extraContent 中的加密内容。
21418
21506
  * @throws 解密失败时抛出异常
21419
21507
  */
21420
21508
  async decryptExtra(callbackData) {
21421
- const extraData = JSON.parse(callbackData.extra);
21509
+ const extraData = JSON.parse(callbackData.extraContent);
21422
21510
  const encryptMsg = extraData.encryptMsg ?? "";
21423
21511
  const sessionId = extraData.sessionId ?? "";
21424
21512
  if (!encryptMsg) {
21425
- log25.warn("\u26A0\uFE0F extra \u4E2D encryptMsg \u4E3A\u7A7A\uFF0C\u4F7F\u7528\u539F\u59CB content", { msgUid: callbackData.msgUid });
21513
+ log25.warn("\u26A0\uFE0F extraContent \u4E2D encryptMsg \u4E3A\u7A7A\uFF0C\u4F7F\u7528\u539F\u59CB content", { msgUid: callbackData.msgUid });
21426
21514
  return callbackData.content;
21427
21515
  }
21428
21516
  const cryptoIv = extraData.cryptoIv ?? "";
package/dist/index.d.cts CHANGED
@@ -214,7 +214,7 @@ interface OutboundMessage {
214
214
  replyToMessageId?: string;
215
215
  /** 跳过出站加密 — 用于"思考中"等无需加密的系统提示消息 */
216
216
  skipEncrypt?: boolean;
217
- /** 文件加密元数据 — 文件已在上传前加密时传入,sendMessage 直接使用此 keyId/iv 构建 extra */
217
+ /** 文件加密元数据 — 文件已在上传前加密时传入,sendMessage 直接使用此 keyId/iv 构建 extraContent */
218
218
  encryptionMeta?: {
219
219
  keyId: string;
220
220
  iv: string;
@@ -925,8 +925,8 @@ declare class MessagePipe {
925
925
  /** 文件类消息类型集合 — 这些类型的加密消息保留原始 content(文件元数据) */
926
926
  private static readonly FILE_MSG_TYPES;
927
927
  /**
928
- * 构建文件类加密消息的 extra — 完整模板格式。
929
- * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extra 中携带会话信息。
928
+ * 构建文件类加密消息的 extraContent — 完整模板格式。
929
+ * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extraContent 中携带会话信息。
930
930
  */
931
931
  private static buildFileEncryptExtra;
932
932
  /**
@@ -940,10 +940,10 @@ declare class MessagePipe {
940
940
  *
941
941
  * 文件类消息 (image/file/voice/video):
942
942
  * - content 保留原始文件元数据(fileId、fileName、size 等)
943
- * - extra 使用完整模板格式,encryptMsg 为空
943
+ * - extraContent 使用完整模板格式,encryptMsg 为空
944
944
  *
945
945
  * 文本类消息 (text/markdown):
946
- * - content 加密后置空,密文放入 extra.encryptMsg
946
+ * - content 加密后置空,密文放入 extraContent.encryptMsg
947
947
  */
948
948
  sendMessage(msg: OutboundMessage): Promise<void>;
949
949
  /**
@@ -966,6 +966,12 @@ declare class MessagePipe {
966
966
  * @param body - 请求体
967
967
  * @param logTag - 日志标签前缀 (如 'outbound' / 'recall')
968
968
  */
969
+ /** 单次 HTTP 请求封装 — callMessageApi 内部使用 */
970
+ private _callMessageApiOnce;
971
+ /**
972
+ * 消息服务 API 公共调用方法 — 含 1 次重试 (仅限 5xx / 网络错误)。
973
+ * 失败返回 null(不抛异常),错误通过 log 记录。
974
+ */
969
975
  private callMessageApi;
970
976
  /**
971
977
  * 入站处理流水线 (7 步):
@@ -984,7 +990,7 @@ declare class MessagePipe {
984
990
  */
985
991
  private sendHintMessage;
986
992
  /**
987
- * 解密 extra 中的加密内容。
993
+ * 解密 extraContent 中的加密内容。
988
994
  * @throws 解密失败时抛出异常
989
995
  */
990
996
  private decryptExtra;