@qzhuli/qzhuli-cli 0.2.1 → 0.3.0
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/README.md +2 -1
- package/dist/cmd.js +1166 -52
- package/package.json +5 -1
- package/scripts/postinstall.mjs +226 -0
- package/skills/qzhuli-cli/SKILL.md +58 -4
package/dist/cmd.js
CHANGED
|
@@ -11133,7 +11133,7 @@ var require_protos = __commonJS({
|
|
|
11133
11133
|
|
|
11134
11134
|
// src/cmd.ts
|
|
11135
11135
|
init_cjs_shims();
|
|
11136
|
-
var
|
|
11136
|
+
var import_commander9 = require("commander");
|
|
11137
11137
|
|
|
11138
11138
|
// src/commands/auth/index.ts
|
|
11139
11139
|
init_cjs_shims();
|
|
@@ -11244,10 +11244,27 @@ var commands = {
|
|
|
11244
11244
|
desc: "Conversation operations",
|
|
11245
11245
|
listDesc: "List all conversations",
|
|
11246
11246
|
limitOption: "Max conversations to retrieve",
|
|
11247
|
+
offsetOption: "Skip first N conversations",
|
|
11247
11248
|
createDesc: "Create conversation",
|
|
11248
11249
|
createArgDesc: "User UID",
|
|
11249
11250
|
createAgentIdOption: "Agent ID",
|
|
11250
|
-
createSuccess: "Conversation created."
|
|
11251
|
+
createSuccess: "Conversation created.",
|
|
11252
|
+
profileDesc: "Get conversation details",
|
|
11253
|
+
profileArgDesc: "Conversation ID",
|
|
11254
|
+
typeOption: "Conversation type (1=private, 2=group, auto-detect if omitted)",
|
|
11255
|
+
profileSuccess: "Conversation details retrieved.",
|
|
11256
|
+
searchDesc: "Find all conversations with a user by Q\u52A9\u53F7 or UID",
|
|
11257
|
+
searchArgDesc: "Query string (default: Q\u52A9\u53F7)",
|
|
11258
|
+
searchByUid: "Search by UID",
|
|
11259
|
+
searchNotFound: "No conversations found with {name}.",
|
|
11260
|
+
searchSuccess: "Found {count} conversation(s) with {name}."
|
|
11261
|
+
},
|
|
11262
|
+
cache: {
|
|
11263
|
+
desc: "Manage local cache",
|
|
11264
|
+
syncDesc: "Sync all data to local cache",
|
|
11265
|
+
statusDesc: "Show cache status",
|
|
11266
|
+
clearDesc: "Clear cache",
|
|
11267
|
+
clearTableOption: "Clear specific cache table"
|
|
11251
11268
|
}
|
|
11252
11269
|
};
|
|
11253
11270
|
var messages = {
|
|
@@ -11374,10 +11391,27 @@ var commands2 = {
|
|
|
11374
11391
|
desc: "\u4F1A\u8BDD\u64CD\u4F5C",
|
|
11375
11392
|
listDesc: "\u5217\u51FA\u6240\u6709\u4F1A\u8BDD",
|
|
11376
11393
|
limitOption: "\u6700\u5927\u4F1A\u8BDD\u6570",
|
|
11394
|
+
offsetOption: "\u8DF3\u8FC7\u524D N \u6761\u4F1A\u8BDD",
|
|
11377
11395
|
createDesc: "\u521B\u5EFA\u4F1A\u8BDD",
|
|
11378
11396
|
createArgDesc: "\u7528\u6237UID",
|
|
11379
11397
|
createAgentIdOption: "\u52A9\u7406ID",
|
|
11380
|
-
createSuccess: "\u4F1A\u8BDD\u521B\u5EFA\u6210\u529F\u3002"
|
|
11398
|
+
createSuccess: "\u4F1A\u8BDD\u521B\u5EFA\u6210\u529F\u3002",
|
|
11399
|
+
profileDesc: "\u67E5\u8BE2\u4F1A\u8BDD\u8BE6\u60C5",
|
|
11400
|
+
profileArgDesc: "\u4F1A\u8BDDID",
|
|
11401
|
+
typeOption: "\u4F1A\u8BDD\u7C7B\u578B\uFF081=\u79C1\u804A, 2=\u7FA4\u804A\uFF0C\u4E0D\u4F20\u81EA\u52A8\u5224\u65AD\uFF09",
|
|
11402
|
+
profileSuccess: "\u5DF2\u83B7\u53D6\u4F1A\u8BDD\u8BE6\u60C5\u3002",
|
|
11403
|
+
searchDesc: "\u901A\u8FC7Q\u52A9\u53F7\u6216UID\u67E5\u627E\u4E0E\u67D0\u7528\u6237\u7684\u6240\u6709\u4F1A\u8BDD",
|
|
11404
|
+
searchArgDesc: "\u67E5\u8BE2\u5185\u5BB9\uFF08\u9ED8\u8BA4\u6309 Q\u52A9\u53F7\uFF09",
|
|
11405
|
+
searchByUid: "\u6309 UID \u641C\u7D22",
|
|
11406
|
+
searchNotFound: "\u672A\u627E\u5230\u4E0E {name} \u7684\u4F1A\u8BDD\u3002",
|
|
11407
|
+
searchSuccess: "\u627E\u5230\u4E0E {name} \u7684 {count} \u4E2A\u4F1A\u8BDD\u3002"
|
|
11408
|
+
},
|
|
11409
|
+
cache: {
|
|
11410
|
+
desc: "\u7BA1\u7406\u672C\u5730\u7F13\u5B58",
|
|
11411
|
+
syncDesc: "\u540C\u6B65\u6240\u6709\u6570\u636E\u5230\u672C\u5730\u7F13\u5B58",
|
|
11412
|
+
statusDesc: "\u663E\u793A\u7F13\u5B58\u72B6\u6001",
|
|
11413
|
+
clearDesc: "\u6E05\u9664\u7F13\u5B58",
|
|
11414
|
+
clearTableOption: "\u6E05\u9664\u6307\u5B9A\u7F13\u5B58\u8868"
|
|
11381
11415
|
}
|
|
11382
11416
|
};
|
|
11383
11417
|
var messages2 = {
|
|
@@ -12242,10 +12276,121 @@ function NewCmdAuth(factory) {
|
|
|
12242
12276
|
return cmd;
|
|
12243
12277
|
}
|
|
12244
12278
|
|
|
12245
|
-
// src/commands/
|
|
12279
|
+
// src/commands/cache/index.ts
|
|
12246
12280
|
init_cjs_shims();
|
|
12247
12281
|
var import_commander2 = require("commander");
|
|
12248
12282
|
|
|
12283
|
+
// src/commands/cache/clear.ts
|
|
12284
|
+
init_cjs_shims();
|
|
12285
|
+
async function cacheClearRun(factory, opts) {
|
|
12286
|
+
if (opts.table) {
|
|
12287
|
+
switch (opts.table) {
|
|
12288
|
+
case "conversations":
|
|
12289
|
+
factory.repos.conversation.clear();
|
|
12290
|
+
break;
|
|
12291
|
+
case "contacts":
|
|
12292
|
+
factory.repos.contact.clear();
|
|
12293
|
+
break;
|
|
12294
|
+
case "users":
|
|
12295
|
+
factory.repos.user.clear();
|
|
12296
|
+
break;
|
|
12297
|
+
case "relations":
|
|
12298
|
+
factory.repos.relation.clear();
|
|
12299
|
+
break;
|
|
12300
|
+
case "messages":
|
|
12301
|
+
factory.repos.message.clear();
|
|
12302
|
+
break;
|
|
12303
|
+
default:
|
|
12304
|
+
return {
|
|
12305
|
+
status: "error",
|
|
12306
|
+
code: "INVALID_ARGUMENT" /* INVALID_ARGUMENT */,
|
|
12307
|
+
message: t("commands.cache.unknownTable").replace("{table}", opts.table),
|
|
12308
|
+
data: null
|
|
12309
|
+
};
|
|
12310
|
+
}
|
|
12311
|
+
} else {
|
|
12312
|
+
factory.repos.conversation.clear();
|
|
12313
|
+
factory.repos.contact.clear();
|
|
12314
|
+
factory.repos.user.clear();
|
|
12315
|
+
factory.repos.relation.clear();
|
|
12316
|
+
factory.repos.message.clear();
|
|
12317
|
+
}
|
|
12318
|
+
return {
|
|
12319
|
+
status: "success",
|
|
12320
|
+
code: "CONFIG_UPDATED" /* CONFIG_UPDATED */,
|
|
12321
|
+
message: opts.table ? t("commands.cache.clearTableSuccess").replace("{table}", opts.table) : t("commands.cache.clearSuccess"),
|
|
12322
|
+
data: null
|
|
12323
|
+
};
|
|
12324
|
+
}
|
|
12325
|
+
|
|
12326
|
+
// src/commands/cache/status.ts
|
|
12327
|
+
init_cjs_shims();
|
|
12328
|
+
async function cacheStatusRun(factory) {
|
|
12329
|
+
const [convStatus, contactStatus] = await Promise.all([
|
|
12330
|
+
factory.repos.conversation.getStatus(),
|
|
12331
|
+
factory.repos.contact.getStatus()
|
|
12332
|
+
]);
|
|
12333
|
+
return {
|
|
12334
|
+
status: "success",
|
|
12335
|
+
code: "CONFIG_RETRIEVED" /* CONFIG_RETRIEVED */,
|
|
12336
|
+
message: "Cache status:",
|
|
12337
|
+
data: {
|
|
12338
|
+
conversations: {
|
|
12339
|
+
count: convStatus.count,
|
|
12340
|
+
lastSyncAt: convStatus.lastSyncAt ? new Date(convStatus.lastSyncAt).toISOString() : "never"
|
|
12341
|
+
},
|
|
12342
|
+
contacts: {
|
|
12343
|
+
count: contactStatus.count,
|
|
12344
|
+
lastSyncAt: contactStatus.lastSyncAt ? new Date(contactStatus.lastSyncAt).toISOString() : "never"
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12347
|
+
};
|
|
12348
|
+
}
|
|
12349
|
+
|
|
12350
|
+
// src/commands/cache/sync.ts
|
|
12351
|
+
init_cjs_shims();
|
|
12352
|
+
async function cacheSyncRun(factory) {
|
|
12353
|
+
try {
|
|
12354
|
+
await factory.repos.contact.sync();
|
|
12355
|
+
await factory.repos.conversation.sync();
|
|
12356
|
+
} catch (error) {
|
|
12357
|
+
return {
|
|
12358
|
+
status: "error",
|
|
12359
|
+
code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
12360
|
+
message: error instanceof Error ? error.message : "Cache sync failed.",
|
|
12361
|
+
data: null
|
|
12362
|
+
};
|
|
12363
|
+
}
|
|
12364
|
+
return {
|
|
12365
|
+
status: "success",
|
|
12366
|
+
code: "CONFIG_UPDATED" /* CONFIG_UPDATED */,
|
|
12367
|
+
message: t("commands.cache.syncSuccess"),
|
|
12368
|
+
data: null
|
|
12369
|
+
};
|
|
12370
|
+
}
|
|
12371
|
+
|
|
12372
|
+
// src/commands/cache/index.ts
|
|
12373
|
+
function NewCmdCache(factory) {
|
|
12374
|
+
const cmd = new import_commander2.Command("cache").description(t("commands.cache.desc"));
|
|
12375
|
+
cmd.command("sync").description(t("commands.cache.syncDesc")).action(async () => {
|
|
12376
|
+
const result = await cacheSyncRun(factory);
|
|
12377
|
+
handleCommand(result);
|
|
12378
|
+
});
|
|
12379
|
+
cmd.command("status").description(t("commands.cache.statusDesc")).action(async () => {
|
|
12380
|
+
const result = await cacheStatusRun(factory);
|
|
12381
|
+
handleCommand(result);
|
|
12382
|
+
});
|
|
12383
|
+
cmd.command("clear").description(t("commands.cache.clearDesc")).option("--table <table>", t("commands.cache.clearTableOption")).action(async (opts) => {
|
|
12384
|
+
const result = await cacheClearRun(factory, { table: opts.table });
|
|
12385
|
+
handleCommand(result);
|
|
12386
|
+
});
|
|
12387
|
+
return cmd;
|
|
12388
|
+
}
|
|
12389
|
+
|
|
12390
|
+
// src/commands/config/index.ts
|
|
12391
|
+
init_cjs_shims();
|
|
12392
|
+
var import_commander3 = require("commander");
|
|
12393
|
+
|
|
12249
12394
|
// src/commands/config/get.ts
|
|
12250
12395
|
init_cjs_shims();
|
|
12251
12396
|
function configGetRun(_factory, opts) {
|
|
@@ -12273,7 +12418,7 @@ function configGetRun(_factory, opts) {
|
|
|
12273
12418
|
|
|
12274
12419
|
// src/commands/config/index.ts
|
|
12275
12420
|
function NewCmdConfig(factory) {
|
|
12276
|
-
const cmd = new
|
|
12421
|
+
const cmd = new import_commander3.Command("config").description(t("commands.config.desc"));
|
|
12277
12422
|
cmd.option("--locale <language>", t("commands.config.localeOption")).option("--debug", t("commands.config.debugOn")).option("--no-debug", t("commands.config.debugOff")).action((opts) => {
|
|
12278
12423
|
const result = configGetRun(factory, {
|
|
12279
12424
|
locale: opts.locale,
|
|
@@ -12286,7 +12431,7 @@ function NewCmdConfig(factory) {
|
|
|
12286
12431
|
|
|
12287
12432
|
// src/commands/conversation/index.ts
|
|
12288
12433
|
init_cjs_shims();
|
|
12289
|
-
var
|
|
12434
|
+
var import_commander4 = require("commander");
|
|
12290
12435
|
|
|
12291
12436
|
// src/commands/conversation/create.ts
|
|
12292
12437
|
init_cjs_shims();
|
|
@@ -12311,8 +12456,11 @@ async function conversationCreateRun(factory, opts) {
|
|
|
12311
12456
|
// src/commands/conversation/list.ts
|
|
12312
12457
|
init_cjs_shims();
|
|
12313
12458
|
async function conversationListRun(factory, opts) {
|
|
12314
|
-
const result = await factory.
|
|
12315
|
-
|
|
12459
|
+
const result = await factory.repos.conversation.queryAll({
|
|
12460
|
+
limit: opts.limit,
|
|
12461
|
+
offset: opts.offset
|
|
12462
|
+
});
|
|
12463
|
+
if (!result.ok && result.code === "AUTH_FAILED" /* AUTH_FAILED */) {
|
|
12316
12464
|
return {
|
|
12317
12465
|
status: "error",
|
|
12318
12466
|
code: "AUTH_FAILED" /* AUTH_FAILED */,
|
|
@@ -12320,15 +12468,150 @@ async function conversationListRun(factory, opts) {
|
|
|
12320
12468
|
data: null
|
|
12321
12469
|
};
|
|
12322
12470
|
}
|
|
12323
|
-
return
|
|
12471
|
+
return {
|
|
12472
|
+
status: result.ok ? "success" : "error",
|
|
12473
|
+
code: result.ok ? "CONVERSATION_LIST" /* CONVERSATION_LIST */ : result.code,
|
|
12474
|
+
message: result.ok ? `Conversations (${result.data.length}):` : result.message,
|
|
12475
|
+
data: result.ok ? { conversations: result.data } : null
|
|
12476
|
+
};
|
|
12477
|
+
}
|
|
12478
|
+
|
|
12479
|
+
// src/commands/conversation/profile.ts
|
|
12480
|
+
init_cjs_shims();
|
|
12481
|
+
async function conversationProfileRun(factory, opts) {
|
|
12482
|
+
const result = await factory.repos.conversation.getProfile(
|
|
12483
|
+
opts.conversationId,
|
|
12484
|
+
opts.conversationType
|
|
12485
|
+
);
|
|
12486
|
+
if (!result.ok) {
|
|
12487
|
+
return {
|
|
12488
|
+
status: "error",
|
|
12489
|
+
code: result.code,
|
|
12490
|
+
message: result.code === "AUTH_FAILED" /* AUTH_FAILED */ ? t("auth.notAuthenticated") : result.message,
|
|
12491
|
+
data: null
|
|
12492
|
+
};
|
|
12493
|
+
}
|
|
12494
|
+
return {
|
|
12495
|
+
status: "success",
|
|
12496
|
+
code: "CONVERSATION_LIST" /* CONVERSATION_LIST */,
|
|
12497
|
+
message: t("commands.conversation.profileSuccess"),
|
|
12498
|
+
data: result.data
|
|
12499
|
+
};
|
|
12500
|
+
}
|
|
12501
|
+
|
|
12502
|
+
// src/commands/conversation/search.ts
|
|
12503
|
+
init_cjs_shims();
|
|
12504
|
+
function convertUser(raw) {
|
|
12505
|
+
const user = {
|
|
12506
|
+
id: raw.id,
|
|
12507
|
+
cid: raw.cid,
|
|
12508
|
+
uid: raw.uid,
|
|
12509
|
+
nickname: raw.nickname,
|
|
12510
|
+
avatar: raw.avatar,
|
|
12511
|
+
status: raw.status
|
|
12512
|
+
};
|
|
12513
|
+
if (raw.link_name) user.linkName = raw.link_name;
|
|
12514
|
+
if (raw.member_delete_time) user.memberDeleteTime = raw.member_delete_time;
|
|
12515
|
+
if (raw.agent) {
|
|
12516
|
+
const a = raw.agent;
|
|
12517
|
+
const agentVoice = a.voice ? {
|
|
12518
|
+
code: a.voice.code,
|
|
12519
|
+
...a.voice.voice_name ? { voiceName: a.voice.voice_name } : {},
|
|
12520
|
+
...a.voice.file_link ? { fileLink: a.voice.file_link } : {},
|
|
12521
|
+
...a.voice.text ? { text: a.voice.text } : {}
|
|
12522
|
+
} : void 0;
|
|
12523
|
+
user.agent = {
|
|
12524
|
+
id: a.id,
|
|
12525
|
+
title: a.title,
|
|
12526
|
+
avatar: a.avatar,
|
|
12527
|
+
packageIcon: a.package_icon,
|
|
12528
|
+
packageLevel: a.package_level,
|
|
12529
|
+
...a.background !== void 0 ? { background: a.background } : {},
|
|
12530
|
+
...a.agent_type !== void 0 ? { agentType: a.agent_type } : {},
|
|
12531
|
+
...agentVoice ? { voice: agentVoice } : {}
|
|
12532
|
+
};
|
|
12533
|
+
}
|
|
12534
|
+
return user;
|
|
12535
|
+
}
|
|
12536
|
+
function convertVisitor(raw) {
|
|
12537
|
+
return { id: raw.id, cid: raw.cid };
|
|
12538
|
+
}
|
|
12539
|
+
function profileToItem(profile) {
|
|
12540
|
+
const convData = profile.conversation;
|
|
12541
|
+
return {
|
|
12542
|
+
conversationId: convData.conversation_id,
|
|
12543
|
+
blackStatus: convData.black_status,
|
|
12544
|
+
isGroup: convData.is_group,
|
|
12545
|
+
name: convData.name,
|
|
12546
|
+
avatar: convData.avatar,
|
|
12547
|
+
sourceType: convData.source_type,
|
|
12548
|
+
classesId: convData.classes_id,
|
|
12549
|
+
classesName: convData.classes_name,
|
|
12550
|
+
teamId: convData.team_id,
|
|
12551
|
+
teamName: convData.team_name,
|
|
12552
|
+
users: profile.users.map(convertUser),
|
|
12553
|
+
visitors: profile.visitors.map(convertVisitor)
|
|
12554
|
+
};
|
|
12555
|
+
}
|
|
12556
|
+
async function conversationSearchRun(factory, opts) {
|
|
12557
|
+
const userResult = opts.byUid ? await factory.repos.user.findUserById(opts.query) : await factory.repos.user.searchUserById(opts.query);
|
|
12558
|
+
if (!userResult.ok) {
|
|
12559
|
+
return {
|
|
12560
|
+
status: "error",
|
|
12561
|
+
code: userResult.code,
|
|
12562
|
+
message: userResult.code === "AUTH_FAILED" /* AUTH_FAILED */ ? t("auth.notAuthenticated") : userResult.message,
|
|
12563
|
+
data: null
|
|
12564
|
+
};
|
|
12565
|
+
}
|
|
12566
|
+
if (!userResult.data) {
|
|
12567
|
+
return {
|
|
12568
|
+
status: "error",
|
|
12569
|
+
code: "USER_NOT_FOUND_NEEDS_RESOLUTION" /* USER_NOT_FOUND_NEEDS_RESOLUTION */,
|
|
12570
|
+
message: t("messages.notFound"),
|
|
12571
|
+
data: null
|
|
12572
|
+
};
|
|
12573
|
+
}
|
|
12574
|
+
const targetId = userResult.data.id;
|
|
12575
|
+
const targetUid = userResult.data.uid;
|
|
12576
|
+
const nickname = userResult.data.nickname;
|
|
12577
|
+
const profilesResult = await factory.repos.conversation.searchByUid(targetUid);
|
|
12578
|
+
if (!profilesResult.ok || profilesResult.data.length === 0) {
|
|
12579
|
+
return {
|
|
12580
|
+
status: "success",
|
|
12581
|
+
code: "NOT_FOUND" /* NOT_FOUND */,
|
|
12582
|
+
message: t("commands.conversation.searchNotFound").replace("{name}", nickname),
|
|
12583
|
+
data: null
|
|
12584
|
+
};
|
|
12585
|
+
}
|
|
12586
|
+
const results = profilesResult.data.map(profileToItem);
|
|
12587
|
+
const result = {
|
|
12588
|
+
id: targetId,
|
|
12589
|
+
uid: targetUid,
|
|
12590
|
+
nickname,
|
|
12591
|
+
conversations: results
|
|
12592
|
+
};
|
|
12593
|
+
return {
|
|
12594
|
+
status: "success",
|
|
12595
|
+
code: "CONVERSATION_LIST" /* CONVERSATION_LIST */,
|
|
12596
|
+
message: t("commands.conversation.searchSuccess").replace("{name}", nickname).replace("{count}", String(results.length)),
|
|
12597
|
+
data: result
|
|
12598
|
+
};
|
|
12324
12599
|
}
|
|
12325
12600
|
|
|
12326
12601
|
// src/commands/conversation/index.ts
|
|
12327
12602
|
function NewCmdConversation(factory) {
|
|
12328
|
-
const cmd = new
|
|
12329
|
-
cmd.command("list").description(t("commands.conversation.listDesc")).option("--limit <n>", t("commands.conversation.limitOption"), "50").action(async (options3) => {
|
|
12603
|
+
const cmd = new import_commander4.Command("conversation").description(t("commands.conversation.desc"));
|
|
12604
|
+
cmd.command("list").description(t("commands.conversation.listDesc")).option("--limit <n>", t("commands.conversation.limitOption"), "50").option("--offset <n>", t("commands.conversation.offsetOption"), "0").action(async (options3) => {
|
|
12330
12605
|
const result = await conversationListRun(factory, {
|
|
12331
|
-
limit: parseInt(options3.limit, 10)
|
|
12606
|
+
limit: parseInt(options3.limit, 10),
|
|
12607
|
+
offset: parseInt(options3.offset, 10)
|
|
12608
|
+
});
|
|
12609
|
+
handleCommand(result);
|
|
12610
|
+
});
|
|
12611
|
+
cmd.command("profile").description(t("commands.conversation.profileDesc")).argument("<id>", t("commands.conversation.profileArgDesc")).option("--type <n>", t("commands.conversation.typeOption")).action(async (id, opts) => {
|
|
12612
|
+
const result = await conversationProfileRun(factory, {
|
|
12613
|
+
conversationId: id,
|
|
12614
|
+
conversationType: opts.type ? parseInt(opts.type, 10) : void 0
|
|
12332
12615
|
});
|
|
12333
12616
|
handleCommand(result);
|
|
12334
12617
|
});
|
|
@@ -12336,17 +12619,21 @@ function NewCmdConversation(factory) {
|
|
|
12336
12619
|
const result = await conversationCreateRun(factory, { uid, agentId: opts.agentId });
|
|
12337
12620
|
handleCommand(result);
|
|
12338
12621
|
});
|
|
12622
|
+
cmd.command("search").description(t("commands.conversation.searchDesc")).argument("<query>", t("commands.conversation.searchArgDesc")).option("--uid", t("commands.conversation.searchByUid")).action(async (query, opts) => {
|
|
12623
|
+
const result = await conversationSearchRun(factory, { query, byUid: opts.uid });
|
|
12624
|
+
handleCommand(result);
|
|
12625
|
+
});
|
|
12339
12626
|
return cmd;
|
|
12340
12627
|
}
|
|
12341
12628
|
|
|
12342
12629
|
// src/commands/friend/index.ts
|
|
12343
12630
|
init_cjs_shims();
|
|
12344
|
-
var
|
|
12631
|
+
var import_commander5 = require("commander");
|
|
12345
12632
|
|
|
12346
12633
|
// src/commands/friend/list.ts
|
|
12347
12634
|
init_cjs_shims();
|
|
12348
12635
|
async function friendListRun(factory) {
|
|
12349
|
-
const result = await factory.
|
|
12636
|
+
const result = await factory.repos.contact.getLinksContacts();
|
|
12350
12637
|
if (!result.ok) {
|
|
12351
12638
|
return {
|
|
12352
12639
|
status: "error",
|
|
@@ -12376,7 +12663,7 @@ async function friendListRun(factory) {
|
|
|
12376
12663
|
// src/commands/friend/profile.ts
|
|
12377
12664
|
init_cjs_shims();
|
|
12378
12665
|
async function friendProfileRun(factory, opts) {
|
|
12379
|
-
const listResult = await factory.
|
|
12666
|
+
const listResult = await factory.repos.contact.getLinksContacts();
|
|
12380
12667
|
if (!listResult.ok) {
|
|
12381
12668
|
return {
|
|
12382
12669
|
status: "error",
|
|
@@ -12461,7 +12748,7 @@ async function friendProfileRun(factory, opts) {
|
|
|
12461
12748
|
}
|
|
12462
12749
|
};
|
|
12463
12750
|
}
|
|
12464
|
-
const profileResult = await factory.
|
|
12751
|
+
const profileResult = await factory.repos.user.getUserAgentProfile(match.uid);
|
|
12465
12752
|
if (!profileResult.ok) {
|
|
12466
12753
|
return {
|
|
12467
12754
|
status: "error",
|
|
@@ -12494,7 +12781,7 @@ async function friendProfileRun(factory, opts) {
|
|
|
12494
12781
|
|
|
12495
12782
|
// src/commands/friend/index.ts
|
|
12496
12783
|
function NewCmdFriend(factory) {
|
|
12497
|
-
const cmd = new
|
|
12784
|
+
const cmd = new import_commander5.Command("friend").description(t("commands.friend.desc"));
|
|
12498
12785
|
cmd.command("list").description(t("commands.friend.listDesc")).action(async () => {
|
|
12499
12786
|
const result = await friendListRun(factory);
|
|
12500
12787
|
handleCommand(result);
|
|
@@ -12512,7 +12799,7 @@ function NewCmdFriend(factory) {
|
|
|
12512
12799
|
|
|
12513
12800
|
// src/commands/message/index.ts
|
|
12514
12801
|
init_cjs_shims();
|
|
12515
|
-
var
|
|
12802
|
+
var import_commander6 = require("commander");
|
|
12516
12803
|
|
|
12517
12804
|
// src/commands/message/history.ts
|
|
12518
12805
|
init_cjs_shims();
|
|
@@ -12522,8 +12809,8 @@ async function messageHistoryRun(factory, opts) {
|
|
|
12522
12809
|
direction: opts.direction,
|
|
12523
12810
|
limit: opts.limit
|
|
12524
12811
|
};
|
|
12525
|
-
const result = await factory.
|
|
12526
|
-
if (result.
|
|
12812
|
+
const result = await factory.repos.message.pullMessages(opts.conversationId, pullOpts);
|
|
12813
|
+
if (!result.ok && result.code === "AUTH_FAILED" /* AUTH_FAILED */) {
|
|
12527
12814
|
return {
|
|
12528
12815
|
status: "error",
|
|
12529
12816
|
code: "AUTH_FAILED" /* AUTH_FAILED */,
|
|
@@ -12531,7 +12818,12 @@ async function messageHistoryRun(factory, opts) {
|
|
|
12531
12818
|
data: null
|
|
12532
12819
|
};
|
|
12533
12820
|
}
|
|
12534
|
-
return
|
|
12821
|
+
return {
|
|
12822
|
+
status: result.ok ? "success" : "error",
|
|
12823
|
+
code: result.ok ? "MESSAGE_HISTORY" /* MESSAGE_HISTORY */ : result.code,
|
|
12824
|
+
message: result.ok ? `Messages (${result.data.messages.length}):` : result.message,
|
|
12825
|
+
data: result.ok ? result.data : null
|
|
12826
|
+
};
|
|
12535
12827
|
}
|
|
12536
12828
|
|
|
12537
12829
|
// src/commands/message/send.ts
|
|
@@ -12555,7 +12847,7 @@ async function messageSendRun(factory, opts) {
|
|
|
12555
12847
|
|
|
12556
12848
|
// src/commands/message/index.ts
|
|
12557
12849
|
function NewCmdMessage(factory) {
|
|
12558
|
-
const cmd = new
|
|
12850
|
+
const cmd = new import_commander6.Command("message").description(t("commands.message.desc"));
|
|
12559
12851
|
cmd.command("send <conversation-id> <target-cid> <content>").description(t("commands.message.sendDesc")).action(async (conversationId, targetCid, content) => {
|
|
12560
12852
|
const result = await messageSendRun(factory, { conversationId, targetCid, content });
|
|
12561
12853
|
handleCommand(result);
|
|
@@ -12574,12 +12866,12 @@ function NewCmdMessage(factory) {
|
|
|
12574
12866
|
|
|
12575
12867
|
// src/commands/relation/index.ts
|
|
12576
12868
|
init_cjs_shims();
|
|
12577
|
-
var
|
|
12869
|
+
var import_commander7 = require("commander");
|
|
12578
12870
|
|
|
12579
12871
|
// src/commands/relation/get.ts
|
|
12580
12872
|
init_cjs_shims();
|
|
12581
12873
|
async function relationGetRun(factory, opts) {
|
|
12582
|
-
const result = await factory.
|
|
12874
|
+
const result = await factory.repos.relation.getLinkNameType(opts.friendUid);
|
|
12583
12875
|
if (!result.ok) {
|
|
12584
12876
|
return {
|
|
12585
12877
|
status: "error",
|
|
@@ -12647,7 +12939,7 @@ async function relationSetRun(factory, opts) {
|
|
|
12647
12939
|
|
|
12648
12940
|
// src/commands/relation/index.ts
|
|
12649
12941
|
function NewCmdRelation(factory) {
|
|
12650
|
-
const cmd = new
|
|
12942
|
+
const cmd = new import_commander7.Command("relation").description(t("commands.relation.desc"));
|
|
12651
12943
|
cmd.command("get <uid>").description(t("commands.relation.getDesc")).action(async (uid) => {
|
|
12652
12944
|
const result = await relationGetRun(factory, { friendUid: uid });
|
|
12653
12945
|
handleCommand(result);
|
|
@@ -12665,7 +12957,7 @@ function NewCmdRelation(factory) {
|
|
|
12665
12957
|
|
|
12666
12958
|
// src/commands/user/index.ts
|
|
12667
12959
|
init_cjs_shims();
|
|
12668
|
-
var
|
|
12960
|
+
var import_commander8 = require("commander");
|
|
12669
12961
|
|
|
12670
12962
|
// src/commands/user/add.ts
|
|
12671
12963
|
init_cjs_shims();
|
|
@@ -12720,7 +13012,7 @@ async function userAddRun(factory, opts) {
|
|
|
12720
13012
|
// src/commands/user/search.ts
|
|
12721
13013
|
init_cjs_shims();
|
|
12722
13014
|
async function userSearchRun(factory, opts) {
|
|
12723
|
-
const result = opts.byUid ? await factory.
|
|
13015
|
+
const result = opts.byUid ? await factory.repos.user.findUserById(opts.query) : await factory.repos.user.searchUserById(opts.query);
|
|
12724
13016
|
if (!result.ok) {
|
|
12725
13017
|
return {
|
|
12726
13018
|
status: "error",
|
|
@@ -12748,7 +13040,7 @@ async function userSearchRun(factory, opts) {
|
|
|
12748
13040
|
|
|
12749
13041
|
// src/commands/user/index.ts
|
|
12750
13042
|
function NewCmdUser(factory) {
|
|
12751
|
-
const cmd = new
|
|
13043
|
+
const cmd = new import_commander8.Command("user").description(t("commands.user.desc"));
|
|
12752
13044
|
cmd.command("search").description(t("commands.user.searchDesc")).argument("<query>", t("commands.user.searchArgDesc")).option("--uid", t("commands.user.searchByUid")).action(async (query, opts) => {
|
|
12753
13045
|
const result = await userSearchRun(factory, { query, byUid: opts.uid });
|
|
12754
13046
|
handleCommand(result);
|
|
@@ -12887,6 +13179,23 @@ var ApiGateway = class {
|
|
|
12887
13179
|
friend_agent_id: friendAgentId
|
|
12888
13180
|
});
|
|
12889
13181
|
}
|
|
13182
|
+
/** POST /user/get_profile_by_conversation_id — auth required */
|
|
13183
|
+
getProfileByConversationId(conversationId, conversationType) {
|
|
13184
|
+
const auth3 = this.requireAuth();
|
|
13185
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13186
|
+
const params = {
|
|
13187
|
+
uid: auth3.data,
|
|
13188
|
+
conversation_id: conversationId
|
|
13189
|
+
};
|
|
13190
|
+
if (conversationType !== void 0) {
|
|
13191
|
+
params.conversation_type = conversationType;
|
|
13192
|
+
}
|
|
13193
|
+
return this.client.performRequest(
|
|
13194
|
+
"/user/get_profile_by_conversation_id",
|
|
13195
|
+
"POST",
|
|
13196
|
+
params
|
|
13197
|
+
);
|
|
13198
|
+
}
|
|
12890
13199
|
};
|
|
12891
13200
|
|
|
12892
13201
|
// src/internal/api-client.ts
|
|
@@ -13099,6 +13408,33 @@ var IMClient = class {
|
|
|
13099
13408
|
console.log("[DEBUG]", ...args);
|
|
13100
13409
|
}
|
|
13101
13410
|
}
|
|
13411
|
+
/** Convert a protobuf Long to a readable string. */
|
|
13412
|
+
longStr(v) {
|
|
13413
|
+
if (!v) return "0";
|
|
13414
|
+
if (typeof v === "object" && "low" in v && "high" in v) {
|
|
13415
|
+
return v.toString();
|
|
13416
|
+
}
|
|
13417
|
+
return String(v);
|
|
13418
|
+
}
|
|
13419
|
+
/** Map operation code to a human-readable name. */
|
|
13420
|
+
opName(code) {
|
|
13421
|
+
const map = {
|
|
13422
|
+
1: "KeepAlive",
|
|
13423
|
+
1e3: "ClientOpen",
|
|
13424
|
+
1001: "ClientClose",
|
|
13425
|
+
1003: "ConversationQuery",
|
|
13426
|
+
2e3: "ConversationMessageSend",
|
|
13427
|
+
2001: "ConversationMessagePull",
|
|
13428
|
+
2005: "ConversationMessagePush",
|
|
13429
|
+
3e3: "Ack"
|
|
13430
|
+
};
|
|
13431
|
+
return map[code] ?? `Op(${code})`;
|
|
13432
|
+
}
|
|
13433
|
+
/** Format byte size for display. */
|
|
13434
|
+
size(n) {
|
|
13435
|
+
if (n < 1024) return `${n}B`;
|
|
13436
|
+
return `${(n / 1024).toFixed(1)}KB`;
|
|
13437
|
+
}
|
|
13102
13438
|
isConnected() {
|
|
13103
13439
|
return this.connected;
|
|
13104
13440
|
}
|
|
@@ -13106,9 +13442,11 @@ var IMClient = class {
|
|
|
13106
13442
|
async connect() {
|
|
13107
13443
|
return new Promise((resolve2, reject) => {
|
|
13108
13444
|
const wsUrl = this.buildWsUrl();
|
|
13445
|
+
this.debug(`\u2192 CONNECT ${wsUrl}, cid=${this.config.cid}`);
|
|
13109
13446
|
this.ws = new import_ws.default(wsUrl);
|
|
13110
13447
|
this.ws.binaryType = "arraybuffer";
|
|
13111
13448
|
this.ws.on("open", () => {
|
|
13449
|
+
this.debug(" \u2713 Connected");
|
|
13112
13450
|
clearTimeout(timeout);
|
|
13113
13451
|
this.connected = true;
|
|
13114
13452
|
this.onEvent({ type: "connected" });
|
|
@@ -13116,13 +13454,17 @@ var IMClient = class {
|
|
|
13116
13454
|
resolve2();
|
|
13117
13455
|
});
|
|
13118
13456
|
this.ws.on("message", (data) => {
|
|
13119
|
-
|
|
13457
|
+
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
13458
|
+
this.debug(` \u2190 ${this.size(buf.length)} received`);
|
|
13459
|
+
this.handleResponse(buf);
|
|
13120
13460
|
});
|
|
13121
|
-
this.ws.on("close", (
|
|
13461
|
+
this.ws.on("close", (code, reason) => {
|
|
13462
|
+
this.debug(` \u2717 Closed (code: ${code}, reason: ${reason.toString()})`);
|
|
13122
13463
|
this.connected = false;
|
|
13123
13464
|
this.onEvent({ type: "disconnected", reason: reason.toString() });
|
|
13124
13465
|
});
|
|
13125
13466
|
this.ws.on("error", (err) => {
|
|
13467
|
+
this.debug(` \u2717 Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
13126
13468
|
this.onEvent({ type: "error", error: err });
|
|
13127
13469
|
reject(err);
|
|
13128
13470
|
});
|
|
@@ -13135,6 +13477,7 @@ var IMClient = class {
|
|
|
13135
13477
|
/** Close the IM connection. */
|
|
13136
13478
|
close() {
|
|
13137
13479
|
if (this.ws) {
|
|
13480
|
+
this.debug(`\u2192 CLOSE`);
|
|
13138
13481
|
this.ws.close();
|
|
13139
13482
|
this.ws = null;
|
|
13140
13483
|
this.connected = false;
|
|
@@ -13174,13 +13517,21 @@ var IMClient = class {
|
|
|
13174
13517
|
const handler = (ack) => {
|
|
13175
13518
|
clearTimeout(timeout);
|
|
13176
13519
|
this.ws?.off("message", onMessage);
|
|
13520
|
+
this.debug(
|
|
13521
|
+
` \u2713 Ack, msg=${ack.messageId}, blocked=${ack.blocked}, moderated=${ack.moderated}`
|
|
13522
|
+
);
|
|
13177
13523
|
resolve2(ack);
|
|
13178
13524
|
};
|
|
13179
13525
|
const onMessage = (data2) => {
|
|
13180
13526
|
const response = this.root.IMResponse.decode(new Uint8Array(data2));
|
|
13181
|
-
|
|
13182
|
-
|
|
13527
|
+
const op = response.operation;
|
|
13528
|
+
const blobLen = response.blob?.length ?? 0;
|
|
13529
|
+
if (op === this.root.IMOperation.Ack || op === this.root.IMOperation.ConversationMessageSend) {
|
|
13530
|
+
if (blobLen > 0) {
|
|
13183
13531
|
const ack = this.root.MsgAck.decode(new Uint8Array(response.blob));
|
|
13532
|
+
this.debug(
|
|
13533
|
+
` \u2190 ${this.opName(op)} ${this.size(blobLen)}, id=${this.longStr(ack.id)}, blocked=${ack.blocked}, ackTs=${this.longStr(ack.ackTs)}`
|
|
13534
|
+
);
|
|
13184
13535
|
handler({
|
|
13185
13536
|
messageId: String(ack.id),
|
|
13186
13537
|
conversationId: ack.conversationId,
|
|
@@ -13192,14 +13543,15 @@ var IMClient = class {
|
|
|
13192
13543
|
ackTs: ack.ackTs ? Number(ack.ackTs) : 0
|
|
13193
13544
|
});
|
|
13194
13545
|
} else {
|
|
13195
|
-
this.debug(
|
|
13546
|
+
this.debug(` \u2190 ${this.opName(op)} (empty blob, ignored)`);
|
|
13196
13547
|
}
|
|
13197
13548
|
} else {
|
|
13198
|
-
this.debug(
|
|
13549
|
+
this.debug(` \u2190 ${this.opName(op)} ${this.size(blobLen)} (ignored, not ack)`);
|
|
13199
13550
|
}
|
|
13200
13551
|
};
|
|
13201
13552
|
this.ws?.on("message", onMessage);
|
|
13202
13553
|
const data = this.root.IMRequest.encode(request).finish();
|
|
13554
|
+
this.debug(`\u2192 ${this.opName(2e3)} ${this.size(data.length)} to: ${targetCid}`);
|
|
13203
13555
|
this.ws?.send(Buffer.from(data));
|
|
13204
13556
|
});
|
|
13205
13557
|
}
|
|
@@ -13230,6 +13582,7 @@ var IMClient = class {
|
|
|
13230
13582
|
clearTimeout(timeout);
|
|
13231
13583
|
this.ws?.off("message", onMessage);
|
|
13232
13584
|
if (!response.blob || response.blob.length === 0) {
|
|
13585
|
+
this.debug(` \u2190 ${this.opName(2001)} (empty, no messages)`);
|
|
13233
13586
|
resolve2({
|
|
13234
13587
|
messages: [],
|
|
13235
13588
|
finished: true,
|
|
@@ -13244,6 +13597,9 @@ var IMClient = class {
|
|
|
13244
13597
|
return;
|
|
13245
13598
|
}
|
|
13246
13599
|
const msgList = this.root.MsgList.decode(new Uint8Array(response.blob));
|
|
13600
|
+
this.debug(
|
|
13601
|
+
` \u2190 ${this.opName(2001)}, ${msgList.msgs?.length ?? 0} msgs, finished=${msgList.finished}, lastId=${this.longStr(msgList.lastId)}`
|
|
13602
|
+
);
|
|
13247
13603
|
const debug = this.debugEnabled;
|
|
13248
13604
|
const messages3 = [];
|
|
13249
13605
|
for (const msg of msgList.msgs) {
|
|
@@ -13271,6 +13627,9 @@ var IMClient = class {
|
|
|
13271
13627
|
timestamp: msg.ackTs ? Number(msg.ackTs) : msg.jetTs ? Number(msg.jetTs) : 0
|
|
13272
13628
|
};
|
|
13273
13629
|
if (debug) {
|
|
13630
|
+
this.debug(
|
|
13631
|
+
` #${pulledMsg.id} ${pulledMsg.type} ${pulledMsg.role} from ${pulledMsg.senderCid} (${pulledMsg.content.length} chars, ${pulledMsg.extraContents.length} extras)`
|
|
13632
|
+
);
|
|
13274
13633
|
if (msg.blob && msg.blob.length > 0)
|
|
13275
13634
|
pulledMsg.rawBlob = Buffer.from(msg.blob).toString("base64");
|
|
13276
13635
|
if (msg.blobExtra && msg.blobExtra.length > 0)
|
|
@@ -13278,6 +13637,7 @@ var IMClient = class {
|
|
|
13278
13637
|
}
|
|
13279
13638
|
messages3.push(pulledMsg);
|
|
13280
13639
|
}
|
|
13640
|
+
this.debug(` \u2713 ${messages3.length} messages resolved`);
|
|
13281
13641
|
resolve2({
|
|
13282
13642
|
messages: messages3,
|
|
13283
13643
|
finished: msgList.finished,
|
|
@@ -13301,13 +13661,11 @@ var IMClient = class {
|
|
|
13301
13661
|
});
|
|
13302
13662
|
}
|
|
13303
13663
|
/** Query all conversations for the user. */
|
|
13304
|
-
async queryConversations(
|
|
13664
|
+
async queryConversations() {
|
|
13305
13665
|
if (!this.ws || !this.connected) {
|
|
13306
13666
|
throw new Error("Not connected to IM server");
|
|
13307
13667
|
}
|
|
13308
|
-
const queryBlob = this.root.ConversationQueryParams.create({
|
|
13309
|
-
limit: options3?.limit ?? 0
|
|
13310
|
-
});
|
|
13668
|
+
const queryBlob = this.root.ConversationQueryParams.create({});
|
|
13311
13669
|
const request = this.root.IMRequest.create({
|
|
13312
13670
|
operation: this.root.IMOperation.ConversationQuery,
|
|
13313
13671
|
cid: this.config.cid,
|
|
@@ -13324,6 +13682,7 @@ var IMClient = class {
|
|
|
13324
13682
|
clearTimeout(timeout);
|
|
13325
13683
|
this.ws?.off("message", onMessage);
|
|
13326
13684
|
if (!response.blob || response.blob.length === 0) {
|
|
13685
|
+
this.debug(` \u2190 ${this.opName(1003)} (empty, no conversations)`);
|
|
13327
13686
|
resolve2({
|
|
13328
13687
|
conversations: [],
|
|
13329
13688
|
limit: 0,
|
|
@@ -13334,6 +13693,7 @@ var IMClient = class {
|
|
|
13334
13693
|
return;
|
|
13335
13694
|
}
|
|
13336
13695
|
const list = this.root.ConversationList.decode(new Uint8Array(response.blob));
|
|
13696
|
+
this.debug(` \u2190 ${this.opName(1003)} ${list.convs?.length ?? 0} conversations`);
|
|
13337
13697
|
const debug = this.debugEnabled;
|
|
13338
13698
|
const conversations3 = [];
|
|
13339
13699
|
for (const conv of list.convs ?? []) {
|
|
@@ -13394,13 +13754,18 @@ var IMClient = class {
|
|
|
13394
13754
|
ts: Date.now()
|
|
13395
13755
|
});
|
|
13396
13756
|
const data = this.root.IMRequest.encode(request).finish();
|
|
13757
|
+
this.debug(`\u2192 ${this.opName(1e3)} ${this.size(data.length)}, cid=${this.config.cid}`);
|
|
13397
13758
|
this.ws.send(Buffer.from(data));
|
|
13398
13759
|
}
|
|
13399
13760
|
handleResponse(data) {
|
|
13400
13761
|
try {
|
|
13401
13762
|
const response = this.root.IMResponse.decode(new Uint8Array(data));
|
|
13402
|
-
|
|
13763
|
+
const op = response.operation;
|
|
13764
|
+
if (op === this.root.IMOperation.ConversationMessagePush) {
|
|
13403
13765
|
const msgList = this.root.MsgList.decode(new Uint8Array(response.blob));
|
|
13766
|
+
this.debug(
|
|
13767
|
+
` \u2190 ${this.opName(op)}, ${msgList.msgs?.length ?? 0} msgs, conv=${msgList.convId}`
|
|
13768
|
+
);
|
|
13404
13769
|
const debug = this.debugEnabled;
|
|
13405
13770
|
for (const msg of msgList.msgs) {
|
|
13406
13771
|
const extraContents = (msg.extraContents ?? []).map((ec) => {
|
|
@@ -13427,6 +13792,9 @@ var IMClient = class {
|
|
|
13427
13792
|
timestamp: msg.ackTs ? Number(msg.ackTs) : msg.jetTs ? Number(msg.jetTs) : 0
|
|
13428
13793
|
};
|
|
13429
13794
|
if (debug) {
|
|
13795
|
+
this.debug(
|
|
13796
|
+
` #${pulledMsg.id} ${pulledMsg.type} ${pulledMsg.role} from ${pulledMsg.senderCid} (${pulledMsg.content.length} chars)`
|
|
13797
|
+
);
|
|
13430
13798
|
if (msg.blob && msg.blob.length > 0)
|
|
13431
13799
|
pulledMsg.rawBlob = Buffer.from(msg.blob).toString("base64");
|
|
13432
13800
|
if (msg.blobExtra && msg.blobExtra.length > 0)
|
|
@@ -13437,6 +13805,11 @@ var IMClient = class {
|
|
|
13437
13805
|
message: pulledMsg
|
|
13438
13806
|
});
|
|
13439
13807
|
}
|
|
13808
|
+
} else {
|
|
13809
|
+
const blobLen = response.blob?.length ?? 0;
|
|
13810
|
+
this.debug(
|
|
13811
|
+
` \u2190 ${this.opName(op)} ${this.size(blobLen)} (unhandled, no handler registered)`
|
|
13812
|
+
);
|
|
13440
13813
|
}
|
|
13441
13814
|
} catch (err) {
|
|
13442
13815
|
this.debug("handleResponse decode error:", err instanceof Error ? err.message : String(err));
|
|
@@ -13451,20 +13824,28 @@ var IMGateway = class {
|
|
|
13451
13824
|
}
|
|
13452
13825
|
config;
|
|
13453
13826
|
dryRun = false;
|
|
13827
|
+
/** Log debug output when debug mode is enabled. */
|
|
13828
|
+
debug(...args) {
|
|
13829
|
+
if (this.config.debugEnabled) {
|
|
13830
|
+
console.log("[DEBUG]", ...args);
|
|
13831
|
+
}
|
|
13832
|
+
}
|
|
13454
13833
|
setDryRun(enabled) {
|
|
13455
13834
|
this.dryRun = enabled;
|
|
13456
13835
|
}
|
|
13457
13836
|
/** Execute a function with an IM connection, ensuring connect/close lifecycle. */
|
|
13458
13837
|
async withConnection(fn) {
|
|
13838
|
+
this.debug("\u2500\u2500 new connection \u2500\u2500");
|
|
13459
13839
|
const client = new IMClient(this.config, (event) => {
|
|
13460
|
-
if (event.type === "error"
|
|
13461
|
-
|
|
13840
|
+
if (event.type === "error") {
|
|
13841
|
+
this.debug(` \u2717 IM error: ${event.error.message}`);
|
|
13462
13842
|
}
|
|
13463
13843
|
});
|
|
13464
13844
|
try {
|
|
13465
13845
|
await client.connect();
|
|
13466
13846
|
return await fn(client);
|
|
13467
13847
|
} finally {
|
|
13848
|
+
this.debug("\u2500\u2500 connection closed \u2500\u2500");
|
|
13468
13849
|
client.close();
|
|
13469
13850
|
}
|
|
13470
13851
|
}
|
|
@@ -13559,7 +13940,7 @@ var IMGateway = class {
|
|
|
13559
13940
|
};
|
|
13560
13941
|
}
|
|
13561
13942
|
try {
|
|
13562
|
-
const result = await this.withConnection((client) => client.queryConversations(
|
|
13943
|
+
const result = await this.withConnection((client) => client.queryConversations());
|
|
13563
13944
|
let conversations3 = result.conversations.map((conv) => ({
|
|
13564
13945
|
id: conv.id,
|
|
13565
13946
|
name: conv.name,
|
|
@@ -13573,6 +13954,9 @@ var IMGateway = class {
|
|
|
13573
13954
|
extraDecoded: conv.extraDecoded,
|
|
13574
13955
|
rawExtra: conv.rawExtra
|
|
13575
13956
|
}));
|
|
13957
|
+
if (options3?.offset && options3.offset > 0) {
|
|
13958
|
+
conversations3 = conversations3.slice(options3.offset);
|
|
13959
|
+
}
|
|
13576
13960
|
if (options3?.limit && options3.limit > 0) {
|
|
13577
13961
|
conversations3 = conversations3.slice(0, options3.limit);
|
|
13578
13962
|
}
|
|
@@ -13621,26 +14005,755 @@ var IMGateway = class {
|
|
|
13621
14005
|
}
|
|
13622
14006
|
};
|
|
13623
14007
|
|
|
14008
|
+
// src/internal/repository/index.ts
|
|
14009
|
+
init_cjs_shims();
|
|
14010
|
+
|
|
14011
|
+
// src/internal/repository/contact-repository.ts
|
|
14012
|
+
init_cjs_shims();
|
|
14013
|
+
var ApiContactRepository = class {
|
|
14014
|
+
constructor(gateway) {
|
|
14015
|
+
this.gateway = gateway;
|
|
14016
|
+
}
|
|
14017
|
+
gateway;
|
|
14018
|
+
async getLinksContacts() {
|
|
14019
|
+
return this.gateway.getLinksContacts();
|
|
14020
|
+
}
|
|
14021
|
+
invalidate() {
|
|
14022
|
+
}
|
|
14023
|
+
async sync() {
|
|
14024
|
+
}
|
|
14025
|
+
clear() {
|
|
14026
|
+
}
|
|
14027
|
+
async getStatus() {
|
|
14028
|
+
return { count: 0, lastSyncAt: null };
|
|
14029
|
+
}
|
|
14030
|
+
};
|
|
14031
|
+
var SqliteContactRepository = class {
|
|
14032
|
+
db;
|
|
14033
|
+
constructor(db) {
|
|
14034
|
+
this.db = db;
|
|
14035
|
+
}
|
|
14036
|
+
async getLinksContacts() {
|
|
14037
|
+
const rows = this.db.prepare(
|
|
14038
|
+
"SELECT owner_uid, data, cached_at FROM contacts_cache ORDER BY cached_at DESC LIMIT 1"
|
|
14039
|
+
).all();
|
|
14040
|
+
if (!rows || rows.length === 0) {
|
|
14041
|
+
return fail("NOT_FOUND" /* NOT_FOUND */, "Contacts not found in cache");
|
|
14042
|
+
}
|
|
14043
|
+
return ok(JSON.parse(rows[0].data));
|
|
14044
|
+
}
|
|
14045
|
+
upsert(ownerUid, data) {
|
|
14046
|
+
this.db.prepare("INSERT INTO contacts_cache (owner_uid, data, cached_at) VALUES (?, ?, ?)").run(ownerUid, JSON.stringify(data), Date.now());
|
|
14047
|
+
}
|
|
14048
|
+
invalidate() {
|
|
14049
|
+
this.db.exec("DELETE FROM contacts_cache");
|
|
14050
|
+
}
|
|
14051
|
+
async sync() {
|
|
14052
|
+
}
|
|
14053
|
+
clear() {
|
|
14054
|
+
this.db.exec("DELETE FROM contacts_cache");
|
|
14055
|
+
this.db.exec("DELETE FROM cache_metadata WHERE key = 'last_sync_contacts'");
|
|
14056
|
+
}
|
|
14057
|
+
async getStatus() {
|
|
14058
|
+
const count = this.db.prepare("SELECT COUNT(*) as c FROM contacts_cache").get();
|
|
14059
|
+
const meta = this.db.prepare("SELECT value FROM cache_metadata WHERE key = 'last_sync_contacts'").get();
|
|
14060
|
+
return {
|
|
14061
|
+
count: count.c,
|
|
14062
|
+
lastSyncAt: meta ? parseInt(meta.value, 10) : null
|
|
14063
|
+
};
|
|
14064
|
+
}
|
|
14065
|
+
};
|
|
14066
|
+
var CachedContactRepository = class {
|
|
14067
|
+
constructor(local, remote, ttlMs = 5 * 60 * 1e3) {
|
|
14068
|
+
this.local = local;
|
|
14069
|
+
this.remote = remote;
|
|
14070
|
+
this.ttlMs = ttlMs;
|
|
14071
|
+
}
|
|
14072
|
+
local;
|
|
14073
|
+
remote;
|
|
14074
|
+
ttlMs;
|
|
14075
|
+
async getLinksContacts() {
|
|
14076
|
+
const cached = await this.local.getLinksContacts();
|
|
14077
|
+
if (cached.ok) {
|
|
14078
|
+
const status = await this.local.getStatus();
|
|
14079
|
+
if (status.lastSyncAt !== null && Date.now() - status.lastSyncAt < this.ttlMs) {
|
|
14080
|
+
return cached;
|
|
14081
|
+
}
|
|
14082
|
+
}
|
|
14083
|
+
const remote = await this.remote.getLinksContacts();
|
|
14084
|
+
if (remote.ok) {
|
|
14085
|
+
this.local.upsert("", remote.data);
|
|
14086
|
+
this.local.db.prepare("INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)").run("last_sync_contacts", String(Date.now()));
|
|
14087
|
+
}
|
|
14088
|
+
return remote;
|
|
14089
|
+
}
|
|
14090
|
+
invalidate() {
|
|
14091
|
+
this.local.invalidate();
|
|
14092
|
+
}
|
|
14093
|
+
async sync() {
|
|
14094
|
+
const result = await this.remote.getLinksContacts();
|
|
14095
|
+
if (result.ok) {
|
|
14096
|
+
this.local.upsert("", result.data);
|
|
14097
|
+
this.local.db.prepare("INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)").run("last_sync_contacts", String(Date.now()));
|
|
14098
|
+
}
|
|
14099
|
+
}
|
|
14100
|
+
clear() {
|
|
14101
|
+
this.local.clear();
|
|
14102
|
+
}
|
|
14103
|
+
async getStatus() {
|
|
14104
|
+
return this.local.getStatus();
|
|
14105
|
+
}
|
|
14106
|
+
};
|
|
14107
|
+
|
|
14108
|
+
// src/internal/repository/conversation-repository.ts
|
|
14109
|
+
init_cjs_shims();
|
|
14110
|
+
var ApiConversationRepository = class {
|
|
14111
|
+
constructor(gateway, imGateway) {
|
|
14112
|
+
this.gateway = gateway;
|
|
14113
|
+
this.imGateway = imGateway;
|
|
14114
|
+
}
|
|
14115
|
+
gateway;
|
|
14116
|
+
imGateway;
|
|
14117
|
+
async queryAll(options3) {
|
|
14118
|
+
const result = await this.imGateway.queryConversations(options3);
|
|
14119
|
+
if (result.status === "error") {
|
|
14120
|
+
return fail(result.code, result.message);
|
|
14121
|
+
}
|
|
14122
|
+
return ok(result.data?.conversations ?? []);
|
|
14123
|
+
}
|
|
14124
|
+
async getProfile(conversationId, conversationType) {
|
|
14125
|
+
return this.gateway.getProfileByConversationId(conversationId, conversationType);
|
|
14126
|
+
}
|
|
14127
|
+
async searchByUid(uid) {
|
|
14128
|
+
const listResult = await this.imGateway.queryConversations({ limit: 0, offset: 0 });
|
|
14129
|
+
if (listResult.status === "error") {
|
|
14130
|
+
return fail(listResult.code, listResult.message);
|
|
14131
|
+
}
|
|
14132
|
+
const conversations3 = listResult.data?.conversations ?? [];
|
|
14133
|
+
const results = [];
|
|
14134
|
+
for (const conv of conversations3) {
|
|
14135
|
+
const profileResult = await this.gateway.getProfileByConversationId(conv.id);
|
|
14136
|
+
if (!profileResult.ok) continue;
|
|
14137
|
+
const hasTarget = profileResult.data.users.some((u) => u.uid === uid);
|
|
14138
|
+
if (hasTarget) {
|
|
14139
|
+
results.push(profileResult.data);
|
|
14140
|
+
}
|
|
14141
|
+
}
|
|
14142
|
+
return ok(results);
|
|
14143
|
+
}
|
|
14144
|
+
invalidate(_conversationId) {
|
|
14145
|
+
}
|
|
14146
|
+
async sync() {
|
|
14147
|
+
}
|
|
14148
|
+
clear() {
|
|
14149
|
+
}
|
|
14150
|
+
async getStatus() {
|
|
14151
|
+
return { count: 0, lastSyncAt: null };
|
|
14152
|
+
}
|
|
14153
|
+
};
|
|
14154
|
+
var SqliteConversationRepository = class {
|
|
14155
|
+
db;
|
|
14156
|
+
constructor(db) {
|
|
14157
|
+
this.db = db;
|
|
14158
|
+
}
|
|
14159
|
+
queryAll(_options) {
|
|
14160
|
+
return Promise.resolve(ok([]));
|
|
14161
|
+
}
|
|
14162
|
+
getProfile(conversationId) {
|
|
14163
|
+
const row = this.db.prepare("SELECT data FROM conversation_profiles WHERE conversation_id = ?").get(conversationId);
|
|
14164
|
+
if (!row) {
|
|
14165
|
+
return Promise.resolve(
|
|
14166
|
+
fail("NOT_FOUND" /* NOT_FOUND */, "Conversation profile not found in cache")
|
|
14167
|
+
);
|
|
14168
|
+
}
|
|
14169
|
+
return Promise.resolve(ok(JSON.parse(row.data)));
|
|
14170
|
+
}
|
|
14171
|
+
searchByUid(uid) {
|
|
14172
|
+
const rows = this.db.prepare(
|
|
14173
|
+
"SELECT data FROM conversation_profiles WHERE user_ids LIKE ? OR user_ids LIKE ? OR user_ids LIKE ? OR user_ids = ?"
|
|
14174
|
+
).all(`%${uid},%`, `%,${uid},%`, `%,${uid}`, uid);
|
|
14175
|
+
const results = rows.map((r) => JSON.parse(r.data));
|
|
14176
|
+
return Promise.resolve(ok(results));
|
|
14177
|
+
}
|
|
14178
|
+
upsertProfile(conversationId, profile) {
|
|
14179
|
+
const user_ids = profile.users.map((u) => u.uid).join(",");
|
|
14180
|
+
const cached_at = Date.now();
|
|
14181
|
+
const data = JSON.stringify(profile);
|
|
14182
|
+
this.db.prepare(
|
|
14183
|
+
"INSERT INTO conversation_profiles (conversation_id, data, user_ids, cached_at) VALUES (?, ?, ?, ?) ON CONFLICT(conversation_id) DO UPDATE SET data=?, user_ids=?, cached_at=?"
|
|
14184
|
+
).run(conversationId, data, user_ids, cached_at, data, user_ids, cached_at);
|
|
14185
|
+
}
|
|
14186
|
+
// MARK: Conversation Index (for incremental sync)
|
|
14187
|
+
upsertIndex(conv) {
|
|
14188
|
+
const members = JSON.stringify(conv.cids);
|
|
14189
|
+
const nicks = conv.nicks ? JSON.stringify(conv.nicks) : null;
|
|
14190
|
+
const im_version = conv.version;
|
|
14191
|
+
const cached_at = Date.now();
|
|
14192
|
+
this.db.prepare(
|
|
14193
|
+
"INSERT INTO conversations_index (conversation_id, im_version, members, nicks, cached_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(conversation_id) DO UPDATE SET im_version=?, members=?, nicks=?, cached_at=?"
|
|
14194
|
+
).run(conv.id, im_version, members, nicks, cached_at, im_version, members, nicks, cached_at);
|
|
14195
|
+
}
|
|
14196
|
+
getIndexById(conversationId) {
|
|
14197
|
+
return this.db.prepare(
|
|
14198
|
+
"SELECT conversation_id, im_version, members, nicks, cached_at FROM conversations_index WHERE conversation_id = ?"
|
|
14199
|
+
).get(conversationId);
|
|
14200
|
+
}
|
|
14201
|
+
getAllIds() {
|
|
14202
|
+
const rows = this.db.prepare("SELECT conversation_id FROM conversations_index").all();
|
|
14203
|
+
return rows.map((r) => r.conversation_id);
|
|
14204
|
+
}
|
|
14205
|
+
deleteStale(conversationIds) {
|
|
14206
|
+
if (conversationIds.length === 0) return;
|
|
14207
|
+
const placeholders = conversationIds.map(() => "?").join(",");
|
|
14208
|
+
this.db.prepare(`DELETE FROM conversation_profiles WHERE conversation_id NOT IN (${placeholders})`).run(...conversationIds);
|
|
14209
|
+
this.db.prepare(`DELETE FROM conversations_index WHERE conversation_id NOT IN (${placeholders})`).run(...conversationIds);
|
|
14210
|
+
}
|
|
14211
|
+
invalidate(conversationId) {
|
|
14212
|
+
this.db.prepare("DELETE FROM conversation_profiles WHERE conversation_id = ?").run(conversationId);
|
|
14213
|
+
this.db.prepare("DELETE FROM conversations_index WHERE conversation_id = ?").run(conversationId);
|
|
14214
|
+
}
|
|
14215
|
+
sync() {
|
|
14216
|
+
return Promise.resolve();
|
|
14217
|
+
}
|
|
14218
|
+
clear() {
|
|
14219
|
+
this.db.exec("DELETE FROM conversation_profiles");
|
|
14220
|
+
this.db.exec("DELETE FROM conversations_index");
|
|
14221
|
+
this.db.exec("DELETE FROM cache_metadata WHERE key = 'last_sync_conversations'");
|
|
14222
|
+
}
|
|
14223
|
+
getStatus() {
|
|
14224
|
+
const count = this.db.prepare("SELECT COUNT(*) as c FROM conversation_profiles").get();
|
|
14225
|
+
const meta = this.db.prepare("SELECT value FROM cache_metadata WHERE key = 'last_sync_conversations'").get();
|
|
14226
|
+
return Promise.resolve({
|
|
14227
|
+
count: count.c,
|
|
14228
|
+
lastSyncAt: meta ? parseInt(meta.value, 10) : null
|
|
14229
|
+
});
|
|
14230
|
+
}
|
|
14231
|
+
};
|
|
14232
|
+
var CachedConversationRepository = class {
|
|
14233
|
+
// 30 minutes
|
|
14234
|
+
constructor(local, remote) {
|
|
14235
|
+
this.local = local;
|
|
14236
|
+
this.remote = remote;
|
|
14237
|
+
}
|
|
14238
|
+
local;
|
|
14239
|
+
remote;
|
|
14240
|
+
syncInProgress = false;
|
|
14241
|
+
TTL_MS = 30 * 60 * 1e3;
|
|
14242
|
+
async queryAll(options3) {
|
|
14243
|
+
return this.remote.queryAll(options3);
|
|
14244
|
+
}
|
|
14245
|
+
async getProfile(conversationId, conversationType) {
|
|
14246
|
+
const cached = await this.local.getProfile(conversationId);
|
|
14247
|
+
if (cached.ok) return cached;
|
|
14248
|
+
const remote = await this.remote.getProfile(conversationId, conversationType);
|
|
14249
|
+
if (remote.ok) {
|
|
14250
|
+
this.local.upsertProfile(conversationId, remote.data);
|
|
14251
|
+
}
|
|
14252
|
+
return remote;
|
|
14253
|
+
}
|
|
14254
|
+
async searchByUid(uid) {
|
|
14255
|
+
const cached = await this.local.searchByUid(uid);
|
|
14256
|
+
if (cached.ok && cached.data.length > 0) return cached;
|
|
14257
|
+
await this.ensureSynced();
|
|
14258
|
+
const retry = await this.local.searchByUid(uid);
|
|
14259
|
+
return retry;
|
|
14260
|
+
}
|
|
14261
|
+
async ensureSynced() {
|
|
14262
|
+
if (this.syncInProgress) return;
|
|
14263
|
+
const status = await this.local.getStatus();
|
|
14264
|
+
const isFresh = status.lastSyncAt !== null && Date.now() - status.lastSyncAt < this.TTL_MS;
|
|
14265
|
+
if (isFresh) return;
|
|
14266
|
+
this.syncInProgress = true;
|
|
14267
|
+
try {
|
|
14268
|
+
await this.sync();
|
|
14269
|
+
} finally {
|
|
14270
|
+
this.syncInProgress = false;
|
|
14271
|
+
}
|
|
14272
|
+
}
|
|
14273
|
+
async sync() {
|
|
14274
|
+
const convResult = await this.remote.queryAll({ limit: 0, offset: 0 });
|
|
14275
|
+
if (!convResult.ok || !convResult.data) return;
|
|
14276
|
+
const remoteConversations = convResult.data;
|
|
14277
|
+
const remoteIds = new Set(remoteConversations.map((c) => c.id));
|
|
14278
|
+
const cachedIds = new Set(this.local.getAllIds());
|
|
14279
|
+
const newIds = remoteConversations.filter((c) => !cachedIds.has(c.id)).map((c) => c.id);
|
|
14280
|
+
for (const conv of remoteConversations) {
|
|
14281
|
+
if (newIds.includes(conv.id)) {
|
|
14282
|
+
const profileResult = await this.remote.getProfile(conv.id);
|
|
14283
|
+
if (profileResult.ok) {
|
|
14284
|
+
this.local.upsertProfile(conv.id, profileResult.data);
|
|
14285
|
+
}
|
|
14286
|
+
}
|
|
14287
|
+
}
|
|
14288
|
+
for (const conv of remoteConversations) {
|
|
14289
|
+
this.local.upsertIndex(conv);
|
|
14290
|
+
}
|
|
14291
|
+
this.local.deleteStale([...remoteIds]);
|
|
14292
|
+
this.local.db.prepare("INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)").run("last_sync_conversations", String(Date.now()));
|
|
14293
|
+
}
|
|
14294
|
+
invalidate(conversationId) {
|
|
14295
|
+
this.local.invalidate(conversationId);
|
|
14296
|
+
}
|
|
14297
|
+
clear() {
|
|
14298
|
+
this.local.clear();
|
|
14299
|
+
}
|
|
14300
|
+
async getStatus() {
|
|
14301
|
+
return this.local.getStatus();
|
|
14302
|
+
}
|
|
14303
|
+
};
|
|
14304
|
+
|
|
14305
|
+
// src/internal/repository/message-repository.ts
|
|
14306
|
+
init_cjs_shims();
|
|
14307
|
+
var ApiMessageRepository = class {
|
|
14308
|
+
constructor(imGateway) {
|
|
14309
|
+
this.imGateway = imGateway;
|
|
14310
|
+
}
|
|
14311
|
+
imGateway;
|
|
14312
|
+
async pullMessages(conversationId, options3) {
|
|
14313
|
+
const result = await this.imGateway.pullMessages(conversationId, options3);
|
|
14314
|
+
if (result.status === "error") {
|
|
14315
|
+
return fail(result.code, result.message);
|
|
14316
|
+
}
|
|
14317
|
+
return ok({
|
|
14318
|
+
messages: result.data?.messages ?? [],
|
|
14319
|
+
finished: result.data?.finished ?? true,
|
|
14320
|
+
lastId: result.data?.lastId ?? ""
|
|
14321
|
+
});
|
|
14322
|
+
}
|
|
14323
|
+
invalidate(_conversationId) {
|
|
14324
|
+
}
|
|
14325
|
+
async sync(_conversationId) {
|
|
14326
|
+
}
|
|
14327
|
+
clear() {
|
|
14328
|
+
}
|
|
14329
|
+
};
|
|
14330
|
+
var SqliteMessageRepository = class {
|
|
14331
|
+
db;
|
|
14332
|
+
constructor(db) {
|
|
14333
|
+
this.db = db;
|
|
14334
|
+
}
|
|
14335
|
+
async pullMessages(conversationId, options3) {
|
|
14336
|
+
const limit = options3?.limit ?? 50;
|
|
14337
|
+
const fromId = options3?.fromMessageId;
|
|
14338
|
+
let sql = "SELECT message_id, data FROM messages_cache WHERE conversation_id = ?";
|
|
14339
|
+
const params = [conversationId];
|
|
14340
|
+
if (fromId) {
|
|
14341
|
+
if (options3?.direction === "newer") {
|
|
14342
|
+
sql += " AND message_id > ?";
|
|
14343
|
+
params.push(fromId);
|
|
14344
|
+
} else {
|
|
14345
|
+
sql += " AND message_id < ?";
|
|
14346
|
+
params.push(fromId);
|
|
14347
|
+
}
|
|
14348
|
+
}
|
|
14349
|
+
sql += " ORDER BY message_id DESC LIMIT ?";
|
|
14350
|
+
params.push(limit);
|
|
14351
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
14352
|
+
if (rows.length === 0) {
|
|
14353
|
+
return fail("NOT_FOUND" /* NOT_FOUND */, "Messages not found in cache");
|
|
14354
|
+
}
|
|
14355
|
+
return ok({
|
|
14356
|
+
messages: rows.map((r) => JSON.parse(r.data)),
|
|
14357
|
+
finished: rows.length < limit,
|
|
14358
|
+
lastId: rows[rows.length - 1]?.message_id ?? ""
|
|
14359
|
+
});
|
|
14360
|
+
}
|
|
14361
|
+
upsert(conversationId, messageId, data) {
|
|
14362
|
+
this.db.prepare(
|
|
14363
|
+
"INSERT INTO messages_cache (conversation_id, message_id, data, cached_at) VALUES (?, ?, ?, ?) ON CONFLICT(conversation_id, message_id) DO UPDATE SET data=?, cached_at=?"
|
|
14364
|
+
).run(
|
|
14365
|
+
conversationId,
|
|
14366
|
+
messageId,
|
|
14367
|
+
JSON.stringify(data),
|
|
14368
|
+
Date.now(),
|
|
14369
|
+
JSON.stringify(data),
|
|
14370
|
+
Date.now()
|
|
14371
|
+
);
|
|
14372
|
+
}
|
|
14373
|
+
invalidate(conversationId) {
|
|
14374
|
+
this.db.prepare("DELETE FROM messages_cache WHERE conversation_id = ?").run(conversationId);
|
|
14375
|
+
}
|
|
14376
|
+
async sync(_conversationId) {
|
|
14377
|
+
}
|
|
14378
|
+
clear() {
|
|
14379
|
+
this.db.exec("DELETE FROM messages_cache");
|
|
14380
|
+
}
|
|
14381
|
+
};
|
|
14382
|
+
var CachedMessageRepository = class {
|
|
14383
|
+
constructor(local, remote) {
|
|
14384
|
+
this.local = local;
|
|
14385
|
+
this.remote = remote;
|
|
14386
|
+
}
|
|
14387
|
+
local;
|
|
14388
|
+
remote;
|
|
14389
|
+
async pullMessages(conversationId, options3) {
|
|
14390
|
+
const cached = await this.local.pullMessages(conversationId, options3);
|
|
14391
|
+
if (cached.ok) return cached;
|
|
14392
|
+
const remote = await this.remote.pullMessages(conversationId, options3);
|
|
14393
|
+
if (remote.ok) {
|
|
14394
|
+
for (const msg of remote.data.messages) {
|
|
14395
|
+
const messageId = msg.id;
|
|
14396
|
+
if (messageId) {
|
|
14397
|
+
this.local.upsert(conversationId, messageId, msg);
|
|
14398
|
+
}
|
|
14399
|
+
}
|
|
14400
|
+
}
|
|
14401
|
+
return remote;
|
|
14402
|
+
}
|
|
14403
|
+
invalidate(conversationId) {
|
|
14404
|
+
this.local.invalidate(conversationId);
|
|
14405
|
+
}
|
|
14406
|
+
async sync(conversationId) {
|
|
14407
|
+
const result = await this.remote.pullMessages(conversationId, { limit: 100 });
|
|
14408
|
+
if (result.ok) {
|
|
14409
|
+
for (const msg of result.data.messages) {
|
|
14410
|
+
const messageId = msg.id;
|
|
14411
|
+
if (messageId) {
|
|
14412
|
+
this.local.upsert(conversationId, messageId, msg);
|
|
14413
|
+
}
|
|
14414
|
+
}
|
|
14415
|
+
}
|
|
14416
|
+
}
|
|
14417
|
+
clear() {
|
|
14418
|
+
this.local.clear();
|
|
14419
|
+
}
|
|
14420
|
+
};
|
|
14421
|
+
|
|
14422
|
+
// src/internal/repository/relation-repository.ts
|
|
14423
|
+
init_cjs_shims();
|
|
14424
|
+
var ApiRelationRepository = class {
|
|
14425
|
+
constructor(gateway) {
|
|
14426
|
+
this.gateway = gateway;
|
|
14427
|
+
}
|
|
14428
|
+
gateway;
|
|
14429
|
+
async getLinkNameType(friendUid) {
|
|
14430
|
+
return this.gateway.getLinkNameType(friendUid);
|
|
14431
|
+
}
|
|
14432
|
+
invalidate(_friendUid) {
|
|
14433
|
+
}
|
|
14434
|
+
async sync() {
|
|
14435
|
+
}
|
|
14436
|
+
clear() {
|
|
14437
|
+
}
|
|
14438
|
+
};
|
|
14439
|
+
var SqliteRelationRepository = class {
|
|
14440
|
+
db;
|
|
14441
|
+
constructor(db) {
|
|
14442
|
+
this.db = db;
|
|
14443
|
+
}
|
|
14444
|
+
async getLinkNameType(friendUid) {
|
|
14445
|
+
const row = this.db.prepare("SELECT name, type, cached_at FROM relations_cache WHERE friend_uid = ?").get(friendUid);
|
|
14446
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "Relation not found in cache");
|
|
14447
|
+
return ok({ name: row.name, type: row.type });
|
|
14448
|
+
}
|
|
14449
|
+
upsert(friendUid, name, type) {
|
|
14450
|
+
this.db.prepare(
|
|
14451
|
+
"INSERT OR REPLACE INTO relations_cache (friend_uid, name, type, cached_at) VALUES (?, ?, ?, ?)"
|
|
14452
|
+
).run(friendUid, name, type, Date.now());
|
|
14453
|
+
}
|
|
14454
|
+
invalidate(friendUid) {
|
|
14455
|
+
this.db.prepare("DELETE FROM relations_cache WHERE friend_uid = ?").run(friendUid);
|
|
14456
|
+
}
|
|
14457
|
+
async sync() {
|
|
14458
|
+
}
|
|
14459
|
+
clear() {
|
|
14460
|
+
this.db.exec("DELETE FROM relations_cache");
|
|
14461
|
+
}
|
|
14462
|
+
};
|
|
14463
|
+
var CachedRelationRepository = class {
|
|
14464
|
+
constructor(local, remote, ttlMs = 5 * 60 * 1e3) {
|
|
14465
|
+
this.local = local;
|
|
14466
|
+
this.remote = remote;
|
|
14467
|
+
this.ttlMs = ttlMs;
|
|
14468
|
+
}
|
|
14469
|
+
local;
|
|
14470
|
+
remote;
|
|
14471
|
+
ttlMs;
|
|
14472
|
+
async getLinkNameType(friendUid) {
|
|
14473
|
+
const row = this.local.db.prepare("SELECT name, type, cached_at FROM relations_cache WHERE friend_uid = ?").get(friendUid);
|
|
14474
|
+
if (row && Date.now() - row.cached_at < this.ttlMs) {
|
|
14475
|
+
return ok({ name: row.name, type: row.type });
|
|
14476
|
+
}
|
|
14477
|
+
const remote = await this.remote.getLinkNameType(friendUid);
|
|
14478
|
+
if (remote.ok) {
|
|
14479
|
+
this.local.upsert(friendUid, remote.data.name, remote.data.type);
|
|
14480
|
+
}
|
|
14481
|
+
return remote;
|
|
14482
|
+
}
|
|
14483
|
+
invalidate(friendUid) {
|
|
14484
|
+
this.local.invalidate(friendUid);
|
|
14485
|
+
}
|
|
14486
|
+
async sync() {
|
|
14487
|
+
}
|
|
14488
|
+
clear() {
|
|
14489
|
+
this.local.clear();
|
|
14490
|
+
}
|
|
14491
|
+
};
|
|
14492
|
+
|
|
14493
|
+
// src/internal/repository/sqlite.ts
|
|
14494
|
+
init_cjs_shims();
|
|
14495
|
+
var import_node_path5 = require("path");
|
|
14496
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
14497
|
+
var SCHEMA_VERSION = 1;
|
|
14498
|
+
var CREATE_TABLES = `
|
|
14499
|
+
CREATE TABLE IF NOT EXISTS conversation_profiles (
|
|
14500
|
+
conversation_id TEXT PRIMARY KEY,
|
|
14501
|
+
data TEXT NOT NULL,
|
|
14502
|
+
user_ids TEXT NOT NULL,
|
|
14503
|
+
cached_at INTEGER NOT NULL
|
|
14504
|
+
);
|
|
14505
|
+
CREATE INDEX IF NOT EXISTS idx_conv_user_ids ON conversation_profiles(user_ids);
|
|
14506
|
+
|
|
14507
|
+
CREATE TABLE IF NOT EXISTS contacts_cache (
|
|
14508
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
14509
|
+
owner_uid TEXT NOT NULL,
|
|
14510
|
+
data TEXT NOT NULL,
|
|
14511
|
+
cached_at INTEGER NOT NULL
|
|
14512
|
+
);
|
|
14513
|
+
CREATE INDEX IF NOT EXISTS idx_contacts_owner ON contacts_cache(owner_uid);
|
|
14514
|
+
|
|
14515
|
+
CREATE TABLE IF NOT EXISTS user_profiles (
|
|
14516
|
+
uid TEXT PRIMARY KEY,
|
|
14517
|
+
data TEXT NOT NULL,
|
|
14518
|
+
cached_at INTEGER NOT NULL
|
|
14519
|
+
);
|
|
14520
|
+
|
|
14521
|
+
CREATE TABLE IF NOT EXISTS relations_cache (
|
|
14522
|
+
friend_uid TEXT PRIMARY KEY,
|
|
14523
|
+
name TEXT,
|
|
14524
|
+
type INTEGER,
|
|
14525
|
+
cached_at INTEGER NOT NULL
|
|
14526
|
+
);
|
|
14527
|
+
|
|
14528
|
+
CREATE TABLE IF NOT EXISTS messages_cache (
|
|
14529
|
+
conversation_id TEXT NOT NULL,
|
|
14530
|
+
message_id TEXT NOT NULL,
|
|
14531
|
+
data TEXT NOT NULL,
|
|
14532
|
+
cached_at INTEGER NOT NULL,
|
|
14533
|
+
PRIMARY KEY (conversation_id, message_id)
|
|
14534
|
+
);
|
|
14535
|
+
CREATE INDEX IF NOT EXISTS idx_msg_conv ON messages_cache(conversation_id);
|
|
14536
|
+
|
|
14537
|
+
CREATE TABLE IF NOT EXISTS conversations_index (
|
|
14538
|
+
conversation_id TEXT PRIMARY KEY,
|
|
14539
|
+
im_version INTEGER NOT NULL,
|
|
14540
|
+
members TEXT NOT NULL,
|
|
14541
|
+
nicks TEXT,
|
|
14542
|
+
cached_at INTEGER NOT NULL
|
|
14543
|
+
);
|
|
14544
|
+
|
|
14545
|
+
CREATE TABLE IF NOT EXISTS cache_metadata (
|
|
14546
|
+
key TEXT PRIMARY KEY,
|
|
14547
|
+
value TEXT
|
|
14548
|
+
);
|
|
14549
|
+
`;
|
|
14550
|
+
function resolveDbPaths(configDir, uid) {
|
|
14551
|
+
return {
|
|
14552
|
+
dbFile: (0, import_node_path5.join)(configDir, `cache-${uid}.db`)
|
|
14553
|
+
};
|
|
14554
|
+
}
|
|
14555
|
+
function openDatabase(dbPath) {
|
|
14556
|
+
const db = new import_better_sqlite3.default(dbPath);
|
|
14557
|
+
db.pragma("journal_mode = WAL");
|
|
14558
|
+
db.pragma("foreign_keys = ON");
|
|
14559
|
+
db.exec(CREATE_TABLES);
|
|
14560
|
+
ensureVersion(db);
|
|
14561
|
+
return db;
|
|
14562
|
+
}
|
|
14563
|
+
function closeDatabase(db) {
|
|
14564
|
+
db.close();
|
|
14565
|
+
}
|
|
14566
|
+
function ensureVersion(db) {
|
|
14567
|
+
const row = db.prepare("SELECT value FROM cache_metadata WHERE key = 'schema_version'").get();
|
|
14568
|
+
const current = row ? parseInt(row.value, 10) : 0;
|
|
14569
|
+
if (current < SCHEMA_VERSION) {
|
|
14570
|
+
db.prepare("INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)").run(
|
|
14571
|
+
"schema_version",
|
|
14572
|
+
String(SCHEMA_VERSION)
|
|
14573
|
+
);
|
|
14574
|
+
}
|
|
14575
|
+
}
|
|
14576
|
+
|
|
14577
|
+
// src/internal/repository/user-repository.ts
|
|
14578
|
+
init_cjs_shims();
|
|
14579
|
+
var ApiUserRepository = class {
|
|
14580
|
+
constructor(gateway) {
|
|
14581
|
+
this.gateway = gateway;
|
|
14582
|
+
}
|
|
14583
|
+
gateway;
|
|
14584
|
+
findUserById(id) {
|
|
14585
|
+
return this.gateway.findUserById(id);
|
|
14586
|
+
}
|
|
14587
|
+
searchUserById(id) {
|
|
14588
|
+
return this.gateway.searchUserById(id);
|
|
14589
|
+
}
|
|
14590
|
+
getUserAgentProfile(friendUid, agentId) {
|
|
14591
|
+
return this.gateway.getUserAgentProfile(friendUid, agentId);
|
|
14592
|
+
}
|
|
14593
|
+
invalidate(_uid) {
|
|
14594
|
+
}
|
|
14595
|
+
async sync() {
|
|
14596
|
+
}
|
|
14597
|
+
clear() {
|
|
14598
|
+
}
|
|
14599
|
+
};
|
|
14600
|
+
var SqliteUserRepository = class {
|
|
14601
|
+
db;
|
|
14602
|
+
constructor(db) {
|
|
14603
|
+
this.db = db;
|
|
14604
|
+
}
|
|
14605
|
+
async findUserById(id) {
|
|
14606
|
+
const row = this.db.prepare("SELECT data FROM user_profiles WHERE uid = ?").get(id);
|
|
14607
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "User not found in cache");
|
|
14608
|
+
return ok(JSON.parse(row.data));
|
|
14609
|
+
}
|
|
14610
|
+
async searchUserById(id) {
|
|
14611
|
+
const row = this.db.prepare("SELECT data FROM user_profiles WHERE uid = ?").get(id);
|
|
14612
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "User not found in cache");
|
|
14613
|
+
const data = JSON.parse(row.data);
|
|
14614
|
+
return ok({
|
|
14615
|
+
id: data.id,
|
|
14616
|
+
uid: data.uid,
|
|
14617
|
+
nickname: data.nickname,
|
|
14618
|
+
avatar: data.avatar,
|
|
14619
|
+
agent: data.agent
|
|
14620
|
+
});
|
|
14621
|
+
}
|
|
14622
|
+
async getUserAgentProfile(friendUid, _agentId) {
|
|
14623
|
+
const row = this.db.prepare("SELECT data FROM user_profiles WHERE uid = ?").get(friendUid);
|
|
14624
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "User profile not found in cache");
|
|
14625
|
+
return ok(JSON.parse(row.data));
|
|
14626
|
+
}
|
|
14627
|
+
upsert(uid, data) {
|
|
14628
|
+
this.db.prepare("INSERT OR REPLACE INTO user_profiles (uid, data, cached_at) VALUES (?, ?, ?)").run(uid, JSON.stringify(data), Date.now());
|
|
14629
|
+
}
|
|
14630
|
+
invalidate(uid) {
|
|
14631
|
+
this.db.prepare("DELETE FROM user_profiles WHERE uid = ?").run(uid);
|
|
14632
|
+
}
|
|
14633
|
+
async sync() {
|
|
14634
|
+
}
|
|
14635
|
+
clear() {
|
|
14636
|
+
this.db.exec("DELETE FROM user_profiles");
|
|
14637
|
+
}
|
|
14638
|
+
};
|
|
14639
|
+
var CachedUserRepository = class {
|
|
14640
|
+
constructor(local, remote, ttlMs = 60 * 60 * 1e3) {
|
|
14641
|
+
this.local = local;
|
|
14642
|
+
this.remote = remote;
|
|
14643
|
+
this.ttlMs = ttlMs;
|
|
14644
|
+
}
|
|
14645
|
+
local;
|
|
14646
|
+
remote;
|
|
14647
|
+
ttlMs;
|
|
14648
|
+
async findUserById(id) {
|
|
14649
|
+
return this.fetchWithCache(
|
|
14650
|
+
id,
|
|
14651
|
+
() => this.remote.findUserById(id),
|
|
14652
|
+
(data) => this.local.upsert(id, data)
|
|
14653
|
+
);
|
|
14654
|
+
}
|
|
14655
|
+
async searchUserById(id) {
|
|
14656
|
+
return this.fetchWithCache(
|
|
14657
|
+
id,
|
|
14658
|
+
() => this.remote.searchUserById(id),
|
|
14659
|
+
(data) => {
|
|
14660
|
+
this.local.upsert(id, {
|
|
14661
|
+
id: data.id,
|
|
14662
|
+
uid: data.uid,
|
|
14663
|
+
nickname: data.nickname,
|
|
14664
|
+
avatar: data.avatar,
|
|
14665
|
+
title: "",
|
|
14666
|
+
link_type: 0,
|
|
14667
|
+
sex: 0,
|
|
14668
|
+
address: "",
|
|
14669
|
+
status: 0,
|
|
14670
|
+
agent: data.agent
|
|
14671
|
+
});
|
|
14672
|
+
}
|
|
14673
|
+
);
|
|
14674
|
+
}
|
|
14675
|
+
async getUserAgentProfile(friendUid, agentId) {
|
|
14676
|
+
return this.fetchWithCache(
|
|
14677
|
+
friendUid,
|
|
14678
|
+
() => this.remote.getUserAgentProfile(friendUid, agentId),
|
|
14679
|
+
(data) => this.local.upsert(friendUid, data)
|
|
14680
|
+
);
|
|
14681
|
+
}
|
|
14682
|
+
async fetchWithCache(uid, fetch2, store) {
|
|
14683
|
+
const row = this.local.db.prepare("SELECT data, cached_at FROM user_profiles WHERE uid = ?").get(uid);
|
|
14684
|
+
if (row && Date.now() - row.cached_at < this.ttlMs) {
|
|
14685
|
+
return ok(JSON.parse(row.data));
|
|
14686
|
+
}
|
|
14687
|
+
const remote = await fetch2();
|
|
14688
|
+
if (remote.ok) {
|
|
14689
|
+
store(remote.data);
|
|
14690
|
+
}
|
|
14691
|
+
return remote;
|
|
14692
|
+
}
|
|
14693
|
+
invalidate(uid) {
|
|
14694
|
+
this.local.invalidate(uid);
|
|
14695
|
+
}
|
|
14696
|
+
async sync() {
|
|
14697
|
+
}
|
|
14698
|
+
clear() {
|
|
14699
|
+
this.local.clear();
|
|
14700
|
+
}
|
|
14701
|
+
};
|
|
14702
|
+
|
|
14703
|
+
// src/internal/repository/index.ts
|
|
14704
|
+
function createRepositories(configDir, uid, gateway, imGateway) {
|
|
14705
|
+
const paths = resolveDbPaths(configDir, uid);
|
|
14706
|
+
const db = openDatabase(paths.dbFile);
|
|
14707
|
+
const sqliteConv = new SqliteConversationRepository(db);
|
|
14708
|
+
const sqliteContact = new SqliteContactRepository(db);
|
|
14709
|
+
const sqliteUser = new SqliteUserRepository(db);
|
|
14710
|
+
const sqliteRelation = new SqliteRelationRepository(db);
|
|
14711
|
+
const sqliteMessage = new SqliteMessageRepository(db);
|
|
14712
|
+
const apiConv = new ApiConversationRepository(gateway, imGateway);
|
|
14713
|
+
const apiContact = new ApiContactRepository(gateway);
|
|
14714
|
+
const apiUser = new ApiUserRepository(gateway);
|
|
14715
|
+
const apiRelation = new ApiRelationRepository(gateway);
|
|
14716
|
+
const apiMessage = new ApiMessageRepository(imGateway);
|
|
14717
|
+
const conversation = new CachedConversationRepository(sqliteConv, apiConv);
|
|
14718
|
+
const contact = new CachedContactRepository(sqliteContact, apiContact);
|
|
14719
|
+
const user = new CachedUserRepository(sqliteUser, apiUser);
|
|
14720
|
+
const relation = new CachedRelationRepository(sqliteRelation, apiRelation);
|
|
14721
|
+
const message = new CachedMessageRepository(sqliteMessage, apiMessage);
|
|
14722
|
+
return {
|
|
14723
|
+
conversation,
|
|
14724
|
+
contact,
|
|
14725
|
+
user,
|
|
14726
|
+
relation,
|
|
14727
|
+
message,
|
|
14728
|
+
close: () => closeDatabase(db)
|
|
14729
|
+
};
|
|
14730
|
+
}
|
|
14731
|
+
|
|
13624
14732
|
// src/factory.ts
|
|
13625
14733
|
function createFactory(config) {
|
|
13626
14734
|
const client = new APIClient(config.baseURL, config.debug);
|
|
13627
14735
|
if (config.uid && config.tk && config.token) {
|
|
13628
14736
|
client.setAuth({ uid: config.uid, tk: config.tk, token: config.token });
|
|
13629
14737
|
}
|
|
14738
|
+
const gateway = new ApiGateway(client);
|
|
14739
|
+
const imGateway = new IMGateway({
|
|
14740
|
+
wsUrl: config.wsURL,
|
|
14741
|
+
cid: config.cid,
|
|
14742
|
+
debugEnabled: config.debug
|
|
14743
|
+
});
|
|
14744
|
+
const configDir = getConfigDirectory(detectEnvironment());
|
|
14745
|
+
const repos = createRepositories(configDir, config.uid ?? "anonymous", gateway, imGateway);
|
|
13630
14746
|
return {
|
|
13631
|
-
gateway
|
|
13632
|
-
imGateway
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
debugEnabled: config.debug
|
|
13636
|
-
}),
|
|
13637
|
-
config
|
|
14747
|
+
gateway,
|
|
14748
|
+
imGateway,
|
|
14749
|
+
config,
|
|
14750
|
+
repos
|
|
13638
14751
|
};
|
|
13639
14752
|
}
|
|
13640
14753
|
|
|
13641
14754
|
// src/cmd.ts
|
|
13642
14755
|
var logDebugEnabled = false;
|
|
13643
|
-
var LocalizedHelp = class extends
|
|
14756
|
+
var LocalizedHelp = class extends import_commander9.Help {
|
|
13644
14757
|
/** Format an argument name with <required> or [optional] brackets. */
|
|
13645
14758
|
argDisplayName(arg) {
|
|
13646
14759
|
const name = arg.name() + (arg.variadic === true ? "..." : "");
|
|
@@ -13683,7 +14796,7 @@ async function main() {
|
|
|
13683
14796
|
logDebugEnabled = config.debug;
|
|
13684
14797
|
setLogDebug(config.debug);
|
|
13685
14798
|
const factory = createFactory(config);
|
|
13686
|
-
const program = new
|
|
14799
|
+
const program = new import_commander9.Command();
|
|
13687
14800
|
const testModeBanner = `
|
|
13688
14801
|
\u2554\u2550\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2550\u2557
|
|
13689
14802
|
\u2551 DEVELOPMENT BUILD \u2551
|
|
@@ -13694,7 +14807,7 @@ async function main() {
|
|
|
13694
14807
|
${t("cli.banner")}` : t("cli.banner");
|
|
13695
14808
|
program.addHelpText("beforeAll", `${banner}
|
|
13696
14809
|
`);
|
|
13697
|
-
program.name("qz").version(`v${"0.
|
|
14810
|
+
program.name("qz").version(`v${"0.3.0"}`, "-v, --version", t("options.version")).helpOption("-h, --help", t("options.help")).option("-q, --jq <expr>", t("options.jq")).option("--dry-run", t("options.dryRun"));
|
|
13698
14811
|
program.usage("<command> [subcommand] [options]");
|
|
13699
14812
|
program.hook("preAction", () => {
|
|
13700
14813
|
const opts = program.opts();
|
|
@@ -13704,6 +14817,7 @@ ${t("cli.banner")}` : t("cli.banner");
|
|
|
13704
14817
|
factory.gateway.setDryRun(dryRun2);
|
|
13705
14818
|
factory.imGateway.setDryRun(dryRun2);
|
|
13706
14819
|
});
|
|
14820
|
+
program.addCommand(NewCmdCache(factory));
|
|
13707
14821
|
program.addCommand(NewCmdAuth(factory));
|
|
13708
14822
|
program.addCommand(NewCmdConfig(factory));
|
|
13709
14823
|
program.addCommand(NewCmdUser(factory));
|