wowok 2.1.20 → 2.1.22
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/cjs/w/local/account.d.ts +1 -1
- package/dist/cjs/w/local/account.js +15 -47
- package/dist/cjs/w/local/account.js.map +1 -1
- package/dist/cjs/w/messenger/crypto.js +31 -0
- package/dist/cjs/w/messenger/crypto.js.map +1 -1
- package/dist/cjs/w/messenger/messenger-api.d.ts +27 -4
- package/dist/cjs/w/messenger/messenger-api.js +191 -256
- package/dist/cjs/w/messenger/messenger-api.js.map +1 -1
- package/dist/cjs/w/messenger/messenger-manager.d.ts +39 -3
- package/dist/cjs/w/messenger/messenger-manager.js +194 -107
- package/dist/cjs/w/messenger/messenger-manager.js.map +1 -1
- package/dist/cjs/w/messenger/messenger.d.ts +9 -1
- package/dist/cjs/w/messenger/messenger.js +33 -0
- package/dist/cjs/w/messenger/messenger.js.map +1 -1
- package/dist/cjs/w/messenger/server.d.ts +2 -2
- package/dist/cjs/w/messenger/server.js.map +1 -1
- package/dist/cjs/w/messenger/session.d.ts +15 -0
- package/dist/cjs/w/messenger/session.js +83 -35
- package/dist/cjs/w/messenger/session.js.map +1 -1
- package/dist/cjs/w/messenger/storage.d.ts +12 -0
- package/dist/cjs/w/messenger/storage.js +51 -2
- package/dist/cjs/w/messenger/storage.js.map +1 -1
- package/dist/cjs/w/messenger/templates/wts-html-template.d.ts +61 -0
- package/dist/cjs/w/messenger/templates/wts-html-template.js +414 -0
- package/dist/cjs/w/messenger/templates/wts-html-template.js.map +1 -0
- package/dist/cjs/w/messenger/types.d.ts +9 -5
- package/dist/cjs/w/messenger/types.js +3 -2
- package/dist/cjs/w/messenger/types.js.map +1 -1
- package/dist/esm/w/local/account.d.ts +1 -1
- package/dist/esm/w/local/account.js +15 -47
- package/dist/esm/w/local/account.js.map +1 -1
- package/dist/esm/w/messenger/crypto.js +31 -0
- package/dist/esm/w/messenger/crypto.js.map +1 -1
- package/dist/esm/w/messenger/messenger-api.d.ts +27 -4
- package/dist/esm/w/messenger/messenger-api.js +191 -256
- package/dist/esm/w/messenger/messenger-api.js.map +1 -1
- package/dist/esm/w/messenger/messenger-manager.d.ts +39 -3
- package/dist/esm/w/messenger/messenger-manager.js +194 -107
- package/dist/esm/w/messenger/messenger-manager.js.map +1 -1
- package/dist/esm/w/messenger/messenger.d.ts +9 -1
- package/dist/esm/w/messenger/messenger.js +33 -0
- package/dist/esm/w/messenger/messenger.js.map +1 -1
- package/dist/esm/w/messenger/server.d.ts +2 -2
- package/dist/esm/w/messenger/server.js.map +1 -1
- package/dist/esm/w/messenger/session.d.ts +15 -0
- package/dist/esm/w/messenger/session.js +83 -35
- package/dist/esm/w/messenger/session.js.map +1 -1
- package/dist/esm/w/messenger/storage.d.ts +12 -0
- package/dist/esm/w/messenger/storage.js +51 -2
- package/dist/esm/w/messenger/storage.js.map +1 -1
- package/dist/esm/w/messenger/templates/wts-html-template.d.ts +61 -0
- package/dist/esm/w/messenger/templates/wts-html-template.js +414 -0
- package/dist/esm/w/messenger/templates/wts-html-template.js.map +1 -0
- package/dist/esm/w/messenger/types.d.ts +9 -5
- package/dist/esm/w/messenger/types.js +3 -2
- package/dist/esm/w/messenger/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ import * as path from "path";
|
|
|
12
12
|
import { isValidU64 } from "../common.js";
|
|
13
13
|
import { verifySingleMerkleRoot, hashPlaintext, verifyEd25519Signature, } from "./crypto.js";
|
|
14
14
|
import { canonicalizeJson } from "./utils.js";
|
|
15
|
+
import { generateHtmlPage, getThemeColors, } from "./templates/wts-html-template.js";
|
|
15
16
|
import { GetAccountOrMark_Address, GetManyAccountOrMark_Address, } from "../local/index.js";
|
|
16
17
|
import { query_objects } from "../query/object.js";
|
|
17
18
|
import { Messenger } from "./messenger.js";
|
|
@@ -89,10 +90,15 @@ export async function watch_conversations(account) {
|
|
|
89
90
|
W_ERROR(WErrors.AccountNotFound, `watch_conversations.account ${account}`);
|
|
90
91
|
}
|
|
91
92
|
const myAddress = address.toLowerCase();
|
|
92
|
-
//
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
// 【修改】使用 MessengerManager 确保账号已就绪并拉取消息
|
|
94
|
+
const manager = getMessengerManager();
|
|
95
|
+
if (!isInitialized) {
|
|
96
|
+
await manager.start();
|
|
97
|
+
}
|
|
98
|
+
// 确保账号已就绪(自动初始化)
|
|
99
|
+
const accountInfo = await manager.ensureAccountReady(address);
|
|
100
|
+
// 拉取消息
|
|
101
|
+
await accountInfo.messenger.pullMessages();
|
|
96
102
|
const messageStorage = new MessageStorage(myAddress);
|
|
97
103
|
const allMessages = messageStorage.getAllMessages(myAddress);
|
|
98
104
|
const conversations = new Map();
|
|
@@ -150,9 +156,12 @@ export async function send_message(from, to, content, options) {
|
|
|
150
156
|
W_ERROR(WErrors.AccountNotFound, `send_message.guardAddress or passportAddress not found`);
|
|
151
157
|
}
|
|
152
158
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
// 【修改】使用 MessengerManager 确保账号已就绪并发送消息
|
|
160
|
+
const manager = getMessengerManager();
|
|
161
|
+
if (!isInitialized) {
|
|
162
|
+
await manager.start();
|
|
163
|
+
}
|
|
164
|
+
return manager.send(fromAddress, t, content, options);
|
|
156
165
|
}
|
|
157
166
|
/**
|
|
158
167
|
* 发送文件(ZIP 格式)
|
|
@@ -185,7 +194,11 @@ export async function send_file(from, to, filePath, options) {
|
|
|
185
194
|
W_ERROR(WErrors.AccountNotFound, `send_file.guardAddress or passportAddress not found`);
|
|
186
195
|
}
|
|
187
196
|
}
|
|
197
|
+
// 【修改】使用 MessengerManager 确保账号已就绪并发送文件
|
|
188
198
|
const manager = getMessengerManager();
|
|
199
|
+
if (!isInitialized) {
|
|
200
|
+
await manager.start();
|
|
201
|
+
}
|
|
189
202
|
return manager.send_file(fromAddress, t, filePath, options);
|
|
190
203
|
}
|
|
191
204
|
/**
|
|
@@ -207,7 +220,11 @@ export async function watch_messages(filter) {
|
|
|
207
220
|
if (filter?.customListFilter?.excludeAddresses != null) {
|
|
208
221
|
filter.customListFilter.excludeAddresses = (await Account.Instance().get_many_address(filter.customListFilter.excludeAddresses)).filter((v) => v != null);
|
|
209
222
|
}
|
|
223
|
+
// 【修改】使用 MessengerManager 确保账号已就绪并查询消息
|
|
210
224
|
const manager = getMessengerManager();
|
|
225
|
+
if (!isInitialized) {
|
|
226
|
+
await manager.start();
|
|
227
|
+
}
|
|
211
228
|
return manager.watch(filter);
|
|
212
229
|
}
|
|
213
230
|
export async function pull_messages(account, limit) {
|
|
@@ -215,9 +232,13 @@ export async function pull_messages(account, limit) {
|
|
|
215
232
|
if (!address) {
|
|
216
233
|
W_ERROR(WErrors.AccountNotFound, `pull_messages.account ${account}`);
|
|
217
234
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
235
|
+
// 【修改】使用 MessengerManager 确保账号已就绪并拉取消息
|
|
236
|
+
const manager = getMessengerManager();
|
|
237
|
+
if (!isInitialized) {
|
|
238
|
+
await manager.start();
|
|
239
|
+
}
|
|
240
|
+
const accountInfo = await manager.ensureAccountReady(address);
|
|
241
|
+
const result = await accountInfo.messenger.pullMessages(limit);
|
|
221
242
|
return result.messages.map((dm) => dm);
|
|
222
243
|
}
|
|
223
244
|
/**
|
|
@@ -297,12 +318,11 @@ export async function extract_zip_messages(account, messages, outputDir) {
|
|
|
297
318
|
if (!userAddress) {
|
|
298
319
|
throw new MessengerError(MessengerErrorCode.INVALID_INPUT, "account is required when extracting by message IDs");
|
|
299
320
|
}
|
|
300
|
-
// 判断是 Message 数组还是 messageId 数组
|
|
301
|
-
const isMessageIds = messages.length > 0 && typeof messages[0] === "string";
|
|
302
321
|
for (const item of messages) {
|
|
303
322
|
try {
|
|
304
323
|
let message;
|
|
305
|
-
|
|
324
|
+
// 逐个判断是 Message 对象还是 messageId 字符串
|
|
325
|
+
if (typeof item === "string") {
|
|
306
326
|
const messageId = item;
|
|
307
327
|
const messageStorage = new MessageStorage(userAddress);
|
|
308
328
|
const storedMessage = messageStorage.getMessageById(messageId);
|
|
@@ -324,9 +344,7 @@ export async function extract_zip_messages(account, messages, outputDir) {
|
|
|
324
344
|
results.push(filePath);
|
|
325
345
|
}
|
|
326
346
|
catch (e) {
|
|
327
|
-
const id =
|
|
328
|
-
? item
|
|
329
|
-
: item.messageId;
|
|
347
|
+
const id = typeof item === "string" ? item : item.messageId;
|
|
330
348
|
console.error(`Failed to extract message ${id}:`, e);
|
|
331
349
|
}
|
|
332
350
|
}
|
|
@@ -384,7 +402,7 @@ export async function verify_wts(wtsFilePath) {
|
|
|
384
402
|
// 1. 加载 WTS 文件
|
|
385
403
|
const wtsFile = load_wts(wtsFilePath);
|
|
386
404
|
// 2. 验证基本结构
|
|
387
|
-
if (!wtsFile.
|
|
405
|
+
if (!wtsFile.payload || !wtsFile.meta) {
|
|
388
406
|
return { valid: false, error: "Invalid WTS file structure" };
|
|
389
407
|
}
|
|
390
408
|
if (wtsFile.meta.type !== "wts") {
|
|
@@ -394,20 +412,91 @@ export async function verify_wts(wtsFilePath) {
|
|
|
394
412
|
};
|
|
395
413
|
}
|
|
396
414
|
const { payload, meta } = wtsFile;
|
|
397
|
-
// 2. 验证每个消息的 Merkle Root
|
|
398
|
-
//
|
|
399
|
-
|
|
415
|
+
// 2. 验证每个消息的 Merkle Root 计算和链连续性
|
|
416
|
+
// 首先按leafIndex排序消息
|
|
417
|
+
const sortedMessages = [...payload.messages].sort((a, b) => {
|
|
418
|
+
if (a.leafIndex === undefined || b.leafIndex === undefined)
|
|
419
|
+
return 0;
|
|
420
|
+
return a.leafIndex - b.leafIndex;
|
|
421
|
+
});
|
|
422
|
+
for (let i = 0; i < sortedMessages.length; i++) {
|
|
423
|
+
const msg = sortedMessages[i];
|
|
424
|
+
// 检查消息对象是否为空
|
|
425
|
+
if (!msg) {
|
|
426
|
+
return {
|
|
427
|
+
valid: false,
|
|
428
|
+
error: `Message ${i}: Message is null or undefined`,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
// 检查关键字段是否为空
|
|
432
|
+
if (!msg.prevRoot) {
|
|
433
|
+
return {
|
|
434
|
+
valid: false,
|
|
435
|
+
error: `Message ${i}: Missing prevRoot`,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (!msg.merkleRoot) {
|
|
439
|
+
return {
|
|
440
|
+
valid: false,
|
|
441
|
+
error: `Message ${i}: Missing merkleRoot`,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (!msg.plaintextHash) {
|
|
445
|
+
return {
|
|
446
|
+
valid: false,
|
|
447
|
+
error: `Message ${i}: Missing plaintextHash`,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (msg.timestamp === undefined || msg.timestamp === null) {
|
|
451
|
+
return {
|
|
452
|
+
valid: false,
|
|
453
|
+
error: `Message ${i}: Missing timestamp`,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (msg.leafIndex === undefined || msg.leafIndex === null) {
|
|
457
|
+
return {
|
|
458
|
+
valid: false,
|
|
459
|
+
error: `Message ${i}: Missing leafIndex`,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
// 验证单个消息的Merkle Root计算
|
|
400
463
|
const result = verifySingleMerkleRoot(msg.prevRoot, msg.merkleRoot, msg.plaintextHash, msg.timestamp, msg.leafIndex);
|
|
401
464
|
if (!result.valid) {
|
|
402
465
|
return {
|
|
403
466
|
valid: false,
|
|
404
|
-
error: `${result.error} at leafIndex ${msg.leafIndex}`,
|
|
467
|
+
error: `${result.error} at message ${i}, leafIndex ${msg.leafIndex}`,
|
|
405
468
|
};
|
|
406
469
|
}
|
|
470
|
+
// 【新增】验证Merkle链连续性(除了第一条消息)
|
|
471
|
+
if (i > 0) {
|
|
472
|
+
const prevMsg = sortedMessages[i - 1];
|
|
473
|
+
// 检查leafIndex是否连续
|
|
474
|
+
if (msg.leafIndex !== prevMsg.leafIndex + 1) {
|
|
475
|
+
return {
|
|
476
|
+
valid: false,
|
|
477
|
+
error: `Merkle chain discontinuity: leafIndex ${prevMsg.leafIndex} -> ${msg.leafIndex} (expected ${prevMsg.leafIndex + 1})`,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
// 检查merkleRoot是否匹配
|
|
481
|
+
if (msg.prevRoot.toLowerCase() !==
|
|
482
|
+
prevMsg.merkleRoot.toLowerCase()) {
|
|
483
|
+
return {
|
|
484
|
+
valid: false,
|
|
485
|
+
error: `Merkle root mismatch at leafIndex ${msg.leafIndex}: expected prevRoot=${prevMsg.merkleRoot}, got ${msg.prevRoot}`,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
407
489
|
}
|
|
408
490
|
// 3. 验证每个消息的 plaintextHash 和服务器签名(如果提供了明文)
|
|
409
491
|
for (let i = 0; i < payload.messages.length; i++) {
|
|
410
492
|
const msg = payload.messages[i];
|
|
493
|
+
// 检查消息对象是否为空
|
|
494
|
+
if (!msg) {
|
|
495
|
+
return {
|
|
496
|
+
valid: false,
|
|
497
|
+
error: `Message ${i}: Message is null or undefined`,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
411
500
|
// 判断是否为非文本消息(ZIP/WTS/WIP)
|
|
412
501
|
const isNonTextMsg = !!msg.zipMetadata;
|
|
413
502
|
// 对于非文本消息,跳过 plaintextHash 格式验证(它们使用不同的哈希格式)
|
|
@@ -795,247 +884,38 @@ async function convertWtsToHtml(wtsFilePath, options = {}) {
|
|
|
795
884
|
// 2. 加载 WTS 文件
|
|
796
885
|
const wtsFile = load_wts(wtsFilePath);
|
|
797
886
|
const { payload, meta } = wtsFile;
|
|
798
|
-
|
|
799
|
-
const
|
|
800
|
-
const borderColor = theme === "dark" ? "#444444" : "#e0e0e0";
|
|
801
|
-
const escapeHtml = (text) => {
|
|
802
|
-
return text
|
|
803
|
-
.replace(/&/g, "&")
|
|
804
|
-
.replace(/</g, "<")
|
|
805
|
-
.replace(/>/g, ">")
|
|
806
|
-
.replace(/"/g, """)
|
|
807
|
-
.replace(/'/g, "'");
|
|
808
|
-
};
|
|
809
|
-
// 判断是否为非文本消息(ZIP/WTS/WIP)
|
|
810
|
-
const isNonTextMessage = (msg) => {
|
|
811
|
-
return !!msg.zipMetadata;
|
|
812
|
-
};
|
|
813
|
-
// 获取文件类型标签
|
|
814
|
-
const getFileTypeLabel = (msg) => {
|
|
815
|
-
// 首先检查 contentType
|
|
816
|
-
if (msg.zipMetadata?.contentType) {
|
|
817
|
-
return msg.zipMetadata.contentType.toUpperCase();
|
|
818
|
-
}
|
|
819
|
-
// 根据文件名推断类型
|
|
820
|
-
const fileName = msg.zipMetadata?.fileName || "";
|
|
821
|
-
if (fileName.endsWith(".wts"))
|
|
822
|
-
return "WTS";
|
|
823
|
-
if (fileName.endsWith(".wip"))
|
|
824
|
-
return "WIP";
|
|
825
|
-
if (fileName.endsWith(".zip"))
|
|
826
|
-
return "ZIP";
|
|
827
|
-
if (fileName.endsWith(".json"))
|
|
828
|
-
return "JSON";
|
|
829
|
-
if (fileName.endsWith(".txt"))
|
|
830
|
-
return "TXT";
|
|
831
|
-
// 根据 zipMetadata 推断
|
|
832
|
-
if (msg.zipMetadata)
|
|
833
|
-
return "ZIP";
|
|
834
|
-
return "FILE";
|
|
835
|
-
};
|
|
836
|
-
// 生成保存按钮的 HTML
|
|
837
|
-
const generateSaveButton = (msg, index) => {
|
|
838
|
-
const fileType = getFileTypeLabel(msg);
|
|
839
|
-
const fileName = msg.zipMetadata?.fileName ||
|
|
840
|
-
`message_${index}.${fileType.toLowerCase()}`;
|
|
841
|
-
// 对于非文本消息,plaintext 是 base64 编码的文件内容
|
|
842
|
-
const fileData = msg.plaintext || "";
|
|
843
|
-
return `
|
|
844
|
-
<div style="margin: 12px 0; padding: 16px; background: ${theme === "dark" ? "#2a3f2a" : "#e8f5e9"}; border: 1px solid ${theme === "dark" ? "#4caf50" : "#81c784"}; border-radius: 8px; text-align: center;">
|
|
845
|
-
<div style="font-size: 14px; color: ${theme === "dark" ? "#81c784" : "#2e7d32"}; margin-bottom: 8px;">
|
|
846
|
-
📎 ${fileType} File
|
|
847
|
-
${msg.zipMetadata ? `<span style="font-size: 12px; color: #666;">(${formatFileSize(msg.zipMetadata.fileSize)})</span>` : ""}
|
|
848
|
-
</div>
|
|
849
|
-
<button
|
|
850
|
-
onclick="saveFile${index}()"
|
|
851
|
-
style="padding: 10px 24px; background: #4caf50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;"
|
|
852
|
-
onmouseover="this.style.background='#45a049'"
|
|
853
|
-
onmouseout="this.style.background='#4caf50'"
|
|
854
|
-
>
|
|
855
|
-
💾 Save ${escapeHtml(fileName)}
|
|
856
|
-
</button>
|
|
857
|
-
</div>
|
|
858
|
-
<script>
|
|
859
|
-
function saveFile${index}() {
|
|
860
|
-
try {
|
|
861
|
-
// Base64 解码
|
|
862
|
-
const base64Data = "${escapeHtml(fileData)}";
|
|
863
|
-
const byteCharacters = atob(base64Data);
|
|
864
|
-
const byteNumbers = new Array(byteCharacters.length);
|
|
865
|
-
for (let i = 0; i < byteCharacters.length; i++) {
|
|
866
|
-
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
867
|
-
}
|
|
868
|
-
const byteArray = new Uint8Array(byteNumbers);
|
|
869
|
-
|
|
870
|
-
// 根据文件类型设置 MIME 类型
|
|
871
|
-
let mimeType = "application/octet-stream";
|
|
872
|
-
const fileName = "${escapeHtml(fileName)}";
|
|
873
|
-
if (fileName.endsWith('.json')) mimeType = "application/json";
|
|
874
|
-
else if (fileName.endsWith('.zip')) mimeType = "application/zip";
|
|
875
|
-
else if (fileName.endsWith('.txt')) mimeType = "text/plain";
|
|
876
|
-
|
|
877
|
-
const blob = new Blob([byteArray], { type: mimeType });
|
|
878
|
-
const url = URL.createObjectURL(blob);
|
|
879
|
-
const a = document.createElement("a");
|
|
880
|
-
a.href = url;
|
|
881
|
-
a.download = fileName;
|
|
882
|
-
document.body.appendChild(a);
|
|
883
|
-
a.click();
|
|
884
|
-
document.body.removeChild(a);
|
|
885
|
-
URL.revokeObjectURL(url);
|
|
886
|
-
} catch (e) {
|
|
887
|
-
alert('保存文件失败: ' + e.message);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
</script>
|
|
891
|
-
`;
|
|
892
|
-
};
|
|
893
|
-
// 格式化文件大小
|
|
894
|
-
const formatFileSize = (bytes) => {
|
|
895
|
-
if (bytes < 1024)
|
|
896
|
-
return bytes + " B";
|
|
897
|
-
if (bytes < 1024 * 1024)
|
|
898
|
-
return (bytes / 1024).toFixed(1) + " KB";
|
|
899
|
-
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
|
|
900
|
-
};
|
|
887
|
+
// 获取主题颜色
|
|
888
|
+
const { bgColor, textColor, borderColor } = getThemeColors(theme);
|
|
901
889
|
// 【修正】分析消息确定"我"是谁,并收集最大 lastReceivedLeafIndex
|
|
902
|
-
// WTS
|
|
903
|
-
//
|
|
904
|
-
const
|
|
890
|
+
// 算法:找出生成WTS的账户(myAccount)收到的消息中,lastReceivedLeafIndex的最大值
|
|
891
|
+
// 生成WTS的账户 = meta.creator
|
|
892
|
+
const myAddress = meta.creator;
|
|
905
893
|
let maxLastReceivedLeafIndex = -1;
|
|
906
894
|
for (const msg of payload.messages) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
895
|
+
// 只考虑"我"收到的消息(to === myAddress)
|
|
896
|
+
if (msg.to.toLowerCase() === myAddress.toLowerCase()) {
|
|
897
|
+
if (msg.lastReceivedLeafIndex !== undefined &&
|
|
898
|
+
msg.lastReceivedLeafIndex >= 0) {
|
|
899
|
+
// 收集最大 lastReceivedLeafIndex
|
|
900
|
+
if (msg.lastReceivedLeafIndex > maxLastReceivedLeafIndex) {
|
|
901
|
+
maxLastReceivedLeafIndex = msg.lastReceivedLeafIndex;
|
|
902
|
+
}
|
|
915
903
|
}
|
|
916
904
|
}
|
|
917
905
|
}
|
|
918
|
-
//
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
// 只标记"我发送的消息",且 leafIndex <= 对方最后收到的序号
|
|
932
|
-
// const isMyMessage = myAddress !== null && msg.from === myAddress;
|
|
933
|
-
// const isDecryptedByRecipient =
|
|
934
|
-
// isMyMessage &&
|
|
935
|
-
// maxLastReceivedLeafIndex >= 0 &&
|
|
936
|
-
// msg.leafIndex <= maxLastReceivedLeafIndex;
|
|
937
|
-
return `
|
|
938
|
-
<div style="margin: 16px 0; padding: 16px; border: 1px solid ${borderColor}; border-radius: 8px; background: ${theme === "dark" ? "#2a2a2a" : "#f9f9f9"};">
|
|
939
|
-
<div style="display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 12px; color: #666;">
|
|
940
|
-
<span>From: ${escapeHtml(msg.from.slice(0, 6))}...${escapeHtml(msg.from.slice(-3))} To: ${escapeHtml(msg.to.slice(0, 6))}...${escapeHtml(msg.to.slice(-3))}</span>
|
|
941
|
-
<span>${new Date(msg.timestamp).toLocaleString()}</span>
|
|
942
|
-
</div>
|
|
943
|
-
${isNonText
|
|
944
|
-
? generateSaveButton(msg, index)
|
|
945
|
-
: msg.plaintext
|
|
946
|
-
? `<div style="margin: 12px 0; padding: 12px; background: ${theme === "dark" ? "#333333" : "#ffffff"}; border: 1px solid ${borderColor}; border-radius: 4px; font-size: 14px; line-height: 1.6; max-height: calc(1.6em * 6); overflow-y: auto; white-space: pre-wrap; word-break: break-word;">${escapeHtml(msg.plaintext)}</div>`
|
|
947
|
-
: ""}
|
|
948
|
-
<div style="margin: 8px 0; font-size: 11px; color: #666;">
|
|
949
|
-
<div>Merkle Root: ${escapeHtml(msg.merkleRoot)}</div>
|
|
950
|
-
${msg.lastReceivedLeafIndex !== undefined && msg.lastReceivedLeafIndex >= 0 ? `<div>Last Received Leaf Index: ${msg.lastReceivedLeafIndex}</div>` : ""}
|
|
951
|
-
${msg.arkConfirmed ? `<div>ARK Confirmed: ${escapeHtml(msg.arkConfirmed.recipient)} at ${new Date(msg.arkConfirmed.timestamp).toLocaleString()}</div>` : ""}
|
|
952
|
-
${msg.guardAddress ? `<div>Guard: ${escapeHtml(msg.guardAddress)}</div>` : ""}
|
|
953
|
-
${msg.passportAddress ? `<div>Passport: ${escapeHtml(msg.passportAddress)}</div>` : ""}
|
|
954
|
-
</div>
|
|
955
|
-
<div style="font-size: 11px; color: #888; margin-top: 8px; padding-top: 8px; border-top: 1px solid ${borderColor};">
|
|
956
|
-
<div>Leaf Index: ${msg.leafIndex}</div>
|
|
957
|
-
</div>
|
|
958
|
-
</div>
|
|
959
|
-
`;
|
|
960
|
-
})
|
|
961
|
-
.join("");
|
|
962
|
-
// 签名信息不再在 HTML 中显示(根据安全设计)
|
|
963
|
-
const signatureHtml = "";
|
|
964
|
-
const html = `<!DOCTYPE html>
|
|
965
|
-
<html lang="en">
|
|
966
|
-
<head>
|
|
967
|
-
<meta charset="UTF-8">
|
|
968
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
969
|
-
<title>${escapeHtml(title)}</title>
|
|
970
|
-
<style>
|
|
971
|
-
body {
|
|
972
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
973
|
-
max-width: 800px;
|
|
974
|
-
margin: 0 auto;
|
|
975
|
-
padding: 20px;
|
|
976
|
-
background: ${bgColor};
|
|
977
|
-
color: ${textColor};
|
|
978
|
-
}
|
|
979
|
-
.header {
|
|
980
|
-
border-bottom: 2px solid ${borderColor};
|
|
981
|
-
padding-bottom: 16px;
|
|
982
|
-
margin-bottom: 24px;
|
|
983
|
-
}
|
|
984
|
-
.meta {
|
|
985
|
-
font-size: 12px;
|
|
986
|
-
color: #666;
|
|
987
|
-
margin-top: 8px;
|
|
988
|
-
}
|
|
989
|
-
.note {
|
|
990
|
-
font-size: 11px;
|
|
991
|
-
color: #999;
|
|
992
|
-
margin-top: 12px;
|
|
993
|
-
padding: 8px;
|
|
994
|
-
background: ${theme === "dark" ? "#2a2a2a" : "#f5f5f5"};
|
|
995
|
-
border-radius: 4px;
|
|
996
|
-
font-style: italic;
|
|
997
|
-
}
|
|
998
|
-
</style>
|
|
999
|
-
</head>
|
|
1000
|
-
<body>
|
|
1001
|
-
<div class="header">
|
|
1002
|
-
<h1>Witness Timestamped Sequence</h1>
|
|
1003
|
-
<div class="note">
|
|
1004
|
-
Important Note: This page is for message browsing and verification reference only. Please verify whether the message sequence has been tampered with through the API using the WTS file.
|
|
1005
|
-
</div>
|
|
1006
|
-
<div style="font-size: 14px; color: #666; margin-top: 4px;">
|
|
1007
|
-
${new Date(meta.startTime).toLocaleDateString()} - ${new Date(meta.endTime).toLocaleDateString()}
|
|
1008
|
-
${verifyResult.valid
|
|
1009
|
-
? `<span style="color: #4caf50; margin-left: 8px;">✓ The accuracy, sequentiality, and integrity of the messages are verified without tampering</span>`
|
|
1010
|
-
: `<span style="color: #ff9800; margin-left: 8px;">⚠ ${verifyResult.error || "Hash mismatch - content has been tampered"}</span>`}
|
|
1011
|
-
</div>
|
|
1012
|
-
<div class="meta">
|
|
1013
|
-
<div>Session Participants: ${payload.session.participants.map((p) => escapeHtml(p.slice(0, 6)) + "..." + escapeHtml(p.slice(-3))).join(", ")}</div>
|
|
1014
|
-
<div>Messages: ${meta.messageCount} | Time: ${new Date(meta.startTime).toLocaleString()} - ${new Date(meta.endTime).toLocaleString()}</div>
|
|
1015
|
-
<div style="word-break: break-all;">Merkle Root: ${escapeHtml(meta.merkleRoot)}</div>
|
|
1016
|
-
${verifyResult.signatures && verifyResult.signatures.length > 0
|
|
1017
|
-
? `
|
|
1018
|
-
<div style="margin-top: 8px; padding: 8px; background: ${theme === "dark" ? "#2a3f2a" : "#e8f5e9"}; border-radius: 4px;">
|
|
1019
|
-
<div style="font-size: 11px; color: ${theme === "dark" ? "#81c784" : "#2e7d32"}; margin-bottom: 4px;">✓ Signature Verification (${verifyResult.signatures.length} signature${verifyResult.signatures.length > 1 ? "s" : ""})</div>
|
|
1020
|
-
${verifyResult.signatures
|
|
1021
|
-
.map((s, idx) => `
|
|
1022
|
-
<div style="font-size: 10px; color: ${textColor}; margin-left: 8px;">
|
|
1023
|
-
${idx + 1}. ${s.address ? escapeHtml(s.address.slice(0, 6)) + "..." + escapeHtml(s.address.slice(-3)) : "Unknown"}: ${s.valid ? '<span style="color: #4caf50;">✓ Valid</span>' : '<span style="color: #f44336;">✗ Invalid</span>'}
|
|
1024
|
-
</div>
|
|
1025
|
-
`)
|
|
1026
|
-
.join("")}
|
|
1027
|
-
</div>
|
|
1028
|
-
`
|
|
1029
|
-
: ""}
|
|
1030
|
-
</div>
|
|
1031
|
-
</div>
|
|
1032
|
-
<div class="messages">
|
|
1033
|
-
${messagesHtml}
|
|
1034
|
-
</div>
|
|
1035
|
-
${signatureHtml}
|
|
1036
|
-
</body>
|
|
1037
|
-
</html>`;
|
|
1038
|
-
return html;
|
|
906
|
+
// 使用模板生成 HTML
|
|
907
|
+
return generateHtmlPage({
|
|
908
|
+
title,
|
|
909
|
+
theme,
|
|
910
|
+
bgColor,
|
|
911
|
+
textColor,
|
|
912
|
+
borderColor,
|
|
913
|
+
meta,
|
|
914
|
+
payload,
|
|
915
|
+
verifyResult,
|
|
916
|
+
maxLastReceivedLeafIndex,
|
|
917
|
+
myAddress,
|
|
918
|
+
});
|
|
1039
919
|
}
|
|
1040
920
|
/**
|
|
1041
921
|
* 提交消息链上证明
|
|
@@ -1157,7 +1037,13 @@ export async function blacklist(request) {
|
|
|
1157
1037
|
if (!acc) {
|
|
1158
1038
|
W_ERROR(WErrors.AccountNotFound, `blacklist.account ${request.account}`);
|
|
1159
1039
|
}
|
|
1040
|
+
// 【修改】使用 MessengerManager 确保账号已就绪
|
|
1160
1041
|
const manager = getMessengerManager();
|
|
1042
|
+
if (!isInitialized) {
|
|
1043
|
+
await manager.start();
|
|
1044
|
+
}
|
|
1045
|
+
// 确保账号已就绪(自动初始化)
|
|
1046
|
+
await manager.ensureAccountReady(acc);
|
|
1161
1047
|
switch (request.op) {
|
|
1162
1048
|
case "add": {
|
|
1163
1049
|
let addresses = await GetManyAccountOrMark_Address(request.users);
|
|
@@ -1220,7 +1106,13 @@ export async function friendslist(request) {
|
|
|
1220
1106
|
if (!acc) {
|
|
1221
1107
|
W_ERROR(WErrors.AccountNotFound, `friendslist.account ${request.account}`);
|
|
1222
1108
|
}
|
|
1109
|
+
// 【修改】使用 MessengerManager 确保账号已就绪
|
|
1223
1110
|
const manager = getMessengerManager();
|
|
1111
|
+
if (!isInitialized) {
|
|
1112
|
+
await manager.start();
|
|
1113
|
+
}
|
|
1114
|
+
// 确保账号已就绪(自动初始化)
|
|
1115
|
+
await manager.ensureAccountReady(acc);
|
|
1224
1116
|
switch (request.op) {
|
|
1225
1117
|
case "add": {
|
|
1226
1118
|
let addresses = await GetManyAccountOrMark_Address(request.users);
|
|
@@ -1280,7 +1172,13 @@ export async function guardlist(request) {
|
|
|
1280
1172
|
if (!acc) {
|
|
1281
1173
|
W_ERROR(WErrors.AccountNotFound, `guardlist.account ${request.account}`);
|
|
1282
1174
|
}
|
|
1175
|
+
// 【修改】使用 MessengerManager 确保账号已就绪
|
|
1283
1176
|
const manager = getMessengerManager();
|
|
1177
|
+
if (!isInitialized) {
|
|
1178
|
+
await manager.start();
|
|
1179
|
+
}
|
|
1180
|
+
// 确保账号已就绪(自动初始化)
|
|
1181
|
+
await manager.ensureAccountReady(acc);
|
|
1284
1182
|
switch (request.op) {
|
|
1285
1183
|
case "add": {
|
|
1286
1184
|
// 按 guard 字段去重,保留最后一个
|
|
@@ -1371,4 +1269,41 @@ export async function guardlist(request) {
|
|
|
1371
1269
|
throw new MessengerError(MessengerErrorCode.INVALID_INPUT, `guardlist.unknown op: ${request}`);
|
|
1372
1270
|
}
|
|
1373
1271
|
}
|
|
1272
|
+
/**
|
|
1273
|
+
* 用户设置管理 - 统一入口
|
|
1274
|
+
* @param request 操作请求
|
|
1275
|
+
* @returns 操作结果
|
|
1276
|
+
*/
|
|
1277
|
+
export async function settings(request) {
|
|
1278
|
+
const acc = await Account.Instance().get_address(request.account);
|
|
1279
|
+
if (!acc) {
|
|
1280
|
+
W_ERROR(WErrors.AccountNotFound, `settings.account ${request.account}`);
|
|
1281
|
+
}
|
|
1282
|
+
// 使用 MessengerManager 确保账号已就绪
|
|
1283
|
+
const manager = getMessengerManager();
|
|
1284
|
+
if (!isInitialized) {
|
|
1285
|
+
await manager.start();
|
|
1286
|
+
}
|
|
1287
|
+
// 确保账号已就绪(自动初始化)
|
|
1288
|
+
await manager.ensureAccountReady(acc);
|
|
1289
|
+
switch (request.op) {
|
|
1290
|
+
case "get": {
|
|
1291
|
+
const result = await manager.getSettings(acc);
|
|
1292
|
+
return { op: "get", result };
|
|
1293
|
+
}
|
|
1294
|
+
case "set": {
|
|
1295
|
+
const settings = {};
|
|
1296
|
+
if (request.allowStrangerMessages !== undefined) {
|
|
1297
|
+
settings.allowStrangerMessages = request.allowStrangerMessages;
|
|
1298
|
+
}
|
|
1299
|
+
if (request.maxInboxSize !== undefined) {
|
|
1300
|
+
settings.maxInboxSize = request.maxInboxSize;
|
|
1301
|
+
}
|
|
1302
|
+
const result = await manager.setSettings(acc, settings);
|
|
1303
|
+
return { op: "set", result };
|
|
1304
|
+
}
|
|
1305
|
+
default:
|
|
1306
|
+
throw new MessengerError(MessengerErrorCode.INVALID_INPUT, `settings.unknown op: ${request}`);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1374
1309
|
//# sourceMappingURL=messenger-api.js.map
|