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 +80 -20
- package/dist/index.d.cts +14 -0
- package/dist/setup-entry.cjs +80 -20
- package/package.json +1 -1
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
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
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 (
|
|
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
|
-
|
|
20159
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
/**
|
package/dist/setup-entry.cjs
CHANGED
|
@@ -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
|
-
|
|
19160
|
-
|
|
19161
|
-
|
|
19162
|
-
|
|
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 (
|
|
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
|
-
|
|
19203
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
19585
|
+
log13.debug("heartbeat: ping sent");
|
|
19526
19586
|
this.clearPongTimeout();
|
|
19527
19587
|
this.pongTimeoutTimer = setTimeout(() => {
|
|
19528
19588
|
if (!this.pongReceived && this.running) {
|