liangzimixin 0.3.44 → 0.3.46

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
@@ -20055,7 +20055,7 @@ var MessageDedup = class {
20055
20055
  // src/transport/message-pipe.ts
20056
20056
  var import_node_crypto3 = require("crypto");
20057
20057
  var log21 = createLogger("transport/message-pipe");
20058
- var MessagePipe = class {
20058
+ var MessagePipe = class _MessagePipe {
20059
20059
  wsClient;
20060
20060
  dedup;
20061
20061
  crypto;
@@ -20097,6 +20097,31 @@ var MessagePipe = class {
20097
20097
  async injectRawFrame(rawData) {
20098
20098
  return this.handleInbound(rawData);
20099
20099
  }
20100
+ /** 文件类消息类型集合 — 这些类型的加密消息保留原始 content(文件元数据) */
20101
+ static FILE_MSG_TYPES = /* @__PURE__ */ new Set(["image", "file", "voice", "video"]);
20102
+ /**
20103
+ * 构建文件类加密消息的 extra — 完整模板格式。
20104
+ * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extra 中携带会话信息。
20105
+ */
20106
+ static buildFileEncryptExtra(iv, keyId) {
20107
+ return JSON.stringify({
20108
+ containSensitive: false,
20109
+ cryptoIv: iv,
20110
+ decryptText: "",
20111
+ decrypted: false,
20112
+ destroyType: 0,
20113
+ encryptMsg: "",
20114
+ expireAt: null,
20115
+ level: 0,
20116
+ newDestroy: 1,
20117
+ phone: "",
20118
+ receiveShowTip: false,
20119
+ receiveTip: "",
20120
+ sessionId: keyId,
20121
+ showTip: false,
20122
+ tip: ""
20123
+ });
20124
+ }
20100
20125
  /**
20101
20126
  * 出站发送 — 通过 HTTP API 发送消息到 IM 服务器。
20102
20127
  * POST {messageServiceBaseUrl}/messages/v1/send
@@ -20105,30 +20130,48 @@ var MessagePipe = class {
20105
20130
  * 加密行为取决于 encryptionMode:
20106
20131
  * - quantum_only: 强制加密,失败直接抛错(不降级明文)
20107
20132
  * - quantum_and_plain: 加密失败降级明文(保持兼容)
20133
+ *
20134
+ * 文件类消息 (image/file/voice/video):
20135
+ * - content 保留原始文件元数据(fileId、fileName、size 等)
20136
+ * - extra 使用完整模板格式,encryptMsg 为空
20137
+ *
20138
+ * 文本类消息 (text/markdown):
20139
+ * - content 加密后置空,密文放入 extra.encryptMsg
20108
20140
  */
20109
20141
  async sendMessage(msg) {
20110
20142
  let content = msg.content;
20111
20143
  let extra = "";
20144
+ const isFileMessage = _MessagePipe.FILE_MSG_TYPES.has(msg.msgType);
20112
20145
  if (!msg.skipEncrypt && this.quantumAccount) {
20113
20146
  if (this.encryptionMode === "quantum_only") {
20114
20147
  const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
20115
- extra = JSON.stringify({
20116
- cryptoIv: iv,
20117
- encryptMsg: ciphertext,
20118
- sessionId: keyId
20119
- });
20120
- content = JSON.stringify({ content: "" });
20121
- log21.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_only)", { sessionId: keyId });
20122
- } else {
20123
- try {
20124
- const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
20148
+ if (isFileMessage) {
20149
+ extra = _MessagePipe.buildFileEncryptExtra(iv, keyId);
20150
+ log21.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_only)", { sessionId: keyId, msgType: msg.msgType });
20151
+ } else {
20125
20152
  extra = JSON.stringify({
20126
20153
  cryptoIv: iv,
20127
20154
  encryptMsg: ciphertext,
20128
20155
  sessionId: keyId
20129
20156
  });
20130
20157
  content = JSON.stringify({ content: "" });
20131
- log21.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId });
20158
+ log21.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_only)", { sessionId: keyId });
20159
+ }
20160
+ } else {
20161
+ try {
20162
+ const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
20163
+ if (isFileMessage) {
20164
+ extra = _MessagePipe.buildFileEncryptExtra(iv, keyId);
20165
+ log21.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId, msgType: msg.msgType });
20166
+ } else {
20167
+ extra = JSON.stringify({
20168
+ cryptoIv: iv,
20169
+ encryptMsg: ciphertext,
20170
+ sessionId: keyId
20171
+ });
20172
+ content = JSON.stringify({ content: "" });
20173
+ log21.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId });
20174
+ }
20132
20175
  } catch (err) {
20133
20176
  log21.error("\u274C \u51FA\u7AD9\u6D88\u606F\u52A0\u5BC6\u5931\u8D25\uFF0C\u5C06\u53D1\u9001\u660E\u6587", {
20134
20177
  error: err.message
@@ -20150,13 +20193,11 @@ var MessagePipe = class {
20150
20193
  }
20151
20194
  const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
20152
20195
  if (!result) return;
20153
- log21.info("outbound:sent \u2192 IM \u670D\u52A1\u5668", {
20196
+ log21.info("\u{1F4E4} outbound:sent \u2192 IM \u670D\u52A1\u5668", {
20154
20197
  chatId: msg.chatId,
20155
- receiveId: msg.senderId,
20156
- msgType: msg.msgType,
20157
20198
  encrypted: Boolean(extra),
20158
- contentPreview: extra ? "[encrypted]" : msg.content.slice(0, 200),
20159
- requestId: result.request_id
20199
+ requestId: result.request_id,
20200
+ body
20160
20201
  });
20161
20202
  }
20162
20203
  /**
@@ -20265,8 +20306,27 @@ var MessagePipe = class {
20265
20306
  log21.warn("\u26A0\uFE0F CallbackData \u89E3\u6790\u5931\u8D25", { error: err.message });
20266
20307
  return;
20267
20308
  }
20309
+ log21.info("\u{1F4E9} \u5165\u7AD9\u539F\u59CB\u6570\u636E", {
20310
+ appId: callbackData.appId,
20311
+ msgUid: callbackData.msgUid,
20312
+ eventType: callbackData.eventType,
20313
+ userId: callbackData.userId,
20314
+ toUserId: callbackData.toUserId,
20315
+ groupId: callbackData.groupId,
20316
+ type: callbackData.type,
20317
+ content: callbackData.content,
20318
+ extra: callbackData.extra
20319
+ });
20268
20320
  const extra = callbackData.extra;
20269
- const isEncrypted = typeof extra === "string" && extra.length > 0;
20321
+ let isEncrypted = false;
20322
+ if (typeof extra === "string" && extra.length > 0) {
20323
+ try {
20324
+ const parsed = JSON.parse(extra);
20325
+ isEncrypted = Boolean(parsed.encryptMsg);
20326
+ } catch {
20327
+ log21.debug("\u2139\uFE0F extra \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u89C6\u4E3A\u660E\u6587", { msgUid: callbackData.msgUid });
20328
+ }
20329
+ }
20270
20330
  let contentObj;
20271
20331
  if (this.encryptionMode === "quantum_only") {
20272
20332
  if (!this.quantumAccount) {
@@ -20457,7 +20517,7 @@ var ConnectionManager = class {
20457
20517
  this.client.on("pong", () => {
20458
20518
  this.pongReceived = true;
20459
20519
  this.clearPongTimeout();
20460
- log22.info("heartbeat: pong received");
20520
+ log22.debug("heartbeat: pong received");
20461
20521
  });
20462
20522
  }
20463
20523
  /** 启动心跳保活 — 定时发送 WebSocket Ping 帧 */
@@ -20478,7 +20538,7 @@ var ConnectionManager = class {
20478
20538
  }
20479
20539
  this.pongReceived = false;
20480
20540
  this.client.ping();
20481
- log22.info("heartbeat: ping sent");
20541
+ log22.debug("heartbeat: ping sent");
20482
20542
  this.clearPongTimeout();
20483
20543
  this.pongTimeoutTimer = setTimeout(() => {
20484
20544
  if (!this.pongReceived && this.running) {
package/dist/index.d.cts CHANGED
@@ -786,6 +786,13 @@ declare class MessagePipe {
786
786
  * TODO: 验证通过后移除此方法
787
787
  */
788
788
  injectRawFrame(rawData: string | Buffer): Promise<void>;
789
+ /** 文件类消息类型集合 — 这些类型的加密消息保留原始 content(文件元数据) */
790
+ private static readonly FILE_MSG_TYPES;
791
+ /**
792
+ * 构建文件类加密消息的 extra — 完整模板格式。
793
+ * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extra 中携带会话信息。
794
+ */
795
+ private static buildFileEncryptExtra;
789
796
  /**
790
797
  * 出站发送 — 通过 HTTP API 发送消息到 IM 服务器。
791
798
  * POST {messageServiceBaseUrl}/messages/v1/send
@@ -794,6 +801,13 @@ declare class MessagePipe {
794
801
  * 加密行为取决于 encryptionMode:
795
802
  * - quantum_only: 强制加密,失败直接抛错(不降级明文)
796
803
  * - quantum_and_plain: 加密失败降级明文(保持兼容)
804
+ *
805
+ * 文件类消息 (image/file/voice/video):
806
+ * - content 保留原始文件元数据(fileId、fileName、size 等)
807
+ * - extra 使用完整模板格式,encryptMsg 为空
808
+ *
809
+ * 文本类消息 (text/markdown):
810
+ * - content 加密后置空,密文放入 extra.encryptMsg
797
811
  */
798
812
  sendMessage(msg: OutboundMessage): Promise<void>;
799
813
  /**
@@ -19099,7 +19099,7 @@ var MessageDedup = class {
19099
19099
  // src/transport/message-pipe.ts
19100
19100
  var import_node_crypto3 = require("crypto");
19101
19101
  var log12 = createLogger("transport/message-pipe");
19102
- var MessagePipe = class {
19102
+ var MessagePipe = class _MessagePipe {
19103
19103
  wsClient;
19104
19104
  dedup;
19105
19105
  crypto;
@@ -19141,6 +19141,31 @@ var MessagePipe = class {
19141
19141
  async injectRawFrame(rawData) {
19142
19142
  return this.handleInbound(rawData);
19143
19143
  }
19144
+ /** 文件类消息类型集合 — 这些类型的加密消息保留原始 content(文件元数据) */
19145
+ static FILE_MSG_TYPES = /* @__PURE__ */ new Set(["image", "file", "voice", "video"]);
19146
+ /**
19147
+ * 构建文件类加密消息的 extra — 完整模板格式。
19148
+ * 文件消息不加密 content(需要保留 fileId 等元数据),只在 extra 中携带会话信息。
19149
+ */
19150
+ static buildFileEncryptExtra(iv, keyId) {
19151
+ return JSON.stringify({
19152
+ containSensitive: false,
19153
+ cryptoIv: iv,
19154
+ decryptText: "",
19155
+ decrypted: false,
19156
+ destroyType: 0,
19157
+ encryptMsg: "",
19158
+ expireAt: null,
19159
+ level: 0,
19160
+ newDestroy: 1,
19161
+ phone: "",
19162
+ receiveShowTip: false,
19163
+ receiveTip: "",
19164
+ sessionId: keyId,
19165
+ showTip: false,
19166
+ tip: ""
19167
+ });
19168
+ }
19144
19169
  /**
19145
19170
  * 出站发送 — 通过 HTTP API 发送消息到 IM 服务器。
19146
19171
  * POST {messageServiceBaseUrl}/messages/v1/send
@@ -19149,30 +19174,48 @@ var MessagePipe = class {
19149
19174
  * 加密行为取决于 encryptionMode:
19150
19175
  * - quantum_only: 强制加密,失败直接抛错(不降级明文)
19151
19176
  * - quantum_and_plain: 加密失败降级明文(保持兼容)
19177
+ *
19178
+ * 文件类消息 (image/file/voice/video):
19179
+ * - content 保留原始文件元数据(fileId、fileName、size 等)
19180
+ * - extra 使用完整模板格式,encryptMsg 为空
19181
+ *
19182
+ * 文本类消息 (text/markdown):
19183
+ * - content 加密后置空,密文放入 extra.encryptMsg
19152
19184
  */
19153
19185
  async sendMessage(msg) {
19154
19186
  let content = msg.content;
19155
19187
  let extra = "";
19188
+ const isFileMessage = _MessagePipe.FILE_MSG_TYPES.has(msg.msgType);
19156
19189
  if (!msg.skipEncrypt && this.quantumAccount) {
19157
19190
  if (this.encryptionMode === "quantum_only") {
19158
19191
  const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
19159
- extra = JSON.stringify({
19160
- cryptoIv: iv,
19161
- encryptMsg: ciphertext,
19162
- sessionId: keyId
19163
- });
19164
- content = JSON.stringify({ content: "" });
19165
- log12.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_only)", { sessionId: keyId });
19166
- } else {
19167
- try {
19168
- const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
19192
+ if (isFileMessage) {
19193
+ extra = _MessagePipe.buildFileEncryptExtra(iv, keyId);
19194
+ log12.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_only)", { sessionId: keyId, msgType: msg.msgType });
19195
+ } else {
19169
19196
  extra = JSON.stringify({
19170
19197
  cryptoIv: iv,
19171
19198
  encryptMsg: ciphertext,
19172
19199
  sessionId: keyId
19173
19200
  });
19174
19201
  content = JSON.stringify({ content: "" });
19175
- log12.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId });
19202
+ log12.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_only)", { sessionId: keyId });
19203
+ }
19204
+ } else {
19205
+ try {
19206
+ const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
19207
+ if (isFileMessage) {
19208
+ extra = _MessagePipe.buildFileEncryptExtra(iv, keyId);
19209
+ log12.debug("\u{1F512} \u51FA\u7AD9\u6587\u4EF6\u6D88\u606F\u5DF2\u6807\u8BB0\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId, msgType: msg.msgType });
19210
+ } else {
19211
+ extra = JSON.stringify({
19212
+ cryptoIv: iv,
19213
+ encryptMsg: ciphertext,
19214
+ sessionId: keyId
19215
+ });
19216
+ content = JSON.stringify({ content: "" });
19217
+ log12.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId });
19218
+ }
19176
19219
  } catch (err) {
19177
19220
  log12.error("\u274C \u51FA\u7AD9\u6D88\u606F\u52A0\u5BC6\u5931\u8D25\uFF0C\u5C06\u53D1\u9001\u660E\u6587", {
19178
19221
  error: err.message
@@ -19194,13 +19237,11 @@ var MessagePipe = class {
19194
19237
  }
19195
19238
  const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
19196
19239
  if (!result) return;
19197
- log12.info("outbound:sent \u2192 IM \u670D\u52A1\u5668", {
19240
+ log12.info("\u{1F4E4} outbound:sent \u2192 IM \u670D\u52A1\u5668", {
19198
19241
  chatId: msg.chatId,
19199
- receiveId: msg.senderId,
19200
- msgType: msg.msgType,
19201
19242
  encrypted: Boolean(extra),
19202
- contentPreview: extra ? "[encrypted]" : msg.content.slice(0, 200),
19203
- requestId: result.request_id
19243
+ requestId: result.request_id,
19244
+ body
19204
19245
  });
19205
19246
  }
19206
19247
  /**
@@ -19309,8 +19350,27 @@ var MessagePipe = class {
19309
19350
  log12.warn("\u26A0\uFE0F CallbackData \u89E3\u6790\u5931\u8D25", { error: err.message });
19310
19351
  return;
19311
19352
  }
19353
+ log12.info("\u{1F4E9} \u5165\u7AD9\u539F\u59CB\u6570\u636E", {
19354
+ appId: callbackData.appId,
19355
+ msgUid: callbackData.msgUid,
19356
+ eventType: callbackData.eventType,
19357
+ userId: callbackData.userId,
19358
+ toUserId: callbackData.toUserId,
19359
+ groupId: callbackData.groupId,
19360
+ type: callbackData.type,
19361
+ content: callbackData.content,
19362
+ extra: callbackData.extra
19363
+ });
19312
19364
  const extra = callbackData.extra;
19313
- const isEncrypted = typeof extra === "string" && extra.length > 0;
19365
+ let isEncrypted = false;
19366
+ if (typeof extra === "string" && extra.length > 0) {
19367
+ try {
19368
+ const parsed = JSON.parse(extra);
19369
+ isEncrypted = Boolean(parsed.encryptMsg);
19370
+ } catch {
19371
+ log12.debug("\u2139\uFE0F extra \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u89C6\u4E3A\u660E\u6587", { msgUid: callbackData.msgUid });
19372
+ }
19373
+ }
19314
19374
  let contentObj;
19315
19375
  if (this.encryptionMode === "quantum_only") {
19316
19376
  if (!this.quantumAccount) {
@@ -19501,7 +19561,7 @@ var ConnectionManager = class {
19501
19561
  this.client.on("pong", () => {
19502
19562
  this.pongReceived = true;
19503
19563
  this.clearPongTimeout();
19504
- log13.info("heartbeat: pong received");
19564
+ log13.debug("heartbeat: pong received");
19505
19565
  });
19506
19566
  }
19507
19567
  /** 启动心跳保活 — 定时发送 WebSocket Ping 帧 */
@@ -19522,7 +19582,7 @@ var ConnectionManager = class {
19522
19582
  }
19523
19583
  this.pongReceived = false;
19524
19584
  this.client.ping();
19525
- log13.info("heartbeat: ping sent");
19585
+ log13.debug("heartbeat: ping sent");
19526
19586
  this.clearPongTimeout();
19527
19587
  this.pongTimeoutTimer = setTimeout(() => {
19528
19588
  if (!this.pongReceived && this.running) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liangzimixin",
3
- "version": "0.3.44",
3
+ "version": "0.3.46",
4
4
  "description": "Quantum-encrypted IM channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",