liangzimixin 0.3.43 → 0.3.44
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 +150 -72
- package/dist/index.d.cts +30 -7
- package/dist/setup-entry.cjs +150 -72
- package/openclaw.plugin.json +7 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -17441,6 +17441,9 @@ function parseChannelAddress(address) {
|
|
|
17441
17441
|
const prefix = `${CHANNEL_ID}:`;
|
|
17442
17442
|
return address.startsWith(prefix) ? address.slice(prefix.length) : address;
|
|
17443
17443
|
}
|
|
17444
|
+
var GUIDE_URL = "https://www.kdocs.cn/l/ca5LX4wxpSSE";
|
|
17445
|
+
var HINT_NO_QUANTUM_ACCOUNT = "\u60A8\u672A\u914D\u7F6E\u91CF\u5B50\u52A0\u5BC6\u53C2\u6570\uFF0C\u8BF7\u53C2\u8003[\u300A\u63A5\u5165\u6307\u5357\u300B\u5B89\u88C5\u91CF\u5B50\u5BC6\u4FE1\u63D2\u4EF6](" + GUIDE_URL + ") \u4FEE\u6539\u914D\u7F6E";
|
|
17446
|
+
var HINT_PLAINTEXT_NOT_SUPPORTED = "\u5F53\u524D\u6A21\u5F0F\u4E0D\u652F\u6301\u666E\u901A\u6D88\u606F\uFF0C\u8BF7\u53C2\u8003[\u300A\u63A5\u5165\u6307\u5357\u300B\u5B89\u88C5\u91CF\u5B50\u5BC6\u4FE1\u63D2\u4EF6](" + GUIDE_URL + ") \u4FEE\u6539\u914D\u7F6E";
|
|
17444
17447
|
|
|
17445
17448
|
// src/config.ts
|
|
17446
17449
|
var AccountConfigSchema = external_exports.object({
|
|
@@ -17462,7 +17465,9 @@ var AccountConfigSchema = external_exports.object({
|
|
|
17462
17465
|
*/
|
|
17463
17466
|
botUserId: external_exports.string().optional(),
|
|
17464
17467
|
/** 🟡 部署环境 — staging=联调, production=线上 */
|
|
17465
|
-
env: external_exports.enum(["staging", "production"]).optional()
|
|
17468
|
+
env: external_exports.enum(["staging", "production"]).optional(),
|
|
17469
|
+
/** 🟡 消息加密模式 — quantum_only=仅加密, quantum_and_plain=加密+普通(默认) */
|
|
17470
|
+
encryptionMode: external_exports.enum(["quantum_only", "quantum_and_plain"]).default("quantum_and_plain")
|
|
17466
17471
|
}).transform((raw) => ({
|
|
17467
17472
|
appId: raw.appId,
|
|
17468
17473
|
appSecret: raw.appSecret,
|
|
@@ -17471,7 +17476,8 @@ var AccountConfigSchema = external_exports.object({
|
|
|
17471
17476
|
// quantumAppId: raw.quantumAppId,
|
|
17472
17477
|
// quantumAppSecret: raw.quantumAppSecret,
|
|
17473
17478
|
botUserId: raw.botUserId,
|
|
17474
|
-
env: raw.env
|
|
17479
|
+
env: raw.env,
|
|
17480
|
+
encryptionMode: raw.encryptionMode
|
|
17475
17481
|
}));
|
|
17476
17482
|
var ENV_PRESETS = {
|
|
17477
17483
|
/** 联调环境 */
|
|
@@ -18180,13 +18186,15 @@ var quantumImOutbound = {
|
|
|
18180
18186
|
}
|
|
18181
18187
|
const messagePipe = messagePipeGetter();
|
|
18182
18188
|
const chatId = parseChannelAddress(to);
|
|
18189
|
+
const encryptionMode = pluginConfigGetter?.()?.credentials?.encryptionMode;
|
|
18183
18190
|
const isEncrypted = getOutboundEncryptionStatus(chatId);
|
|
18191
|
+
const skipEncrypt = encryptionMode === "quantum_only" ? false : isEncrypted !== true;
|
|
18184
18192
|
await messagePipe.sendMessage({
|
|
18185
18193
|
chatId,
|
|
18186
18194
|
senderId: chatId,
|
|
18187
18195
|
msgType: "markdown",
|
|
18188
18196
|
content: JSON.stringify({ content: text }),
|
|
18189
|
-
skipEncrypt
|
|
18197
|
+
skipEncrypt
|
|
18190
18198
|
});
|
|
18191
18199
|
return { channel: CHANNEL_ID, messageId: "", chatId };
|
|
18192
18200
|
},
|
|
@@ -18201,7 +18209,9 @@ var quantumImOutbound = {
|
|
|
18201
18209
|
const sdkRuntime = sdkRuntimeGetter();
|
|
18202
18210
|
const chatId = parseChannelAddress(to);
|
|
18203
18211
|
const fileCfg = pluginConfigGetter().file;
|
|
18212
|
+
const encryptionMode = pluginConfigGetter?.()?.credentials?.encryptionMode;
|
|
18204
18213
|
const isEncrypted = getOutboundEncryptionStatus(chatId);
|
|
18214
|
+
const skipEncrypt = encryptionMode === "quantum_only" ? false : isEncrypted !== true;
|
|
18205
18215
|
const result = await resolveAndUploadMedia({
|
|
18206
18216
|
mediaUrl: mediaUrl ?? void 0,
|
|
18207
18217
|
tokenManager: tokenManagerGetter(),
|
|
@@ -18214,7 +18224,7 @@ var quantumImOutbound = {
|
|
|
18214
18224
|
maxFileSizeMb: fileCfg.maxFileSizeMb,
|
|
18215
18225
|
chunkSizeMb: fileCfg.chunkSizeMb,
|
|
18216
18226
|
timeoutMs: fileCfg.fetchTimeoutMs,
|
|
18217
|
-
skipEncrypt
|
|
18227
|
+
skipEncrypt
|
|
18218
18228
|
});
|
|
18219
18229
|
if (result.warning) {
|
|
18220
18230
|
log4.warn("sendMedia:degraded", { chatId, warning: result.warning });
|
|
@@ -18515,8 +18525,10 @@ function createQuantumImDeliverFn(deps) {
|
|
|
18515
18525
|
maxFileSizeMb,
|
|
18516
18526
|
chunkSizeMb,
|
|
18517
18527
|
timeoutMs,
|
|
18518
|
-
isEncrypted
|
|
18528
|
+
isEncrypted,
|
|
18529
|
+
encryptionMode
|
|
18519
18530
|
} = deps;
|
|
18531
|
+
const shouldSkipEncrypt = encryptionMode === "quantum_only" ? false : isEncrypted === false;
|
|
18520
18532
|
const deliver = async (payload) => {
|
|
18521
18533
|
const mediaUrls = [];
|
|
18522
18534
|
if (payload.mediaUrls?.length) {
|
|
@@ -18534,7 +18546,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
18534
18546
|
msgType: "markdown",
|
|
18535
18547
|
content: JSON.stringify({ content: payload.text }),
|
|
18536
18548
|
replyToMessageId,
|
|
18537
|
-
skipEncrypt:
|
|
18549
|
+
skipEncrypt: shouldSkipEncrypt
|
|
18538
18550
|
});
|
|
18539
18551
|
log11.info("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u5DF2\u53D1\u9001", {
|
|
18540
18552
|
chatId,
|
|
@@ -18557,7 +18569,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
18557
18569
|
maxFileSizeMb,
|
|
18558
18570
|
chunkSizeMb,
|
|
18559
18571
|
timeoutMs,
|
|
18560
|
-
skipEncrypt:
|
|
18572
|
+
skipEncrypt: shouldSkipEncrypt
|
|
18561
18573
|
});
|
|
18562
18574
|
if (result.warning) {
|
|
18563
18575
|
log11.warn("\u{1F4E4} \u5A92\u4F53\u53D1\u9001\u964D\u7EA7", { chatId, mediaUrl, warning: result.warning });
|
|
@@ -18576,7 +18588,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
18576
18588
|
msgType: "markdown",
|
|
18577
18589
|
content: JSON.stringify({ content: `\u{1F4CE} ${mediaUrl}` }),
|
|
18578
18590
|
replyToMessageId,
|
|
18579
|
-
skipEncrypt:
|
|
18591
|
+
skipEncrypt: shouldSkipEncrypt
|
|
18580
18592
|
});
|
|
18581
18593
|
}
|
|
18582
18594
|
}
|
|
@@ -18626,12 +18638,13 @@ var InboundPipeline = class {
|
|
|
18626
18638
|
});
|
|
18627
18639
|
return;
|
|
18628
18640
|
}
|
|
18641
|
+
const feedbackSkipEncrypt = this.deps.pluginConfig.credentials.encryptionMode === "quantum_only" ? false : true;
|
|
18629
18642
|
this.deps.messagePipe.sendMessage({
|
|
18630
18643
|
chatId: msg.chatId,
|
|
18631
18644
|
senderId: msg.senderId,
|
|
18632
18645
|
msgType: "text",
|
|
18633
18646
|
content: JSON.stringify({ content: "\u4EFB\u52A1\u5DF2\u6536\u5230\u{1F44C}\uFF0C\u6B63\u5728\u5904\u7406\u4E2D..." }),
|
|
18634
|
-
skipEncrypt:
|
|
18647
|
+
skipEncrypt: feedbackSkipEncrypt
|
|
18635
18648
|
}).catch((err) => {
|
|
18636
18649
|
log12.warn("\u26A0\uFE0F \u5373\u65F6\u53CD\u9988\u53D1\u9001\u5931\u8D25", { error: err.message });
|
|
18637
18650
|
});
|
|
@@ -18659,7 +18672,8 @@ var InboundPipeline = class {
|
|
|
18659
18672
|
allowPrivateNetwork: this.deps.pluginConfig.file.allowPrivateNetwork,
|
|
18660
18673
|
maxFileSizeMb: this.deps.pluginConfig.file.maxFileSizeMb,
|
|
18661
18674
|
timeoutMs: this.deps.pluginConfig.file.fetchTimeoutMs,
|
|
18662
|
-
isEncrypted: msg.isEncrypted
|
|
18675
|
+
isEncrypted: msg.isEncrypted,
|
|
18676
|
+
encryptionMode: this.deps.pluginConfig.credentials.encryptionMode
|
|
18663
18677
|
});
|
|
18664
18678
|
const { dispatcher, replyOptions } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
18665
18679
|
deliver
|
|
@@ -18759,6 +18773,13 @@ var QUANTUM_IM_CONFIG_JSON_SCHEMA = {
|
|
|
18759
18773
|
description: "\u9009\u62E9\u5BF9\u63A5\u7684\u670D\u52A1\u5668\u73AF\u5883\u3002staging = \u8054\u8C03\u73AF\u5883\uFF0Cproduction = \u7EBF\u4E0A\u73AF\u5883",
|
|
18760
18774
|
enum: ["staging", "production"],
|
|
18761
18775
|
default: "production"
|
|
18776
|
+
},
|
|
18777
|
+
encryptionMode: {
|
|
18778
|
+
type: "string",
|
|
18779
|
+
title: "\u6D88\u606F\u52A0\u5BC6\u6A21\u5F0F",
|
|
18780
|
+
description: "quantum_only = \u4EC5\u91CF\u5B50\u52A0\u5BC6\u6D88\u606F\uFF08\u6240\u6709\u6D88\u606F\u5FC5\u987B\u52A0\u5BC6\uFF09\uFF1Bquantum_and_plain = \u540C\u65F6\u652F\u6301\u52A0\u5BC6\u548C\u666E\u901A\u6D88\u606F",
|
|
18781
|
+
enum: ["quantum_only", "quantum_and_plain"],
|
|
18782
|
+
default: "quantum_and_plain"
|
|
18762
18783
|
}
|
|
18763
18784
|
}
|
|
18764
18785
|
};
|
|
@@ -18853,33 +18874,39 @@ var quantumImOnboarding = {
|
|
|
18853
18874
|
].join("\n"),
|
|
18854
18875
|
"\u91CF\u5B50\u5BC6\u4FE1 \u2014 \u51ED\u636E\u914D\u7F6E"
|
|
18855
18876
|
);
|
|
18856
|
-
const
|
|
18857
|
-
message: "\
|
|
18858
|
-
initialValue: existing.appId ?? void 0,
|
|
18859
|
-
validate: required2
|
|
18860
|
-
});
|
|
18861
|
-
const appSecret = await prompter.text({
|
|
18862
|
-
message: "\u5E94\u7528\u5BC6\u94A5 (appSecret)",
|
|
18863
|
-
initialValue: existing.appSecret ?? void 0,
|
|
18864
|
-
validate: required2
|
|
18865
|
-
});
|
|
18866
|
-
const encryptionMode = await prompter.select({
|
|
18867
|
-
message: "\u6D88\u606F\u4F20\u8F93\u6A21\u5F0F",
|
|
18877
|
+
const encryptionModeChoice = await prompter.select({
|
|
18878
|
+
message: "\u8BF7\u9009\u62E9\u6D88\u606F\u4EA4\u4E92\u65B9\u5F0F",
|
|
18868
18879
|
options: [
|
|
18869
|
-
{ value: "
|
|
18870
|
-
{ value: "
|
|
18880
|
+
{ value: "quantum_only", label: "\u4EC5\u91CF\u5B50\u52A0\u5BC6\u6D88\u606F" },
|
|
18881
|
+
{ value: "quantum_and_plain", label: "\u91CF\u5B50\u52A0\u5BC6+\u666E\u901A\u6D88\u606F\uFF08\u82E5\u91CF\u5B50\u52A0\u5BC6\u7981\u7528\uFF0C\u5219\u7559\u7A7A\uFF09" }
|
|
18871
18882
|
],
|
|
18872
|
-
initialValue: existing.
|
|
18883
|
+
initialValue: existing.encryptionMode ?? "quantum_and_plain"
|
|
18873
18884
|
});
|
|
18874
18885
|
let quantumAccount = "";
|
|
18875
|
-
if (
|
|
18886
|
+
if (encryptionModeChoice === "quantum_only") {
|
|
18876
18887
|
quantumAccount = await prompter.text({
|
|
18877
18888
|
message: "\u91CF\u5B50\u8D26\u6237\u6807\u8BC6 (quantumAccount)",
|
|
18878
18889
|
placeholder: "\u5982: 17100001111",
|
|
18879
18890
|
initialValue: existing.quantumAccount ?? void 0,
|
|
18880
18891
|
validate: required2
|
|
18881
18892
|
});
|
|
18893
|
+
} else {
|
|
18894
|
+
quantumAccount = await prompter.text({
|
|
18895
|
+
message: "\u91CF\u5B50\u8D26\u6237\u6807\u8BC6\uFF08\u53EF\u9009\uFF0C\u4E0D\u586B\u5219\u4EC5\u652F\u6301\u660E\u6587\uFF09",
|
|
18896
|
+
placeholder: "\u5982: 17100001111",
|
|
18897
|
+
initialValue: existing.quantumAccount ?? void 0
|
|
18898
|
+
});
|
|
18882
18899
|
}
|
|
18900
|
+
const appId = await prompter.text({
|
|
18901
|
+
message: "\u5E94\u7528 ID (appId)",
|
|
18902
|
+
initialValue: existing.appId ?? void 0,
|
|
18903
|
+
validate: required2
|
|
18904
|
+
});
|
|
18905
|
+
const appSecret = await prompter.text({
|
|
18906
|
+
message: "\u5E94\u7528\u5BC6\u94A5 (appSecret)",
|
|
18907
|
+
initialValue: existing.appSecret ?? void 0,
|
|
18908
|
+
validate: required2
|
|
18909
|
+
});
|
|
18883
18910
|
const channels = cfg.channels ?? {};
|
|
18884
18911
|
const channelCfg = channels[CHANNEL_ID] ?? {};
|
|
18885
18912
|
const accounts = channelCfg.accounts ?? {};
|
|
@@ -18895,10 +18922,8 @@ var quantumImOnboarding = {
|
|
|
18895
18922
|
default: {
|
|
18896
18923
|
appId: appId.trim(),
|
|
18897
18924
|
appSecret: appSecret.trim(),
|
|
18898
|
-
|
|
18925
|
+
encryptionMode: encryptionModeChoice,
|
|
18899
18926
|
...quantumAccount.trim() ? { quantumAccount: quantumAccount.trim() } : {}
|
|
18900
|
-
// quantumAppId: quantumAppId.trim(),
|
|
18901
|
-
// quantumAppSecret: quantumAppSecret.trim(),
|
|
18902
18927
|
}
|
|
18903
18928
|
}
|
|
18904
18929
|
}
|
|
@@ -20042,6 +20067,8 @@ var MessagePipe = class {
|
|
|
20042
20067
|
messageCallback = null;
|
|
20043
20068
|
/** 量子账户标识 — 用于判断是否具备解密能力 */
|
|
20044
20069
|
quantumAccount;
|
|
20070
|
+
/** 消息加密模式 */
|
|
20071
|
+
encryptionMode;
|
|
20045
20072
|
constructor(deps) {
|
|
20046
20073
|
this.wsClient = deps.wsClient;
|
|
20047
20074
|
this.dedup = deps.dedup;
|
|
@@ -20049,6 +20076,7 @@ var MessagePipe = class {
|
|
|
20049
20076
|
this.tokenFn = deps.tokenFn;
|
|
20050
20077
|
this.messageServiceBaseUrl = deps.messageServiceBaseUrl;
|
|
20051
20078
|
this.quantumAccount = deps.quantumAccount;
|
|
20079
|
+
this.encryptionMode = deps.encryptionMode;
|
|
20052
20080
|
this.wsClient.on("message", (rawData) => {
|
|
20053
20081
|
this.handleInbound(rawData).catch((err) => {
|
|
20054
20082
|
log21.error("\u274C \u5165\u7AD9\u5904\u7406\u5F02\u5E38", { step: "handleInbound", error: err.message });
|
|
@@ -20074,32 +20102,38 @@ var MessagePipe = class {
|
|
|
20074
20102
|
* POST {messageServiceBaseUrl}/messages/v1/send
|
|
20075
20103
|
* 字段映射: senderId → receive_id, msgType → msg_type
|
|
20076
20104
|
*
|
|
20077
|
-
*
|
|
20078
|
-
*
|
|
20079
|
-
*
|
|
20080
|
-
* - 原始 content 置为空字符串 {content: ''}
|
|
20081
|
-
* - extra 序列化为 JSON 字符串
|
|
20082
|
-
*
|
|
20083
|
-
* 失败不抛异常,避免阻塞 SDK deliver 回调。
|
|
20105
|
+
* 加密行为取决于 encryptionMode:
|
|
20106
|
+
* - quantum_only: 强制加密,失败直接抛错(不降级明文)
|
|
20107
|
+
* - quantum_and_plain: 加密失败降级明文(保持兼容)
|
|
20084
20108
|
*/
|
|
20085
20109
|
async sendMessage(msg) {
|
|
20086
20110
|
let content = msg.content;
|
|
20087
20111
|
let extra = "";
|
|
20088
|
-
if (
|
|
20089
|
-
|
|
20112
|
+
if (!msg.skipEncrypt && this.quantumAccount) {
|
|
20113
|
+
if (this.encryptionMode === "quantum_only") {
|
|
20090
20114
|
const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
|
|
20091
20115
|
extra = JSON.stringify({
|
|
20092
20116
|
cryptoIv: iv,
|
|
20093
|
-
// 随机生成的 IV,解密时需要回传
|
|
20094
20117
|
encryptMsg: ciphertext,
|
|
20095
20118
|
sessionId: keyId
|
|
20096
20119
|
});
|
|
20097
20120
|
content = JSON.stringify({ content: "" });
|
|
20098
|
-
log21.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6", { sessionId: keyId });
|
|
20099
|
-
}
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
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);
|
|
20125
|
+
extra = JSON.stringify({
|
|
20126
|
+
cryptoIv: iv,
|
|
20127
|
+
encryptMsg: ciphertext,
|
|
20128
|
+
sessionId: keyId
|
|
20129
|
+
});
|
|
20130
|
+
content = JSON.stringify({ content: "" });
|
|
20131
|
+
log21.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId });
|
|
20132
|
+
} catch (err) {
|
|
20133
|
+
log21.error("\u274C \u51FA\u7AD9\u6D88\u606F\u52A0\u5BC6\u5931\u8D25\uFF0C\u5C06\u53D1\u9001\u660E\u6587", {
|
|
20134
|
+
error: err.message
|
|
20135
|
+
});
|
|
20136
|
+
}
|
|
20103
20137
|
}
|
|
20104
20138
|
}
|
|
20105
20139
|
const body = {
|
|
@@ -20234,44 +20268,45 @@ var MessagePipe = class {
|
|
|
20234
20268
|
const extra = callbackData.extra;
|
|
20235
20269
|
const isEncrypted = typeof extra === "string" && extra.length > 0;
|
|
20236
20270
|
let contentObj;
|
|
20237
|
-
if (
|
|
20271
|
+
if (this.encryptionMode === "quantum_only") {
|
|
20238
20272
|
if (!this.quantumAccount) {
|
|
20239
|
-
log21.
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20273
|
+
log21.info("\u26A0\uFE0F quantum_only: \u672A\u914D\u7F6E quantumAccount\uFF0C\u53D1\u9001\u5F15\u5BFC\u63D0\u793A", { chatId: callbackData.userId });
|
|
20274
|
+
await this.sendHintMessage(callbackData, HINT_NO_QUANTUM_ACCOUNT);
|
|
20275
|
+
return;
|
|
20276
|
+
}
|
|
20277
|
+
if (!isEncrypted) {
|
|
20278
|
+
log21.info("\u26A0\uFE0F quantum_only: \u6536\u5230\u660E\u6587\u6D88\u606F\uFF0C\u62D2\u7EDD\u5904\u7406", { msgUid: callbackData.msgUid });
|
|
20279
|
+
await this.sendHintMessage(callbackData, HINT_PLAINTEXT_NOT_SUPPORTED);
|
|
20243
20280
|
return;
|
|
20244
20281
|
}
|
|
20245
20282
|
try {
|
|
20246
|
-
|
|
20247
|
-
const encryptMsg = extraData.encryptMsg ?? "";
|
|
20248
|
-
const sessionId = extraData.sessionId ?? "";
|
|
20249
|
-
if (!encryptMsg) {
|
|
20250
|
-
log21.warn("\u26A0\uFE0F extra \u4E2D encryptMsg \u4E3A\u7A7A\uFF0C\u8DF3\u8FC7\u89E3\u5BC6", { msgUid: callbackData.msgUid });
|
|
20251
|
-
contentObj = callbackData.content;
|
|
20252
|
-
} else {
|
|
20253
|
-
const cryptoIv = extraData.cryptoIv ?? "";
|
|
20254
|
-
const decrypted = await this.crypto.decrypt(encryptMsg, sessionId, cryptoIv);
|
|
20255
|
-
try {
|
|
20256
|
-
contentObj = JSON.parse(decrypted);
|
|
20257
|
-
} catch {
|
|
20258
|
-
contentObj = { content: decrypted };
|
|
20259
|
-
}
|
|
20260
|
-
log21.debug("\u{1F513} \u6D88\u606F\u89E3\u5BC6\u6210\u529F", {
|
|
20261
|
-
msgUid: callbackData.msgUid,
|
|
20262
|
-
sessionId,
|
|
20263
|
-
hasIv: Boolean(extraData.cryptoIv)
|
|
20264
|
-
});
|
|
20265
|
-
}
|
|
20283
|
+
contentObj = await this.decryptExtra(callbackData);
|
|
20266
20284
|
} catch (err) {
|
|
20267
|
-
log21.error("\u274C \u6D88\u606F\u89E3\u5BC6\u5931\u8D25", {
|
|
20285
|
+
log21.error("\u274C quantum_only: \u6D88\u606F\u89E3\u5BC6\u5931\u8D25", {
|
|
20268
20286
|
msgUid: callbackData.msgUid,
|
|
20269
20287
|
error: err.message
|
|
20270
20288
|
});
|
|
20271
20289
|
return;
|
|
20272
20290
|
}
|
|
20273
20291
|
} else {
|
|
20274
|
-
|
|
20292
|
+
if (isEncrypted) {
|
|
20293
|
+
if (!this.quantumAccount) {
|
|
20294
|
+
log21.info("\u26A0\uFE0F quantum_and_plain: \u6536\u5230\u52A0\u5BC6\u6D88\u606F\u4F46\u672A\u914D\u7F6E quantumAccount", { msgUid: callbackData.msgUid });
|
|
20295
|
+
await this.sendHintMessage(callbackData, HINT_NO_QUANTUM_ACCOUNT);
|
|
20296
|
+
return;
|
|
20297
|
+
}
|
|
20298
|
+
try {
|
|
20299
|
+
contentObj = await this.decryptExtra(callbackData);
|
|
20300
|
+
} catch (err) {
|
|
20301
|
+
log21.error("\u274C quantum_and_plain: \u6D88\u606F\u89E3\u5BC6\u5931\u8D25", {
|
|
20302
|
+
msgUid: callbackData.msgUid,
|
|
20303
|
+
error: err.message
|
|
20304
|
+
});
|
|
20305
|
+
return;
|
|
20306
|
+
}
|
|
20307
|
+
} else {
|
|
20308
|
+
contentObj = callbackData.content;
|
|
20309
|
+
}
|
|
20275
20310
|
}
|
|
20276
20311
|
if (callbackData.eventType !== "callback:direct") {
|
|
20277
20312
|
log21.debug("\u2139\uFE0F \u5FFD\u7565\u975E\u76EE\u6807\u4E8B\u4EF6", { eventType: callbackData.eventType });
|
|
@@ -20298,6 +20333,48 @@ var MessagePipe = class {
|
|
|
20298
20333
|
log21.warn("\u26A0\uFE0F \u6D88\u606F\u56DE\u8C03\u672A\u6CE8\u518C\uFF0C\u65E0\u6CD5\u5904\u7406", { messageId: msg.messageId });
|
|
20299
20334
|
}
|
|
20300
20335
|
}
|
|
20336
|
+
// ── 私有辅助方法 ──
|
|
20337
|
+
/**
|
|
20338
|
+
* 发送系统提示消息(明文,不加密)。
|
|
20339
|
+
* 用于在模式不匹配时向用户发送引导信息。
|
|
20340
|
+
*/
|
|
20341
|
+
async sendHintMessage(callbackData, hintText) {
|
|
20342
|
+
const chatId = callbackData.groupId || callbackData.userId;
|
|
20343
|
+
const senderId = callbackData.userId;
|
|
20344
|
+
try {
|
|
20345
|
+
await this.sendMessage({
|
|
20346
|
+
chatId,
|
|
20347
|
+
senderId,
|
|
20348
|
+
msgType: "markdown",
|
|
20349
|
+
content: JSON.stringify({ content: hintText }),
|
|
20350
|
+
skipEncrypt: true
|
|
20351
|
+
});
|
|
20352
|
+
log21.info("\u{1F4A1} \u5DF2\u53D1\u9001\u6A21\u5F0F\u63D0\u793A\u6D88\u606F", { chatId });
|
|
20353
|
+
} catch (err) {
|
|
20354
|
+
log21.error("\u274C \u63D0\u793A\u6D88\u606F\u53D1\u9001\u5931\u8D25", { error: err.message });
|
|
20355
|
+
}
|
|
20356
|
+
}
|
|
20357
|
+
/**
|
|
20358
|
+
* 解密 extra 中的加密内容。
|
|
20359
|
+
* @throws 解密失败时抛出异常
|
|
20360
|
+
*/
|
|
20361
|
+
async decryptExtra(callbackData) {
|
|
20362
|
+
const extraData = JSON.parse(callbackData.extra);
|
|
20363
|
+
const encryptMsg = extraData.encryptMsg ?? "";
|
|
20364
|
+
const sessionId = extraData.sessionId ?? "";
|
|
20365
|
+
if (!encryptMsg) {
|
|
20366
|
+
log21.warn("\u26A0\uFE0F extra \u4E2D encryptMsg \u4E3A\u7A7A\uFF0C\u4F7F\u7528\u539F\u59CB content", { msgUid: callbackData.msgUid });
|
|
20367
|
+
return callbackData.content;
|
|
20368
|
+
}
|
|
20369
|
+
const cryptoIv = extraData.cryptoIv ?? "";
|
|
20370
|
+
const decrypted = await this.crypto.decrypt(encryptMsg, sessionId, cryptoIv);
|
|
20371
|
+
log21.debug("\u{1F513} \u6D88\u606F\u89E3\u5BC6\u6210\u529F", { msgUid: callbackData.msgUid, sessionId, hasIv: Boolean(cryptoIv) });
|
|
20372
|
+
try {
|
|
20373
|
+
return JSON.parse(decrypted);
|
|
20374
|
+
} catch {
|
|
20375
|
+
return { content: decrypted };
|
|
20376
|
+
}
|
|
20377
|
+
}
|
|
20301
20378
|
};
|
|
20302
20379
|
|
|
20303
20380
|
// src/transport/connection-manager.ts
|
|
@@ -20933,7 +21010,8 @@ async function startPlugin(accountConfig, internalOverrides) {
|
|
|
20933
21010
|
crypto: cryptoEngine,
|
|
20934
21011
|
tokenFn: () => tokenManager.getValidToken(),
|
|
20935
21012
|
messageServiceBaseUrl: config2.message.messageServiceBaseUrl,
|
|
20936
|
-
quantumAccount: accountConfig.quantumAccount
|
|
21013
|
+
quantumAccount: accountConfig.quantumAccount,
|
|
21014
|
+
encryptionMode: accountConfig.encryptionMode
|
|
20937
21015
|
});
|
|
20938
21016
|
const connectionManager = new ConnectionManager(wsClient, {
|
|
20939
21017
|
heartbeatIntervalMs: config2.transport.heartbeatIntervalMs,
|
package/dist/index.d.cts
CHANGED
|
@@ -23,15 +23,21 @@ declare const AccountConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
|
23
23
|
staging: "staging";
|
|
24
24
|
production: "production";
|
|
25
25
|
}>>;
|
|
26
|
+
encryptionMode: z.ZodDefault<z.ZodEnum<{
|
|
27
|
+
quantum_only: "quantum_only";
|
|
28
|
+
quantum_and_plain: "quantum_and_plain";
|
|
29
|
+
}>>;
|
|
26
30
|
}, z.core.$strip>, z.ZodTransform<{
|
|
27
31
|
appId: string;
|
|
28
32
|
appSecret: string;
|
|
29
33
|
quantumAccount: string | undefined;
|
|
30
34
|
botUserId: string | undefined;
|
|
31
35
|
env: "staging" | "production" | undefined;
|
|
36
|
+
encryptionMode: "quantum_only" | "quantum_and_plain";
|
|
32
37
|
}, {
|
|
33
38
|
appId: string;
|
|
34
39
|
appSecret: string;
|
|
40
|
+
encryptionMode: "quantum_only" | "quantum_and_plain";
|
|
35
41
|
quantumAccount?: string | undefined;
|
|
36
42
|
botUserId?: string | undefined;
|
|
37
43
|
env?: "staging" | "production" | undefined;
|
|
@@ -757,6 +763,8 @@ declare class MessagePipe {
|
|
|
757
763
|
private messageCallback;
|
|
758
764
|
/** 量子账户标识 — 用于判断是否具备解密能力 */
|
|
759
765
|
private readonly quantumAccount?;
|
|
766
|
+
/** 消息加密模式 */
|
|
767
|
+
private readonly encryptionMode;
|
|
760
768
|
constructor(deps: {
|
|
761
769
|
wsClient: WSClient;
|
|
762
770
|
dedup: MessageDedup;
|
|
@@ -767,6 +775,8 @@ declare class MessagePipe {
|
|
|
767
775
|
messageServiceBaseUrl: string;
|
|
768
776
|
/** 量子账户标识 — 用于判断是否具备解密能力 */
|
|
769
777
|
quantumAccount?: string;
|
|
778
|
+
/** 消息加密模式 */
|
|
779
|
+
encryptionMode: 'quantum_only' | 'quantum_and_plain';
|
|
770
780
|
});
|
|
771
781
|
/** 注册入站消息回调 — 解密后的消息会通过此回调传给 L4 层 */
|
|
772
782
|
onMessage(callback: (msg: InboundMessage) => void): void;
|
|
@@ -781,13 +791,9 @@ declare class MessagePipe {
|
|
|
781
791
|
* POST {messageServiceBaseUrl}/messages/v1/send
|
|
782
792
|
* 字段映射: senderId → receive_id, msgType → msg_type
|
|
783
793
|
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
*
|
|
787
|
-
* - 原始 content 置为空字符串 {content: ''}
|
|
788
|
-
* - extra 序列化为 JSON 字符串
|
|
789
|
-
*
|
|
790
|
-
* 失败不抛异常,避免阻塞 SDK deliver 回调。
|
|
794
|
+
* 加密行为取决于 encryptionMode:
|
|
795
|
+
* - quantum_only: 强制加密,失败直接抛错(不降级明文)
|
|
796
|
+
* - quantum_and_plain: 加密失败降级明文(保持兼容)
|
|
791
797
|
*/
|
|
792
798
|
sendMessage(msg: OutboundMessage): Promise<void>;
|
|
793
799
|
/**
|
|
@@ -822,6 +828,16 @@ declare class MessagePipe {
|
|
|
822
828
|
* 7. 回调 L4
|
|
823
829
|
*/
|
|
824
830
|
private handleInbound;
|
|
831
|
+
/**
|
|
832
|
+
* 发送系统提示消息(明文,不加密)。
|
|
833
|
+
* 用于在模式不匹配时向用户发送引导信息。
|
|
834
|
+
*/
|
|
835
|
+
private sendHintMessage;
|
|
836
|
+
/**
|
|
837
|
+
* 解密 extra 中的加密内容。
|
|
838
|
+
* @throws 解密失败时抛出异常
|
|
839
|
+
*/
|
|
840
|
+
private decryptExtra;
|
|
825
841
|
}
|
|
826
842
|
|
|
827
843
|
/**
|
|
@@ -1141,6 +1157,13 @@ declare const QUANTUM_IM_CONFIG_JSON_SCHEMA: {
|
|
|
1141
1157
|
readonly enum: readonly ["staging", "production"];
|
|
1142
1158
|
readonly default: "production";
|
|
1143
1159
|
};
|
|
1160
|
+
readonly encryptionMode: {
|
|
1161
|
+
readonly type: "string";
|
|
1162
|
+
readonly title: "消息加密模式";
|
|
1163
|
+
readonly description: "quantum_only = 仅量子加密消息(所有消息必须加密);quantum_and_plain = 同时支持加密和普通消息";
|
|
1164
|
+
readonly enum: readonly ["quantum_only", "quantum_and_plain"];
|
|
1165
|
+
readonly default: "quantum_and_plain";
|
|
1166
|
+
};
|
|
1144
1167
|
};
|
|
1145
1168
|
};
|
|
1146
1169
|
|
package/dist/setup-entry.cjs
CHANGED
|
@@ -3688,6 +3688,9 @@ function parseChannelAddress(address) {
|
|
|
3688
3688
|
const prefix = `${CHANNEL_ID}:`;
|
|
3689
3689
|
return address.startsWith(prefix) ? address.slice(prefix.length) : address;
|
|
3690
3690
|
}
|
|
3691
|
+
var GUIDE_URL = "https://www.kdocs.cn/l/ca5LX4wxpSSE";
|
|
3692
|
+
var HINT_NO_QUANTUM_ACCOUNT = "\u60A8\u672A\u914D\u7F6E\u91CF\u5B50\u52A0\u5BC6\u53C2\u6570\uFF0C\u8BF7\u53C2\u8003[\u300A\u63A5\u5165\u6307\u5357\u300B\u5B89\u88C5\u91CF\u5B50\u5BC6\u4FE1\u63D2\u4EF6](" + GUIDE_URL + ") \u4FEE\u6539\u914D\u7F6E";
|
|
3693
|
+
var HINT_PLAINTEXT_NOT_SUPPORTED = "\u5F53\u524D\u6A21\u5F0F\u4E0D\u652F\u6301\u666E\u901A\u6D88\u606F\uFF0C\u8BF7\u53C2\u8003[\u300A\u63A5\u5165\u6307\u5357\u300B\u5B89\u88C5\u91CF\u5B50\u5BC6\u4FE1\u63D2\u4EF6](" + GUIDE_URL + ") \u4FEE\u6539\u914D\u7F6E";
|
|
3691
3694
|
|
|
3692
3695
|
// src/logger.ts
|
|
3693
3696
|
var LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
@@ -4275,13 +4278,15 @@ var quantumImOutbound = {
|
|
|
4275
4278
|
}
|
|
4276
4279
|
const messagePipe = messagePipeGetter();
|
|
4277
4280
|
const chatId = parseChannelAddress(to);
|
|
4281
|
+
const encryptionMode = pluginConfigGetter?.()?.credentials?.encryptionMode;
|
|
4278
4282
|
const isEncrypted = getOutboundEncryptionStatus(chatId);
|
|
4283
|
+
const skipEncrypt = encryptionMode === "quantum_only" ? false : isEncrypted !== true;
|
|
4279
4284
|
await messagePipe.sendMessage({
|
|
4280
4285
|
chatId,
|
|
4281
4286
|
senderId: chatId,
|
|
4282
4287
|
msgType: "markdown",
|
|
4283
4288
|
content: JSON.stringify({ content: text }),
|
|
4284
|
-
skipEncrypt
|
|
4289
|
+
skipEncrypt
|
|
4285
4290
|
});
|
|
4286
4291
|
return { channel: CHANNEL_ID, messageId: "", chatId };
|
|
4287
4292
|
},
|
|
@@ -4296,7 +4301,9 @@ var quantumImOutbound = {
|
|
|
4296
4301
|
const sdkRuntime = sdkRuntimeGetter();
|
|
4297
4302
|
const chatId = parseChannelAddress(to);
|
|
4298
4303
|
const fileCfg = pluginConfigGetter().file;
|
|
4304
|
+
const encryptionMode = pluginConfigGetter?.()?.credentials?.encryptionMode;
|
|
4299
4305
|
const isEncrypted = getOutboundEncryptionStatus(chatId);
|
|
4306
|
+
const skipEncrypt = encryptionMode === "quantum_only" ? false : isEncrypted !== true;
|
|
4300
4307
|
const result = await resolveAndUploadMedia({
|
|
4301
4308
|
mediaUrl: mediaUrl ?? void 0,
|
|
4302
4309
|
tokenManager: tokenManagerGetter(),
|
|
@@ -4309,7 +4316,7 @@ var quantumImOutbound = {
|
|
|
4309
4316
|
maxFileSizeMb: fileCfg.maxFileSizeMb,
|
|
4310
4317
|
chunkSizeMb: fileCfg.chunkSizeMb,
|
|
4311
4318
|
timeoutMs: fileCfg.fetchTimeoutMs,
|
|
4312
|
-
skipEncrypt
|
|
4319
|
+
skipEncrypt
|
|
4313
4320
|
});
|
|
4314
4321
|
if (result.warning) {
|
|
4315
4322
|
log4.warn("sendMedia:degraded", { chatId, warning: result.warning });
|
|
@@ -18110,7 +18117,9 @@ var AccountConfigSchema = external_exports.object({
|
|
|
18110
18117
|
*/
|
|
18111
18118
|
botUserId: external_exports.string().optional(),
|
|
18112
18119
|
/** 🟡 部署环境 — staging=联调, production=线上 */
|
|
18113
|
-
env: external_exports.enum(["staging", "production"]).optional()
|
|
18120
|
+
env: external_exports.enum(["staging", "production"]).optional(),
|
|
18121
|
+
/** 🟡 消息加密模式 — quantum_only=仅加密, quantum_and_plain=加密+普通(默认) */
|
|
18122
|
+
encryptionMode: external_exports.enum(["quantum_only", "quantum_and_plain"]).default("quantum_and_plain")
|
|
18114
18123
|
}).transform((raw) => ({
|
|
18115
18124
|
appId: raw.appId,
|
|
18116
18125
|
appSecret: raw.appSecret,
|
|
@@ -18119,7 +18128,8 @@ var AccountConfigSchema = external_exports.object({
|
|
|
18119
18128
|
// quantumAppId: raw.quantumAppId,
|
|
18120
18129
|
// quantumAppSecret: raw.quantumAppSecret,
|
|
18121
18130
|
botUserId: raw.botUserId,
|
|
18122
|
-
env: raw.env
|
|
18131
|
+
env: raw.env,
|
|
18132
|
+
encryptionMode: raw.encryptionMode
|
|
18123
18133
|
}));
|
|
18124
18134
|
var ENV_PRESETS = {
|
|
18125
18135
|
/** 联调环境 */
|
|
@@ -19101,6 +19111,8 @@ var MessagePipe = class {
|
|
|
19101
19111
|
messageCallback = null;
|
|
19102
19112
|
/** 量子账户标识 — 用于判断是否具备解密能力 */
|
|
19103
19113
|
quantumAccount;
|
|
19114
|
+
/** 消息加密模式 */
|
|
19115
|
+
encryptionMode;
|
|
19104
19116
|
constructor(deps) {
|
|
19105
19117
|
this.wsClient = deps.wsClient;
|
|
19106
19118
|
this.dedup = deps.dedup;
|
|
@@ -19108,6 +19120,7 @@ var MessagePipe = class {
|
|
|
19108
19120
|
this.tokenFn = deps.tokenFn;
|
|
19109
19121
|
this.messageServiceBaseUrl = deps.messageServiceBaseUrl;
|
|
19110
19122
|
this.quantumAccount = deps.quantumAccount;
|
|
19123
|
+
this.encryptionMode = deps.encryptionMode;
|
|
19111
19124
|
this.wsClient.on("message", (rawData) => {
|
|
19112
19125
|
this.handleInbound(rawData).catch((err) => {
|
|
19113
19126
|
log12.error("\u274C \u5165\u7AD9\u5904\u7406\u5F02\u5E38", { step: "handleInbound", error: err.message });
|
|
@@ -19133,32 +19146,38 @@ var MessagePipe = class {
|
|
|
19133
19146
|
* POST {messageServiceBaseUrl}/messages/v1/send
|
|
19134
19147
|
* 字段映射: senderId → receive_id, msgType → msg_type
|
|
19135
19148
|
*
|
|
19136
|
-
*
|
|
19137
|
-
*
|
|
19138
|
-
*
|
|
19139
|
-
* - 原始 content 置为空字符串 {content: ''}
|
|
19140
|
-
* - extra 序列化为 JSON 字符串
|
|
19141
|
-
*
|
|
19142
|
-
* 失败不抛异常,避免阻塞 SDK deliver 回调。
|
|
19149
|
+
* 加密行为取决于 encryptionMode:
|
|
19150
|
+
* - quantum_only: 强制加密,失败直接抛错(不降级明文)
|
|
19151
|
+
* - quantum_and_plain: 加密失败降级明文(保持兼容)
|
|
19143
19152
|
*/
|
|
19144
19153
|
async sendMessage(msg) {
|
|
19145
19154
|
let content = msg.content;
|
|
19146
19155
|
let extra = "";
|
|
19147
|
-
if (
|
|
19148
|
-
|
|
19156
|
+
if (!msg.skipEncrypt && this.quantumAccount) {
|
|
19157
|
+
if (this.encryptionMode === "quantum_only") {
|
|
19149
19158
|
const { ciphertext, keyId, iv } = await this.crypto.encrypt(msg.content);
|
|
19150
19159
|
extra = JSON.stringify({
|
|
19151
19160
|
cryptoIv: iv,
|
|
19152
|
-
// 随机生成的 IV,解密时需要回传
|
|
19153
19161
|
encryptMsg: ciphertext,
|
|
19154
19162
|
sessionId: keyId
|
|
19155
19163
|
});
|
|
19156
19164
|
content = JSON.stringify({ content: "" });
|
|
19157
|
-
log12.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6", { sessionId: keyId });
|
|
19158
|
-
}
|
|
19159
|
-
|
|
19160
|
-
|
|
19161
|
-
|
|
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);
|
|
19169
|
+
extra = JSON.stringify({
|
|
19170
|
+
cryptoIv: iv,
|
|
19171
|
+
encryptMsg: ciphertext,
|
|
19172
|
+
sessionId: keyId
|
|
19173
|
+
});
|
|
19174
|
+
content = JSON.stringify({ content: "" });
|
|
19175
|
+
log12.debug("\u{1F512} \u51FA\u7AD9\u6D88\u606F\u5DF2\u52A0\u5BC6 (quantum_and_plain)", { sessionId: keyId });
|
|
19176
|
+
} catch (err) {
|
|
19177
|
+
log12.error("\u274C \u51FA\u7AD9\u6D88\u606F\u52A0\u5BC6\u5931\u8D25\uFF0C\u5C06\u53D1\u9001\u660E\u6587", {
|
|
19178
|
+
error: err.message
|
|
19179
|
+
});
|
|
19180
|
+
}
|
|
19162
19181
|
}
|
|
19163
19182
|
}
|
|
19164
19183
|
const body = {
|
|
@@ -19293,44 +19312,45 @@ var MessagePipe = class {
|
|
|
19293
19312
|
const extra = callbackData.extra;
|
|
19294
19313
|
const isEncrypted = typeof extra === "string" && extra.length > 0;
|
|
19295
19314
|
let contentObj;
|
|
19296
|
-
if (
|
|
19315
|
+
if (this.encryptionMode === "quantum_only") {
|
|
19297
19316
|
if (!this.quantumAccount) {
|
|
19298
|
-
log12.
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19317
|
+
log12.info("\u26A0\uFE0F quantum_only: \u672A\u914D\u7F6E quantumAccount\uFF0C\u53D1\u9001\u5F15\u5BFC\u63D0\u793A", { chatId: callbackData.userId });
|
|
19318
|
+
await this.sendHintMessage(callbackData, HINT_NO_QUANTUM_ACCOUNT);
|
|
19319
|
+
return;
|
|
19320
|
+
}
|
|
19321
|
+
if (!isEncrypted) {
|
|
19322
|
+
log12.info("\u26A0\uFE0F quantum_only: \u6536\u5230\u660E\u6587\u6D88\u606F\uFF0C\u62D2\u7EDD\u5904\u7406", { msgUid: callbackData.msgUid });
|
|
19323
|
+
await this.sendHintMessage(callbackData, HINT_PLAINTEXT_NOT_SUPPORTED);
|
|
19302
19324
|
return;
|
|
19303
19325
|
}
|
|
19304
19326
|
try {
|
|
19305
|
-
|
|
19306
|
-
const encryptMsg = extraData.encryptMsg ?? "";
|
|
19307
|
-
const sessionId = extraData.sessionId ?? "";
|
|
19308
|
-
if (!encryptMsg) {
|
|
19309
|
-
log12.warn("\u26A0\uFE0F extra \u4E2D encryptMsg \u4E3A\u7A7A\uFF0C\u8DF3\u8FC7\u89E3\u5BC6", { msgUid: callbackData.msgUid });
|
|
19310
|
-
contentObj = callbackData.content;
|
|
19311
|
-
} else {
|
|
19312
|
-
const cryptoIv = extraData.cryptoIv ?? "";
|
|
19313
|
-
const decrypted = await this.crypto.decrypt(encryptMsg, sessionId, cryptoIv);
|
|
19314
|
-
try {
|
|
19315
|
-
contentObj = JSON.parse(decrypted);
|
|
19316
|
-
} catch {
|
|
19317
|
-
contentObj = { content: decrypted };
|
|
19318
|
-
}
|
|
19319
|
-
log12.debug("\u{1F513} \u6D88\u606F\u89E3\u5BC6\u6210\u529F", {
|
|
19320
|
-
msgUid: callbackData.msgUid,
|
|
19321
|
-
sessionId,
|
|
19322
|
-
hasIv: Boolean(extraData.cryptoIv)
|
|
19323
|
-
});
|
|
19324
|
-
}
|
|
19327
|
+
contentObj = await this.decryptExtra(callbackData);
|
|
19325
19328
|
} catch (err) {
|
|
19326
|
-
log12.error("\u274C \u6D88\u606F\u89E3\u5BC6\u5931\u8D25", {
|
|
19329
|
+
log12.error("\u274C quantum_only: \u6D88\u606F\u89E3\u5BC6\u5931\u8D25", {
|
|
19327
19330
|
msgUid: callbackData.msgUid,
|
|
19328
19331
|
error: err.message
|
|
19329
19332
|
});
|
|
19330
19333
|
return;
|
|
19331
19334
|
}
|
|
19332
19335
|
} else {
|
|
19333
|
-
|
|
19336
|
+
if (isEncrypted) {
|
|
19337
|
+
if (!this.quantumAccount) {
|
|
19338
|
+
log12.info("\u26A0\uFE0F quantum_and_plain: \u6536\u5230\u52A0\u5BC6\u6D88\u606F\u4F46\u672A\u914D\u7F6E quantumAccount", { msgUid: callbackData.msgUid });
|
|
19339
|
+
await this.sendHintMessage(callbackData, HINT_NO_QUANTUM_ACCOUNT);
|
|
19340
|
+
return;
|
|
19341
|
+
}
|
|
19342
|
+
try {
|
|
19343
|
+
contentObj = await this.decryptExtra(callbackData);
|
|
19344
|
+
} catch (err) {
|
|
19345
|
+
log12.error("\u274C quantum_and_plain: \u6D88\u606F\u89E3\u5BC6\u5931\u8D25", {
|
|
19346
|
+
msgUid: callbackData.msgUid,
|
|
19347
|
+
error: err.message
|
|
19348
|
+
});
|
|
19349
|
+
return;
|
|
19350
|
+
}
|
|
19351
|
+
} else {
|
|
19352
|
+
contentObj = callbackData.content;
|
|
19353
|
+
}
|
|
19334
19354
|
}
|
|
19335
19355
|
if (callbackData.eventType !== "callback:direct") {
|
|
19336
19356
|
log12.debug("\u2139\uFE0F \u5FFD\u7565\u975E\u76EE\u6807\u4E8B\u4EF6", { eventType: callbackData.eventType });
|
|
@@ -19357,6 +19377,48 @@ var MessagePipe = class {
|
|
|
19357
19377
|
log12.warn("\u26A0\uFE0F \u6D88\u606F\u56DE\u8C03\u672A\u6CE8\u518C\uFF0C\u65E0\u6CD5\u5904\u7406", { messageId: msg.messageId });
|
|
19358
19378
|
}
|
|
19359
19379
|
}
|
|
19380
|
+
// ── 私有辅助方法 ──
|
|
19381
|
+
/**
|
|
19382
|
+
* 发送系统提示消息(明文,不加密)。
|
|
19383
|
+
* 用于在模式不匹配时向用户发送引导信息。
|
|
19384
|
+
*/
|
|
19385
|
+
async sendHintMessage(callbackData, hintText) {
|
|
19386
|
+
const chatId = callbackData.groupId || callbackData.userId;
|
|
19387
|
+
const senderId = callbackData.userId;
|
|
19388
|
+
try {
|
|
19389
|
+
await this.sendMessage({
|
|
19390
|
+
chatId,
|
|
19391
|
+
senderId,
|
|
19392
|
+
msgType: "markdown",
|
|
19393
|
+
content: JSON.stringify({ content: hintText }),
|
|
19394
|
+
skipEncrypt: true
|
|
19395
|
+
});
|
|
19396
|
+
log12.info("\u{1F4A1} \u5DF2\u53D1\u9001\u6A21\u5F0F\u63D0\u793A\u6D88\u606F", { chatId });
|
|
19397
|
+
} catch (err) {
|
|
19398
|
+
log12.error("\u274C \u63D0\u793A\u6D88\u606F\u53D1\u9001\u5931\u8D25", { error: err.message });
|
|
19399
|
+
}
|
|
19400
|
+
}
|
|
19401
|
+
/**
|
|
19402
|
+
* 解密 extra 中的加密内容。
|
|
19403
|
+
* @throws 解密失败时抛出异常
|
|
19404
|
+
*/
|
|
19405
|
+
async decryptExtra(callbackData) {
|
|
19406
|
+
const extraData = JSON.parse(callbackData.extra);
|
|
19407
|
+
const encryptMsg = extraData.encryptMsg ?? "";
|
|
19408
|
+
const sessionId = extraData.sessionId ?? "";
|
|
19409
|
+
if (!encryptMsg) {
|
|
19410
|
+
log12.warn("\u26A0\uFE0F extra \u4E2D encryptMsg \u4E3A\u7A7A\uFF0C\u4F7F\u7528\u539F\u59CB content", { msgUid: callbackData.msgUid });
|
|
19411
|
+
return callbackData.content;
|
|
19412
|
+
}
|
|
19413
|
+
const cryptoIv = extraData.cryptoIv ?? "";
|
|
19414
|
+
const decrypted = await this.crypto.decrypt(encryptMsg, sessionId, cryptoIv);
|
|
19415
|
+
log12.debug("\u{1F513} \u6D88\u606F\u89E3\u5BC6\u6210\u529F", { msgUid: callbackData.msgUid, sessionId, hasIv: Boolean(cryptoIv) });
|
|
19416
|
+
try {
|
|
19417
|
+
return JSON.parse(decrypted);
|
|
19418
|
+
} catch {
|
|
19419
|
+
return { content: decrypted };
|
|
19420
|
+
}
|
|
19421
|
+
}
|
|
19360
19422
|
};
|
|
19361
19423
|
|
|
19362
19424
|
// src/transport/connection-manager.ts
|
|
@@ -19993,6 +20055,13 @@ var QUANTUM_IM_CONFIG_JSON_SCHEMA = {
|
|
|
19993
20055
|
description: "\u9009\u62E9\u5BF9\u63A5\u7684\u670D\u52A1\u5668\u73AF\u5883\u3002staging = \u8054\u8C03\u73AF\u5883\uFF0Cproduction = \u7EBF\u4E0A\u73AF\u5883",
|
|
19994
20056
|
enum: ["staging", "production"],
|
|
19995
20057
|
default: "production"
|
|
20058
|
+
},
|
|
20059
|
+
encryptionMode: {
|
|
20060
|
+
type: "string",
|
|
20061
|
+
title: "\u6D88\u606F\u52A0\u5BC6\u6A21\u5F0F",
|
|
20062
|
+
description: "quantum_only = \u4EC5\u91CF\u5B50\u52A0\u5BC6\u6D88\u606F\uFF08\u6240\u6709\u6D88\u606F\u5FC5\u987B\u52A0\u5BC6\uFF09\uFF1Bquantum_and_plain = \u540C\u65F6\u652F\u6301\u52A0\u5BC6\u548C\u666E\u901A\u6D88\u606F",
|
|
20063
|
+
enum: ["quantum_only", "quantum_and_plain"],
|
|
20064
|
+
default: "quantum_and_plain"
|
|
19996
20065
|
}
|
|
19997
20066
|
}
|
|
19998
20067
|
};
|
|
@@ -20062,7 +20131,8 @@ async function startPlugin(accountConfig, internalOverrides) {
|
|
|
20062
20131
|
crypto: cryptoEngine,
|
|
20063
20132
|
tokenFn: () => tokenManager.getValidToken(),
|
|
20064
20133
|
messageServiceBaseUrl: config2.message.messageServiceBaseUrl,
|
|
20065
|
-
quantumAccount: accountConfig.quantumAccount
|
|
20134
|
+
quantumAccount: accountConfig.quantumAccount,
|
|
20135
|
+
encryptionMode: accountConfig.encryptionMode
|
|
20066
20136
|
});
|
|
20067
20137
|
const connectionManager = new ConnectionManager(wsClient, {
|
|
20068
20138
|
heartbeatIntervalMs: config2.transport.heartbeatIntervalMs,
|
|
@@ -20382,8 +20452,10 @@ function createQuantumImDeliverFn(deps) {
|
|
|
20382
20452
|
maxFileSizeMb,
|
|
20383
20453
|
chunkSizeMb,
|
|
20384
20454
|
timeoutMs,
|
|
20385
|
-
isEncrypted
|
|
20455
|
+
isEncrypted,
|
|
20456
|
+
encryptionMode
|
|
20386
20457
|
} = deps;
|
|
20458
|
+
const shouldSkipEncrypt = encryptionMode === "quantum_only" ? false : isEncrypted === false;
|
|
20387
20459
|
const deliver = async (payload) => {
|
|
20388
20460
|
const mediaUrls = [];
|
|
20389
20461
|
if (payload.mediaUrls?.length) {
|
|
@@ -20401,7 +20473,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
20401
20473
|
msgType: "markdown",
|
|
20402
20474
|
content: JSON.stringify({ content: payload.text }),
|
|
20403
20475
|
replyToMessageId,
|
|
20404
|
-
skipEncrypt:
|
|
20476
|
+
skipEncrypt: shouldSkipEncrypt
|
|
20405
20477
|
});
|
|
20406
20478
|
log23.info("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u5DF2\u53D1\u9001", {
|
|
20407
20479
|
chatId,
|
|
@@ -20424,7 +20496,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
20424
20496
|
maxFileSizeMb,
|
|
20425
20497
|
chunkSizeMb,
|
|
20426
20498
|
timeoutMs,
|
|
20427
|
-
skipEncrypt:
|
|
20499
|
+
skipEncrypt: shouldSkipEncrypt
|
|
20428
20500
|
});
|
|
20429
20501
|
if (result.warning) {
|
|
20430
20502
|
log23.warn("\u{1F4E4} \u5A92\u4F53\u53D1\u9001\u964D\u7EA7", { chatId, mediaUrl, warning: result.warning });
|
|
@@ -20443,7 +20515,7 @@ function createQuantumImDeliverFn(deps) {
|
|
|
20443
20515
|
msgType: "markdown",
|
|
20444
20516
|
content: JSON.stringify({ content: `\u{1F4CE} ${mediaUrl}` }),
|
|
20445
20517
|
replyToMessageId,
|
|
20446
|
-
skipEncrypt:
|
|
20518
|
+
skipEncrypt: shouldSkipEncrypt
|
|
20447
20519
|
});
|
|
20448
20520
|
}
|
|
20449
20521
|
}
|
|
@@ -20481,12 +20553,13 @@ var InboundPipeline = class {
|
|
|
20481
20553
|
});
|
|
20482
20554
|
return;
|
|
20483
20555
|
}
|
|
20556
|
+
const feedbackSkipEncrypt = this.deps.pluginConfig.credentials.encryptionMode === "quantum_only" ? false : true;
|
|
20484
20557
|
this.deps.messagePipe.sendMessage({
|
|
20485
20558
|
chatId: msg.chatId,
|
|
20486
20559
|
senderId: msg.senderId,
|
|
20487
20560
|
msgType: "text",
|
|
20488
20561
|
content: JSON.stringify({ content: "\u4EFB\u52A1\u5DF2\u6536\u5230\u{1F44C}\uFF0C\u6B63\u5728\u5904\u7406\u4E2D..." }),
|
|
20489
|
-
skipEncrypt:
|
|
20562
|
+
skipEncrypt: feedbackSkipEncrypt
|
|
20490
20563
|
}).catch((err) => {
|
|
20491
20564
|
log24.warn("\u26A0\uFE0F \u5373\u65F6\u53CD\u9988\u53D1\u9001\u5931\u8D25", { error: err.message });
|
|
20492
20565
|
});
|
|
@@ -20514,7 +20587,8 @@ var InboundPipeline = class {
|
|
|
20514
20587
|
allowPrivateNetwork: this.deps.pluginConfig.file.allowPrivateNetwork,
|
|
20515
20588
|
maxFileSizeMb: this.deps.pluginConfig.file.maxFileSizeMb,
|
|
20516
20589
|
timeoutMs: this.deps.pluginConfig.file.fetchTimeoutMs,
|
|
20517
|
-
isEncrypted: msg.isEncrypted
|
|
20590
|
+
isEncrypted: msg.isEncrypted,
|
|
20591
|
+
encryptionMode: this.deps.pluginConfig.credentials.encryptionMode
|
|
20518
20592
|
});
|
|
20519
20593
|
const { dispatcher, replyOptions } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
20520
20594
|
deliver
|
|
@@ -20647,33 +20721,39 @@ var quantumImOnboarding = {
|
|
|
20647
20721
|
].join("\n"),
|
|
20648
20722
|
"\u91CF\u5B50\u5BC6\u4FE1 \u2014 \u51ED\u636E\u914D\u7F6E"
|
|
20649
20723
|
);
|
|
20650
|
-
const
|
|
20651
|
-
message: "\
|
|
20652
|
-
initialValue: existing.appId ?? void 0,
|
|
20653
|
-
validate: required2
|
|
20654
|
-
});
|
|
20655
|
-
const appSecret = await prompter.text({
|
|
20656
|
-
message: "\u5E94\u7528\u5BC6\u94A5 (appSecret)",
|
|
20657
|
-
initialValue: existing.appSecret ?? void 0,
|
|
20658
|
-
validate: required2
|
|
20659
|
-
});
|
|
20660
|
-
const encryptionMode = await prompter.select({
|
|
20661
|
-
message: "\u6D88\u606F\u4F20\u8F93\u6A21\u5F0F",
|
|
20724
|
+
const encryptionModeChoice = await prompter.select({
|
|
20725
|
+
message: "\u8BF7\u9009\u62E9\u6D88\u606F\u4EA4\u4E92\u65B9\u5F0F",
|
|
20662
20726
|
options: [
|
|
20663
|
-
{ value: "
|
|
20664
|
-
{ value: "
|
|
20727
|
+
{ value: "quantum_only", label: "\u4EC5\u91CF\u5B50\u52A0\u5BC6\u6D88\u606F" },
|
|
20728
|
+
{ value: "quantum_and_plain", label: "\u91CF\u5B50\u52A0\u5BC6+\u666E\u901A\u6D88\u606F\uFF08\u82E5\u91CF\u5B50\u52A0\u5BC6\u7981\u7528\uFF0C\u5219\u7559\u7A7A\uFF09" }
|
|
20665
20729
|
],
|
|
20666
|
-
initialValue: existing.
|
|
20730
|
+
initialValue: existing.encryptionMode ?? "quantum_and_plain"
|
|
20667
20731
|
});
|
|
20668
20732
|
let quantumAccount = "";
|
|
20669
|
-
if (
|
|
20733
|
+
if (encryptionModeChoice === "quantum_only") {
|
|
20670
20734
|
quantumAccount = await prompter.text({
|
|
20671
20735
|
message: "\u91CF\u5B50\u8D26\u6237\u6807\u8BC6 (quantumAccount)",
|
|
20672
20736
|
placeholder: "\u5982: 17100001111",
|
|
20673
20737
|
initialValue: existing.quantumAccount ?? void 0,
|
|
20674
20738
|
validate: required2
|
|
20675
20739
|
});
|
|
20740
|
+
} else {
|
|
20741
|
+
quantumAccount = await prompter.text({
|
|
20742
|
+
message: "\u91CF\u5B50\u8D26\u6237\u6807\u8BC6\uFF08\u53EF\u9009\uFF0C\u4E0D\u586B\u5219\u4EC5\u652F\u6301\u660E\u6587\uFF09",
|
|
20743
|
+
placeholder: "\u5982: 17100001111",
|
|
20744
|
+
initialValue: existing.quantumAccount ?? void 0
|
|
20745
|
+
});
|
|
20676
20746
|
}
|
|
20747
|
+
const appId = await prompter.text({
|
|
20748
|
+
message: "\u5E94\u7528 ID (appId)",
|
|
20749
|
+
initialValue: existing.appId ?? void 0,
|
|
20750
|
+
validate: required2
|
|
20751
|
+
});
|
|
20752
|
+
const appSecret = await prompter.text({
|
|
20753
|
+
message: "\u5E94\u7528\u5BC6\u94A5 (appSecret)",
|
|
20754
|
+
initialValue: existing.appSecret ?? void 0,
|
|
20755
|
+
validate: required2
|
|
20756
|
+
});
|
|
20677
20757
|
const channels = cfg.channels ?? {};
|
|
20678
20758
|
const channelCfg = channels[CHANNEL_ID] ?? {};
|
|
20679
20759
|
const accounts = channelCfg.accounts ?? {};
|
|
@@ -20689,10 +20769,8 @@ var quantumImOnboarding = {
|
|
|
20689
20769
|
default: {
|
|
20690
20770
|
appId: appId.trim(),
|
|
20691
20771
|
appSecret: appSecret.trim(),
|
|
20692
|
-
|
|
20772
|
+
encryptionMode: encryptionModeChoice,
|
|
20693
20773
|
...quantumAccount.trim() ? { quantumAccount: quantumAccount.trim() } : {}
|
|
20694
|
-
// quantumAppId: quantumAppId.trim(),
|
|
20695
|
-
// quantumAppSecret: quantumAppSecret.trim(),
|
|
20696
20774
|
}
|
|
20697
20775
|
}
|
|
20698
20776
|
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
"description": "选择对接的服务器环境。staging = 联调环境,production = 线上环境",
|
|
34
34
|
"enum": ["staging", "production"],
|
|
35
35
|
"default": "production"
|
|
36
|
+
},
|
|
37
|
+
"encryptionMode": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"title": "消息加密模式",
|
|
40
|
+
"description": "quantum_only = 仅量子加密消息(所有消息必须加密);quantum_and_plain = 同时支持加密和普通消息",
|
|
41
|
+
"enum": ["quantum_only", "quantum_and_plain"],
|
|
42
|
+
"default": "quantum_and_plain"
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
}
|