openzca 0.1.55 → 0.1.57
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/cli.js +466 -74
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2824,6 +2824,72 @@ import fs5 from "fs/promises";
|
|
|
2824
2824
|
import os3 from "os";
|
|
2825
2825
|
import path5 from "path";
|
|
2826
2826
|
import { promisify } from "util";
|
|
2827
|
+
|
|
2828
|
+
// src/lib/send-retry.ts
|
|
2829
|
+
var DEFAULT_SEND_RETRY_COUNT = 1;
|
|
2830
|
+
var DEFAULT_SEND_RETRY_DELAY_MS = 750;
|
|
2831
|
+
var RETRYABLE_SEND_ERROR_PATTERNS = [
|
|
2832
|
+
/retry limit/i,
|
|
2833
|
+
/\btimeout\b/i,
|
|
2834
|
+
/\btimed out\b/i,
|
|
2835
|
+
/\betimedout\b/i,
|
|
2836
|
+
/\beconnreset\b/i,
|
|
2837
|
+
/\besockettimedout\b/i,
|
|
2838
|
+
/\bsocket hang up\b/i,
|
|
2839
|
+
/\btemporar(?:y|ily)\b/i
|
|
2840
|
+
];
|
|
2841
|
+
function sleep(ms) {
|
|
2842
|
+
return new Promise((resolve) => {
|
|
2843
|
+
setTimeout(resolve, ms);
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
function parsePositiveIntEnv(value, fallback) {
|
|
2847
|
+
const raw = value?.trim();
|
|
2848
|
+
if (!raw) return fallback;
|
|
2849
|
+
if (raw === "0") return 0;
|
|
2850
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2851
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
2852
|
+
}
|
|
2853
|
+
function getSendRetryConfigFromEnv(env = process.env) {
|
|
2854
|
+
return {
|
|
2855
|
+
number: parsePositiveIntEnv(env.OPENZCA_SEND_RETRY_COUNT, DEFAULT_SEND_RETRY_COUNT),
|
|
2856
|
+
delayMs: parsePositiveIntEnv(env.OPENZCA_SEND_RETRY_DELAY_MS, DEFAULT_SEND_RETRY_DELAY_MS)
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
function isRetryableSendError(error) {
|
|
2860
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
2861
|
+
return RETRYABLE_SEND_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
2862
|
+
}
|
|
2863
|
+
function retryable(operation, options) {
|
|
2864
|
+
const maxRetries = Math.max(0, options?.number ?? DEFAULT_SEND_RETRY_COUNT);
|
|
2865
|
+
const delayMs = Math.max(0, options?.delayMs ?? DEFAULT_SEND_RETRY_DELAY_MS);
|
|
2866
|
+
const shouldRetry = options?.on ?? isRetryableSendError;
|
|
2867
|
+
return async (...args) => {
|
|
2868
|
+
let attempt = 0;
|
|
2869
|
+
while (true) {
|
|
2870
|
+
try {
|
|
2871
|
+
return await operation(...args);
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
attempt += 1;
|
|
2874
|
+
if (attempt > maxRetries || !shouldRetry(error)) {
|
|
2875
|
+
throw error;
|
|
2876
|
+
}
|
|
2877
|
+
await options?.onRetry?.({
|
|
2878
|
+
attempt,
|
|
2879
|
+
maxRetries,
|
|
2880
|
+
delayMs,
|
|
2881
|
+
error,
|
|
2882
|
+
args
|
|
2883
|
+
});
|
|
2884
|
+
if (delayMs > 0) {
|
|
2885
|
+
await sleep(delayMs);
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
// src/lib/video-send.ts
|
|
2827
2893
|
var execFileAsync = promisify(execFile);
|
|
2828
2894
|
function planVideoSendMode(params) {
|
|
2829
2895
|
const { files, ffmpegAvailable } = params;
|
|
@@ -2982,7 +3048,8 @@ async function sendNativeVideo(params) {
|
|
|
2982
3048
|
try {
|
|
2983
3049
|
const uploadedVideo = await params.api.uploadAttachment([params.videoPath], params.threadId, params.threadType);
|
|
2984
3050
|
const uploadedThumbnail = await params.api.uploadAttachment([thumbnailPath], params.threadId, params.threadType);
|
|
2985
|
-
|
|
3051
|
+
const sendVideo = retryable(params.api.sendVideo.bind(params.api), getSendRetryConfigFromEnv());
|
|
3052
|
+
return await sendVideo(
|
|
2986
3053
|
{
|
|
2987
3054
|
msg: params.message ?? "",
|
|
2988
3055
|
videoUrl: pickUploadedVideoUrl(uploadedVideo[0]),
|
|
@@ -3224,6 +3291,125 @@ function inferReplyMessageThreadId(params) {
|
|
|
3224
3291
|
return void 0;
|
|
3225
3292
|
}
|
|
3226
3293
|
|
|
3294
|
+
// src/lib/adaptive-batch.ts
|
|
3295
|
+
var RETRYABLE_LOOKUP_ERROR_PATTERNS = [
|
|
3296
|
+
/retry limit/i,
|
|
3297
|
+
/\brate limit/i,
|
|
3298
|
+
/\btoo many requests?\b/i,
|
|
3299
|
+
/\btimeout\b/i,
|
|
3300
|
+
/\btimed out\b/i,
|
|
3301
|
+
/\betimedout\b/i,
|
|
3302
|
+
/\beconnreset\b/i,
|
|
3303
|
+
/\besockettimedout\b/i,
|
|
3304
|
+
/\bsocket hang up\b/i,
|
|
3305
|
+
/\btemporar(?:y|ily)\b/i
|
|
3306
|
+
];
|
|
3307
|
+
var SPLITTABLE_LOOKUP_ERROR_PATTERNS = [
|
|
3308
|
+
/\binvalid param(?:eter)?s?\b/i,
|
|
3309
|
+
/\binvalid request\b/i,
|
|
3310
|
+
/\bbad request\b/i,
|
|
3311
|
+
/tham so khong hop le/i,
|
|
3312
|
+
/tham số không hợp lệ/i
|
|
3313
|
+
];
|
|
3314
|
+
function sleep2(ms) {
|
|
3315
|
+
return new Promise((resolve) => {
|
|
3316
|
+
setTimeout(resolve, ms);
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
function chunkKeys(keys, size) {
|
|
3320
|
+
const chunkSize = Math.max(1, Math.trunc(size) || 1);
|
|
3321
|
+
const chunks = [];
|
|
3322
|
+
for (let index = 0; index < keys.length; index += chunkSize) {
|
|
3323
|
+
chunks.push(keys.slice(index, index + chunkSize));
|
|
3324
|
+
}
|
|
3325
|
+
return chunks;
|
|
3326
|
+
}
|
|
3327
|
+
function toErrorText(error) {
|
|
3328
|
+
return error instanceof Error ? error.message : String(error);
|
|
3329
|
+
}
|
|
3330
|
+
function isRetryableLookupError(error) {
|
|
3331
|
+
const message = toErrorText(error);
|
|
3332
|
+
return RETRYABLE_LOOKUP_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
3333
|
+
}
|
|
3334
|
+
function isSplittableLookupError(error) {
|
|
3335
|
+
const message = toErrorText(error);
|
|
3336
|
+
return SPLITTABLE_LOOKUP_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
3337
|
+
}
|
|
3338
|
+
async function runAdaptiveBatch(keys, options) {
|
|
3339
|
+
const maxRetries = Math.max(0, options.maxRetries ?? 2);
|
|
3340
|
+
const initialDelayMs = Math.max(0, options.retryDelayMs ?? 400);
|
|
3341
|
+
const backoffMultiplier = Math.max(1, options.backoffMultiplier ?? 2);
|
|
3342
|
+
const shouldRetry = options.shouldRetry ?? isRetryableLookupError;
|
|
3343
|
+
const shouldSplit = options.shouldSplit ?? isSplittableLookupError;
|
|
3344
|
+
let attempt = 0;
|
|
3345
|
+
let delayMs = initialDelayMs;
|
|
3346
|
+
while (true) {
|
|
3347
|
+
try {
|
|
3348
|
+
return await options.fetchBatch(keys) ?? {};
|
|
3349
|
+
} catch (error) {
|
|
3350
|
+
if (keys.length > 1 && shouldSplit(error)) {
|
|
3351
|
+
throw error;
|
|
3352
|
+
}
|
|
3353
|
+
attempt += 1;
|
|
3354
|
+
if (attempt > maxRetries || !shouldRetry(error)) {
|
|
3355
|
+
throw error;
|
|
3356
|
+
}
|
|
3357
|
+
await options.onRetry?.({
|
|
3358
|
+
keys: [...keys],
|
|
3359
|
+
attempt,
|
|
3360
|
+
maxRetries,
|
|
3361
|
+
delayMs,
|
|
3362
|
+
error
|
|
3363
|
+
});
|
|
3364
|
+
if (delayMs > 0) {
|
|
3365
|
+
await sleep2(delayMs);
|
|
3366
|
+
}
|
|
3367
|
+
delayMs = Math.max(delayMs * backoffMultiplier, delayMs + 1);
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
async function fetchAdaptiveObjectBatches(keys, options) {
|
|
3372
|
+
const uniqueKeys = Array.from(new Set(keys.map((value) => value.trim()).filter(Boolean)));
|
|
3373
|
+
const pending = chunkKeys(uniqueKeys, options.initialBatchSize ?? 5);
|
|
3374
|
+
const values = /* @__PURE__ */ new Map();
|
|
3375
|
+
const errors = [];
|
|
3376
|
+
const shouldRetry = options.shouldRetry ?? isRetryableLookupError;
|
|
3377
|
+
const shouldSplit = options.shouldSplit ?? isSplittableLookupError;
|
|
3378
|
+
const continueOnItemError = options.continueOnItemError ?? true;
|
|
3379
|
+
const batchDelayMs = Math.max(0, options.batchDelayMs ?? 75);
|
|
3380
|
+
while (pending.length > 0) {
|
|
3381
|
+
const batch = pending.shift();
|
|
3382
|
+
if (!batch || batch.length === 0) {
|
|
3383
|
+
continue;
|
|
3384
|
+
}
|
|
3385
|
+
try {
|
|
3386
|
+
const result = await runAdaptiveBatch(batch, options);
|
|
3387
|
+
for (const key of batch) {
|
|
3388
|
+
const value = result[key];
|
|
3389
|
+
if (value !== void 0) {
|
|
3390
|
+
values.set(key, value);
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
} catch (error) {
|
|
3394
|
+
if (batch.length > 1 && (shouldSplit(error) || shouldRetry(error))) {
|
|
3395
|
+
pending.unshift(...chunkKeys(batch, Math.ceil(batch.length / 2)));
|
|
3396
|
+
continue;
|
|
3397
|
+
}
|
|
3398
|
+
if (!continueOnItemError || batch.length > 1) {
|
|
3399
|
+
throw error;
|
|
3400
|
+
}
|
|
3401
|
+
const itemError = { key: batch[0], error };
|
|
3402
|
+
errors.push(itemError);
|
|
3403
|
+
await options.onItemError?.(itemError);
|
|
3404
|
+
continue;
|
|
3405
|
+
}
|
|
3406
|
+
if (batchDelayMs > 0 && pending.length > 0) {
|
|
3407
|
+
await sleep2(batchDelayMs);
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
return { values, errors };
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3227
3413
|
// src/cli.ts
|
|
3228
3414
|
var require3 = createRequire2(import.meta.url);
|
|
3229
3415
|
var { version: PKG_VERSION } = require3("../package.json");
|
|
@@ -3446,6 +3632,63 @@ function parsePositiveIntFromUnknown(value) {
|
|
|
3446
3632
|
}
|
|
3447
3633
|
return null;
|
|
3448
3634
|
}
|
|
3635
|
+
function retrySendMethod(operation, command, metaBuilder) {
|
|
3636
|
+
const config = getSendRetryConfigFromEnv();
|
|
3637
|
+
return retryable(operation, {
|
|
3638
|
+
...config,
|
|
3639
|
+
onRetry: ({ attempt, maxRetries, delayMs, error, args }) => {
|
|
3640
|
+
writeDebugLine(
|
|
3641
|
+
"send.retry",
|
|
3642
|
+
{
|
|
3643
|
+
...metaBuilder(...args),
|
|
3644
|
+
attempt,
|
|
3645
|
+
maxRetries,
|
|
3646
|
+
delayMs,
|
|
3647
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3648
|
+
},
|
|
3649
|
+
command
|
|
3650
|
+
);
|
|
3651
|
+
}
|
|
3652
|
+
});
|
|
3653
|
+
}
|
|
3654
|
+
var LOOKUP_BATCH_SIZE = 5;
|
|
3655
|
+
var LOOKUP_RETRY_COUNT = 2;
|
|
3656
|
+
var LOOKUP_RETRY_DELAY_MS = 400;
|
|
3657
|
+
var LOOKUP_BATCH_DELAY_MS = 75;
|
|
3658
|
+
async function fetchGroupInfoRecords(api, groupIds) {
|
|
3659
|
+
const { values } = await fetchAdaptiveObjectBatches(groupIds, {
|
|
3660
|
+
fetchBatch: async (keys) => {
|
|
3661
|
+
const response = await api.getGroupInfo(keys);
|
|
3662
|
+
return response.gridInfoMap ?? {};
|
|
3663
|
+
},
|
|
3664
|
+
initialBatchSize: LOOKUP_BATCH_SIZE,
|
|
3665
|
+
maxRetries: LOOKUP_RETRY_COUNT,
|
|
3666
|
+
retryDelayMs: LOOKUP_RETRY_DELAY_MS,
|
|
3667
|
+
batchDelayMs: LOOKUP_BATCH_DELAY_MS
|
|
3668
|
+
});
|
|
3669
|
+
return values;
|
|
3670
|
+
}
|
|
3671
|
+
async function fetchGroupInfoRecord(api, groupId) {
|
|
3672
|
+
const groups = await fetchGroupInfoRecords(api, [groupId]);
|
|
3673
|
+
const group2 = groups.get(groupId);
|
|
3674
|
+
if (!group2) {
|
|
3675
|
+
throw new Error(`Group not found: ${groupId}`);
|
|
3676
|
+
}
|
|
3677
|
+
return group2;
|
|
3678
|
+
}
|
|
3679
|
+
async function fetchGroupMemberProfiles(api, memberIds) {
|
|
3680
|
+
const { values } = await fetchAdaptiveObjectBatches(memberIds, {
|
|
3681
|
+
fetchBatch: async (keys) => {
|
|
3682
|
+
const response = await api.getGroupMembersInfo(keys);
|
|
3683
|
+
return response.profiles ?? {};
|
|
3684
|
+
},
|
|
3685
|
+
initialBatchSize: LOOKUP_BATCH_SIZE,
|
|
3686
|
+
maxRetries: LOOKUP_RETRY_COUNT,
|
|
3687
|
+
retryDelayMs: LOOKUP_RETRY_DELAY_MS,
|
|
3688
|
+
batchDelayMs: LOOKUP_BATCH_DELAY_MS
|
|
3689
|
+
});
|
|
3690
|
+
return values;
|
|
3691
|
+
}
|
|
3449
3692
|
function isProcessAlive(pid) {
|
|
3450
3693
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
3451
3694
|
try {
|
|
@@ -3598,6 +3841,15 @@ async function startListenerIpcServer(api, profile, sessionId, command) {
|
|
|
3598
3841
|
}
|
|
3599
3842
|
const threadType = parsed.threadType === "group" ? ThreadType3.Group : ThreadType3.User;
|
|
3600
3843
|
const requestTimeoutMs = parsePositiveIntFromUnknown(parsed.uploadTimeoutMs) ?? uploadTimeoutMs;
|
|
3844
|
+
const sendMessage = retrySendMethod(
|
|
3845
|
+
api.sendMessage.bind(api),
|
|
3846
|
+
command,
|
|
3847
|
+
(_payload, threadId, threadTypeArg) => ({
|
|
3848
|
+
kind: "listen.ipc.upload",
|
|
3849
|
+
threadId,
|
|
3850
|
+
threadType: threadTypeArg === ThreadType3.Group ? "group" : "user"
|
|
3851
|
+
})
|
|
3852
|
+
);
|
|
3601
3853
|
writeDebugLine(
|
|
3602
3854
|
"listen.ipc.upload.start",
|
|
3603
3855
|
{
|
|
@@ -3613,7 +3865,7 @@ async function startListenerIpcServer(api, profile, sessionId, command) {
|
|
|
3613
3865
|
);
|
|
3614
3866
|
try {
|
|
3615
3867
|
const response = await withTimeout(
|
|
3616
|
-
|
|
3868
|
+
sendMessage(
|
|
3617
3869
|
{
|
|
3618
3870
|
msg: "",
|
|
3619
3871
|
attachments: parsed.attachments
|
|
@@ -3642,7 +3894,7 @@ async function startListenerIpcServer(api, profile, sessionId, command) {
|
|
|
3642
3894
|
command
|
|
3643
3895
|
);
|
|
3644
3896
|
} catch (error) {
|
|
3645
|
-
fail(parsed.requestId,
|
|
3897
|
+
fail(parsed.requestId, toErrorText2(error));
|
|
3646
3898
|
writeDebugLine(
|
|
3647
3899
|
"listen.ipc.upload.error",
|
|
3648
3900
|
{
|
|
@@ -3651,7 +3903,7 @@ async function startListenerIpcServer(api, profile, sessionId, command) {
|
|
|
3651
3903
|
requestId: parsed.requestId,
|
|
3652
3904
|
threadId: parsed.threadId,
|
|
3653
3905
|
threadType: parsed.threadType,
|
|
3654
|
-
message:
|
|
3906
|
+
message: toErrorText2(error)
|
|
3655
3907
|
},
|
|
3656
3908
|
command
|
|
3657
3909
|
);
|
|
@@ -3680,7 +3932,7 @@ async function startListenerIpcServer(api, profile, sessionId, command) {
|
|
|
3680
3932
|
{
|
|
3681
3933
|
profile,
|
|
3682
3934
|
sessionId,
|
|
3683
|
-
message:
|
|
3935
|
+
message: toErrorText2(error)
|
|
3684
3936
|
},
|
|
3685
3937
|
command
|
|
3686
3938
|
);
|
|
@@ -3692,7 +3944,7 @@ async function startListenerIpcServer(api, profile, sessionId, command) {
|
|
|
3692
3944
|
{
|
|
3693
3945
|
profile,
|
|
3694
3946
|
sessionId,
|
|
3695
|
-
message:
|
|
3947
|
+
message: toErrorText2(error)
|
|
3696
3948
|
},
|
|
3697
3949
|
command
|
|
3698
3950
|
);
|
|
@@ -3881,7 +4133,7 @@ async function resolveUploadThreadType(api, profile, threadId, groupFlag, comman
|
|
|
3881
4133
|
{
|
|
3882
4134
|
profile,
|
|
3883
4135
|
threadId,
|
|
3884
|
-
message:
|
|
4136
|
+
message: toErrorText2(error)
|
|
3885
4137
|
},
|
|
3886
4138
|
command
|
|
3887
4139
|
);
|
|
@@ -3906,7 +4158,7 @@ async function resolveUploadThreadType(api, profile, threadId, groupFlag, comman
|
|
|
3906
4158
|
{
|
|
3907
4159
|
profile,
|
|
3908
4160
|
threadId,
|
|
3909
|
-
message:
|
|
4161
|
+
message: toErrorText2(error)
|
|
3910
4162
|
},
|
|
3911
4163
|
command
|
|
3912
4164
|
);
|
|
@@ -4147,8 +4399,8 @@ async function persistOutgoingMessageBestEffort(params) {
|
|
|
4147
4399
|
});
|
|
4148
4400
|
}
|
|
4149
4401
|
}
|
|
4150
|
-
async function persistGroupMembersSnapshot(profile, groupId, api) {
|
|
4151
|
-
const rows = await listGroupMemberRows(api, groupId);
|
|
4402
|
+
async function persistGroupMembersSnapshot(profile, groupId, api, groupInfo) {
|
|
4403
|
+
const rows = await listGroupMemberRows(api, groupId, groupInfo);
|
|
4152
4404
|
const snapshotAtMs = Date.now();
|
|
4153
4405
|
for (const row of rows) {
|
|
4154
4406
|
await persistContact({
|
|
@@ -4353,7 +4605,15 @@ async function prepareDbGroupTarget(params) {
|
|
|
4353
4605
|
isHidden: params.hiddenIds.has(params.groupId),
|
|
4354
4606
|
rawJson: params.rawJson
|
|
4355
4607
|
});
|
|
4356
|
-
|
|
4608
|
+
if (params.hydrateMembers === false) {
|
|
4609
|
+
return {};
|
|
4610
|
+
}
|
|
4611
|
+
try {
|
|
4612
|
+
await persistGroupMembersSnapshot(params.profile, params.groupId, params.api, params.group);
|
|
4613
|
+
return {};
|
|
4614
|
+
} catch (error) {
|
|
4615
|
+
return { memberSnapshotError: toErrorText2(error) };
|
|
4616
|
+
}
|
|
4357
4617
|
}
|
|
4358
4618
|
function resolveContactDisplayName(params) {
|
|
4359
4619
|
return params.displayName?.trim() || params.zaloName?.trim() || params.fallbackTitle?.trim() || params.userId.trim() || void 0;
|
|
@@ -4455,18 +4715,17 @@ async function hydrateUnknownLiveGroup(params) {
|
|
|
4455
4715
|
}
|
|
4456
4716
|
}
|
|
4457
4717
|
if (group2 || title) {
|
|
4458
|
-
await
|
|
4718
|
+
await prepareDbGroupTarget({
|
|
4459
4719
|
profile: params.profile,
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4720
|
+
api: params.api,
|
|
4721
|
+
groupId: params.groupId,
|
|
4722
|
+
group: group2,
|
|
4463
4723
|
title,
|
|
4464
|
-
rawJson: group2 ? JSON.stringify(group2) : void 0
|
|
4724
|
+
rawJson: group2 ? JSON.stringify(group2) : void 0,
|
|
4725
|
+
pinnedIds: /* @__PURE__ */ new Set(),
|
|
4726
|
+
hiddenIds: /* @__PURE__ */ new Set(),
|
|
4727
|
+
hydrateMembers: Boolean(group2)
|
|
4465
4728
|
});
|
|
4466
|
-
try {
|
|
4467
|
-
await persistGroupMembersSnapshot(params.profile, params.groupId, params.api);
|
|
4468
|
-
} catch {
|
|
4469
|
-
}
|
|
4470
4729
|
return;
|
|
4471
4730
|
}
|
|
4472
4731
|
if (params.fallbackTitle?.trim()) {
|
|
@@ -4536,16 +4795,23 @@ async function syncDbGroupHistoryFull(params) {
|
|
|
4536
4795
|
pagesRequested = result.pagesRequested;
|
|
4537
4796
|
listenerImportedCount = await getStoredGroupMessageCount() - beforeCount;
|
|
4538
4797
|
} catch (error) {
|
|
4539
|
-
stopReason = `fallback_window:${
|
|
4798
|
+
stopReason = `fallback_window:${toErrorText2(error)}`;
|
|
4540
4799
|
completeness = "window";
|
|
4541
4800
|
}
|
|
4542
4801
|
const fallbackCount = 200;
|
|
4543
4802
|
params.progress?.(`merging recent group API window (${fallbackCount} per group)`);
|
|
4544
4803
|
const beforeApiCount = await getStoredGroupMessageCount();
|
|
4804
|
+
const topoffErrors = [];
|
|
4545
4805
|
for (const groupId of params.targetGroupIds) {
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4806
|
+
try {
|
|
4807
|
+
const messages = await fetchRecentGroupMessagesViaApi(params.api, groupId, fallbackCount);
|
|
4808
|
+
await persistMessages(messages);
|
|
4809
|
+
params.progress?.(`group ${groupId}: fetched ${messages.length} message(s) from group history API`);
|
|
4810
|
+
} catch (error) {
|
|
4811
|
+
const message = toErrorText2(error);
|
|
4812
|
+
topoffErrors.push({ groupId, error: message });
|
|
4813
|
+
params.progress?.(`group ${groupId}: group history API skipped (${message})`);
|
|
4814
|
+
}
|
|
4549
4815
|
}
|
|
4550
4816
|
const afterCount = await getStoredGroupMessageCount();
|
|
4551
4817
|
const apiAddedCount = afterCount - beforeApiCount;
|
|
@@ -4575,7 +4841,8 @@ async function syncDbGroupHistoryFull(params) {
|
|
|
4575
4841
|
imported,
|
|
4576
4842
|
completeness,
|
|
4577
4843
|
stopReason,
|
|
4578
|
-
pagesRequested
|
|
4844
|
+
pagesRequested,
|
|
4845
|
+
topoffErrors
|
|
4579
4846
|
});
|
|
4580
4847
|
}
|
|
4581
4848
|
async function syncDbFriendDirectory(params) {
|
|
@@ -4712,25 +4979,70 @@ async function runDbSync(params) {
|
|
|
4712
4979
|
});
|
|
4713
4980
|
}
|
|
4714
4981
|
if (params.mode === "all" || params.mode === "groups") {
|
|
4715
|
-
const groups = await
|
|
4982
|
+
const groups = await api.getAllGroups();
|
|
4983
|
+
const groupIds = Object.keys(groups.gridVerMap ?? {});
|
|
4716
4984
|
const targetGroupIds = /* @__PURE__ */ new Set();
|
|
4717
4985
|
const titleById = /* @__PURE__ */ new Map();
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4986
|
+
params.progress?.(`syncing group directory for ${groupIds.length} group(s)`);
|
|
4987
|
+
for (const groupId of groupIds) {
|
|
4988
|
+
let group2;
|
|
4989
|
+
let title;
|
|
4990
|
+
try {
|
|
4991
|
+
try {
|
|
4992
|
+
group2 = await fetchGroupInfoRecord(api, groupId);
|
|
4993
|
+
title = extractGroupTitle(group2);
|
|
4994
|
+
} catch (error) {
|
|
4995
|
+
const message = toErrorText2(error);
|
|
4996
|
+
params.progress?.(`group ${groupId}: metadata unavailable (${message}), continuing`);
|
|
4997
|
+
summary.syncState.push({
|
|
4998
|
+
kind: "group",
|
|
4999
|
+
groupId,
|
|
5000
|
+
status: "warning",
|
|
5001
|
+
stage: "metadata",
|
|
5002
|
+
error: message
|
|
5003
|
+
});
|
|
5004
|
+
}
|
|
5005
|
+
const { memberSnapshotError } = await prepareDbGroupTarget({
|
|
5006
|
+
profile,
|
|
5007
|
+
api,
|
|
5008
|
+
groupId,
|
|
5009
|
+
group: group2,
|
|
5010
|
+
title,
|
|
5011
|
+
rawJson: group2 ? JSON.stringify(group2) : void 0,
|
|
5012
|
+
pinnedIds,
|
|
5013
|
+
hiddenIds,
|
|
5014
|
+
hydrateMembers: Boolean(group2)
|
|
5015
|
+
});
|
|
5016
|
+
if (memberSnapshotError) {
|
|
5017
|
+
params.progress?.(`group ${groupId}: member snapshot unavailable (${memberSnapshotError}), continuing`);
|
|
5018
|
+
summary.syncState.push({
|
|
5019
|
+
kind: "group",
|
|
5020
|
+
groupId,
|
|
5021
|
+
status: "warning",
|
|
5022
|
+
stage: "members",
|
|
5023
|
+
error: memberSnapshotError
|
|
5024
|
+
});
|
|
5025
|
+
}
|
|
5026
|
+
targetGroupIds.add(groupId);
|
|
5027
|
+
titleById.set(groupId, title);
|
|
5028
|
+
} catch (error) {
|
|
5029
|
+
const message = toErrorText2(error);
|
|
5030
|
+
params.progress?.(`group ${groupId}: skipped (${message})`);
|
|
5031
|
+
await setSyncState({
|
|
5032
|
+
profile,
|
|
5033
|
+
scopeThreadId: groupId,
|
|
5034
|
+
threadType: "group",
|
|
5035
|
+
status: "error",
|
|
5036
|
+
error: message
|
|
5037
|
+
});
|
|
5038
|
+
summary.syncState.push({
|
|
5039
|
+
kind: "group",
|
|
5040
|
+
groupId,
|
|
5041
|
+
status: "error",
|
|
5042
|
+
stage: "prepare",
|
|
5043
|
+
error: message
|
|
5044
|
+
});
|
|
5045
|
+
}
|
|
4734
5046
|
}
|
|
4735
5047
|
await syncDbGroupHistoryFull({
|
|
4736
5048
|
profile,
|
|
@@ -4746,18 +5058,29 @@ async function runDbSync(params) {
|
|
|
4746
5058
|
if (!params.groupId) {
|
|
4747
5059
|
throw new Error("Missing group id for db sync group.");
|
|
4748
5060
|
}
|
|
4749
|
-
const
|
|
4750
|
-
const
|
|
4751
|
-
const
|
|
4752
|
-
await prepareDbGroupTarget({
|
|
5061
|
+
const group2 = await fetchGroupInfoRecord(api, params.groupId);
|
|
5062
|
+
const title = extractGroupTitle(group2);
|
|
5063
|
+
const { memberSnapshotError } = await prepareDbGroupTarget({
|
|
4753
5064
|
profile,
|
|
4754
5065
|
api,
|
|
4755
5066
|
groupId: params.groupId,
|
|
5067
|
+
group: group2,
|
|
4756
5068
|
title,
|
|
4757
5069
|
rawJson: group2 ? JSON.stringify(group2) : void 0,
|
|
4758
5070
|
pinnedIds,
|
|
4759
|
-
hiddenIds
|
|
5071
|
+
hiddenIds,
|
|
5072
|
+
hydrateMembers: Boolean(group2)
|
|
4760
5073
|
});
|
|
5074
|
+
if (memberSnapshotError) {
|
|
5075
|
+
params.progress?.(`group ${params.groupId}: member snapshot unavailable (${memberSnapshotError}), continuing`);
|
|
5076
|
+
summary.syncState.push({
|
|
5077
|
+
kind: "group",
|
|
5078
|
+
groupId: params.groupId,
|
|
5079
|
+
status: "warning",
|
|
5080
|
+
stage: "members",
|
|
5081
|
+
error: memberSnapshotError
|
|
5082
|
+
});
|
|
5083
|
+
}
|
|
4761
5084
|
await syncDbGroupHistoryFull({
|
|
4762
5085
|
profile,
|
|
4763
5086
|
api,
|
|
@@ -4816,8 +5139,8 @@ async function buildGroupsDetailed(api) {
|
|
|
4816
5139
|
const groups = await api.getAllGroups();
|
|
4817
5140
|
const ids = Object.keys(groups.gridVerMap ?? {});
|
|
4818
5141
|
if (ids.length === 0) return [];
|
|
4819
|
-
const info = await api
|
|
4820
|
-
return ids.map((id) => info.
|
|
5142
|
+
const info = await fetchGroupInfoRecords(api, ids);
|
|
5143
|
+
return ids.map((id) => info.get(id)).filter((item) => Boolean(item));
|
|
4821
5144
|
}
|
|
4822
5145
|
function normalizeGroupMemberId(value) {
|
|
4823
5146
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -4828,9 +5151,8 @@ function normalizeGroupMemberId(value) {
|
|
|
4828
5151
|
if (!trimmed) return "";
|
|
4829
5152
|
return trimmed.replace(/_\d+$/, "");
|
|
4830
5153
|
}
|
|
4831
|
-
async function listGroupMemberRows(api, groupId) {
|
|
4832
|
-
const
|
|
4833
|
-
const groupInfo = info.gridInfoMap[groupId];
|
|
5154
|
+
async function listGroupMemberRows(api, groupId, preloadedGroupInfo) {
|
|
5155
|
+
const groupInfo = preloadedGroupInfo ?? await fetchGroupInfoRecord(api, groupId);
|
|
4834
5156
|
if (!groupInfo) {
|
|
4835
5157
|
throw new Error(`Group not found: ${groupId}`);
|
|
4836
5158
|
}
|
|
@@ -4854,10 +5176,9 @@ async function listGroupMemberRows(api, groupId) {
|
|
|
4854
5176
|
...Array.from(currentMemberMap.keys())
|
|
4855
5177
|
])
|
|
4856
5178
|
);
|
|
4857
|
-
const
|
|
4858
|
-
const rawProfileMap = profiles.profiles;
|
|
5179
|
+
const profileLookup = ids.length > 0 ? await fetchGroupMemberProfiles(api, ids) : /* @__PURE__ */ new Map();
|
|
4859
5180
|
const profileMap = /* @__PURE__ */ new Map();
|
|
4860
|
-
for (const [key, profile] of
|
|
5181
|
+
for (const [key, profile] of profileLookup.entries()) {
|
|
4861
5182
|
if (!profile) continue;
|
|
4862
5183
|
const normalizedKey = normalizeGroupMemberId(key);
|
|
4863
5184
|
if (normalizedKey && !profileMap.has(normalizedKey)) {
|
|
@@ -4912,7 +5233,7 @@ function isListenerAlreadyStarted(error) {
|
|
|
4912
5233
|
if (!(error instanceof Error)) return false;
|
|
4913
5234
|
return /already started/i.test(error.message);
|
|
4914
5235
|
}
|
|
4915
|
-
function
|
|
5236
|
+
function toErrorText2(error) {
|
|
4916
5237
|
return error instanceof Error ? error.message : String(error);
|
|
4917
5238
|
}
|
|
4918
5239
|
var SHUTDOWN_CALLBACKS = /* @__PURE__ */ new Set();
|
|
@@ -4950,7 +5271,7 @@ async function runShutdownCallbacks(signal) {
|
|
|
4950
5271
|
"process.signal.callback_error",
|
|
4951
5272
|
{
|
|
4952
5273
|
signal,
|
|
4953
|
-
message:
|
|
5274
|
+
message: toErrorText2(error)
|
|
4954
5275
|
},
|
|
4955
5276
|
void 0
|
|
4956
5277
|
);
|
|
@@ -5033,7 +5354,7 @@ async function withUploadListener(api, command, task) {
|
|
|
5033
5354
|
writeDebugLine(
|
|
5034
5355
|
"msg.upload.listener.error",
|
|
5035
5356
|
{
|
|
5036
|
-
message:
|
|
5357
|
+
message: toErrorText2(error)
|
|
5037
5358
|
},
|
|
5038
5359
|
command
|
|
5039
5360
|
);
|
|
@@ -5075,7 +5396,7 @@ async function withUploadListener(api, command, task) {
|
|
|
5075
5396
|
finish();
|
|
5076
5397
|
};
|
|
5077
5398
|
const onConnectError = (error) => {
|
|
5078
|
-
finish(new Error(`Upload listener connection error: ${
|
|
5399
|
+
finish(new Error(`Upload listener connection error: ${toErrorText2(error)}`));
|
|
5079
5400
|
};
|
|
5080
5401
|
const onConnectClosed = (code, reason) => {
|
|
5081
5402
|
finish(
|
|
@@ -5713,7 +6034,7 @@ async function parseCredentialFile(filePath) {
|
|
|
5713
6034
|
language: parsed.language
|
|
5714
6035
|
};
|
|
5715
6036
|
}
|
|
5716
|
-
function
|
|
6037
|
+
function sleep3(ms) {
|
|
5717
6038
|
return new Promise((resolve) => {
|
|
5718
6039
|
setTimeout(resolve, ms);
|
|
5719
6040
|
});
|
|
@@ -5728,7 +6049,7 @@ async function waitForFileContent(filePath, timeoutMs) {
|
|
|
5728
6049
|
}
|
|
5729
6050
|
} catch {
|
|
5730
6051
|
}
|
|
5731
|
-
await
|
|
6052
|
+
await sleep3(150);
|
|
5732
6053
|
}
|
|
5733
6054
|
throw new Error(`Timed out waiting for QR image file: ${filePath}`);
|
|
5734
6055
|
}
|
|
@@ -7081,6 +7402,15 @@ msg.command("send <threadId> <message>").option("-g, --group", "Send to group").
|
|
|
7081
7402
|
)
|
|
7082
7403
|
});
|
|
7083
7404
|
const payloadChunks = deliveryPlan.chunks;
|
|
7405
|
+
const sendMessage = retrySendMethod(
|
|
7406
|
+
api.sendMessage.bind(api),
|
|
7407
|
+
command,
|
|
7408
|
+
(_payload, targetThreadId, targetThreadType) => ({
|
|
7409
|
+
kind: "msg.send",
|
|
7410
|
+
threadId: targetThreadId,
|
|
7411
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user"
|
|
7412
|
+
})
|
|
7413
|
+
);
|
|
7084
7414
|
const responses = [];
|
|
7085
7415
|
const sentPayloads = [];
|
|
7086
7416
|
for (let index = 0; index < payloadChunks.length; index += 1) {
|
|
@@ -7090,7 +7420,7 @@ msg.command("send <threadId> <message>").option("-g, --group", "Send to group").
|
|
|
7090
7420
|
quote
|
|
7091
7421
|
} : chunk;
|
|
7092
7422
|
sentPayloads.push(chunkPayload);
|
|
7093
|
-
responses.push(await
|
|
7423
|
+
responses.push(await sendMessage(chunkPayload, threadId, threadType));
|
|
7094
7424
|
}
|
|
7095
7425
|
const response = responses.length === 1 ? responses[0] : {
|
|
7096
7426
|
chunked: true,
|
|
@@ -7155,6 +7485,16 @@ msg.command("image <threadId> [file]").option("-u, --url <url>", "Image URL (rep
|
|
|
7155
7485
|
wrapAction(
|
|
7156
7486
|
async (threadId, file, opts, command) => {
|
|
7157
7487
|
const { api, profile } = await requireApi(command);
|
|
7488
|
+
const sendMessage = retrySendMethod(
|
|
7489
|
+
api.sendMessage.bind(api),
|
|
7490
|
+
command,
|
|
7491
|
+
(payload, targetThreadId, targetThreadType) => ({
|
|
7492
|
+
kind: "msg.image",
|
|
7493
|
+
threadId: targetThreadId,
|
|
7494
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user",
|
|
7495
|
+
attachmentCount: payload && typeof payload === "object" && Array.isArray(payload.attachments) ? payload.attachments.length : void 0
|
|
7496
|
+
})
|
|
7497
|
+
);
|
|
7158
7498
|
const normalizedFile = file ? normalizeMediaInput(file) : void 0;
|
|
7159
7499
|
const files = [normalizedFile, ...normalizeInputList(opts.url)].filter(Boolean);
|
|
7160
7500
|
const urlInputs = files.filter((entry) => isHttpUrl(entry));
|
|
@@ -7176,7 +7516,7 @@ msg.command("image <threadId> [file]").option("-u, --url <url>", "Image URL (rep
|
|
|
7176
7516
|
throw new Error("Provide at least one image file or --url.");
|
|
7177
7517
|
}
|
|
7178
7518
|
await assertFilesExist(attachments);
|
|
7179
|
-
const response = await
|
|
7519
|
+
const response = await sendMessage(
|
|
7180
7520
|
{
|
|
7181
7521
|
msg: opts.message ?? "",
|
|
7182
7522
|
attachments
|
|
@@ -7218,6 +7558,16 @@ msg.command("video <threadId> [file]").option("-u, --url <url>", "Video URL (rep
|
|
|
7218
7558
|
async (threadId, file, opts, command) => {
|
|
7219
7559
|
const { api, profile } = await requireApi(command);
|
|
7220
7560
|
const threadType = asThreadType(opts.group);
|
|
7561
|
+
const sendMessage = retrySendMethod(
|
|
7562
|
+
api.sendMessage.bind(api),
|
|
7563
|
+
command,
|
|
7564
|
+
(payload, targetThreadId, targetThreadType) => ({
|
|
7565
|
+
kind: "msg.video.fallback",
|
|
7566
|
+
threadId: targetThreadId,
|
|
7567
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user",
|
|
7568
|
+
attachmentCount: payload && typeof payload === "object" && Array.isArray(payload.attachments) ? payload.attachments.length : void 0
|
|
7569
|
+
})
|
|
7570
|
+
);
|
|
7221
7571
|
const normalizedFile = file ? normalizeMediaInput(file) : void 0;
|
|
7222
7572
|
const files = [normalizedFile, ...normalizeInputList(opts.url)].filter(Boolean);
|
|
7223
7573
|
const urlInputs = files.filter((entry) => isHttpUrl(entry));
|
|
@@ -7339,7 +7689,7 @@ msg.command("video <threadId> [file]").option("-u, --url <url>", "Video URL (rep
|
|
|
7339
7689
|
const response = await withUploadListener(
|
|
7340
7690
|
api,
|
|
7341
7691
|
command,
|
|
7342
|
-
async () =>
|
|
7692
|
+
async () => sendMessage(
|
|
7343
7693
|
{
|
|
7344
7694
|
msg: opts.message ?? "",
|
|
7345
7695
|
attachments
|
|
@@ -7383,6 +7733,15 @@ msg.command("voice <threadId> [file]").option("-u, --url <url>", "Voice URL (rep
|
|
|
7383
7733
|
async (threadId, file, opts, command) => {
|
|
7384
7734
|
const { api, profile } = await requireApi(command);
|
|
7385
7735
|
const type = asThreadType(opts.group);
|
|
7736
|
+
const sendVoice = retrySendMethod(
|
|
7737
|
+
api.sendVoice.bind(api),
|
|
7738
|
+
command,
|
|
7739
|
+
(_payload, targetThreadId, targetThreadType) => ({
|
|
7740
|
+
kind: "msg.voice",
|
|
7741
|
+
threadId: targetThreadId,
|
|
7742
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user"
|
|
7743
|
+
})
|
|
7744
|
+
);
|
|
7386
7745
|
const normalizedFile = file ? normalizeMediaInput(file) : void 0;
|
|
7387
7746
|
const files = [normalizedFile, ...normalizeInputList(opts.url)].filter(Boolean);
|
|
7388
7747
|
if (files.length === 0) {
|
|
@@ -7408,7 +7767,7 @@ msg.command("voice <threadId> [file]").option("-u, --url <url>", "Voice URL (rep
|
|
|
7408
7767
|
const uploaded = await api.uploadAttachment(attachments, threadId, type);
|
|
7409
7768
|
for (const item of uploaded) {
|
|
7410
7769
|
if (item.fileType === "others" || item.fileType === "video") {
|
|
7411
|
-
results.push(await
|
|
7770
|
+
results.push(await sendVoice({ voiceUrl: item.fileUrl }, threadId, type));
|
|
7412
7771
|
}
|
|
7413
7772
|
}
|
|
7414
7773
|
if (results.length === 0) {
|
|
@@ -7466,7 +7825,16 @@ msg.command("sticker <threadId> <stickerId>").option("-g, --group", "Send to gro
|
|
|
7466
7825
|
msg.command("link <threadId> <url>").option("-g, --group", "Send to group").description("Send link").action(
|
|
7467
7826
|
wrapAction(async (threadId, url, opts, command) => {
|
|
7468
7827
|
const { api, profile } = await requireApi(command);
|
|
7469
|
-
const
|
|
7828
|
+
const sendLink = retrySendMethod(
|
|
7829
|
+
api.sendLink.bind(api),
|
|
7830
|
+
command,
|
|
7831
|
+
(_payload, targetThreadId, targetThreadType) => ({
|
|
7832
|
+
kind: "msg.link",
|
|
7833
|
+
threadId: targetThreadId,
|
|
7834
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user"
|
|
7835
|
+
})
|
|
7836
|
+
);
|
|
7837
|
+
const response = await sendLink({ link: url }, threadId, asThreadType(opts.group));
|
|
7470
7838
|
output(response, false);
|
|
7471
7839
|
if (await shouldWriteToDb(profile)) {
|
|
7472
7840
|
scheduleDbWrite(profile, command, "msg.link.db.persist_error", async () => {
|
|
@@ -7578,6 +7946,15 @@ msg.command("edit <msgId> <cliMsgId> <threadId> <message>").option("-g, --group"
|
|
|
7578
7946
|
async (msgId, cliMsgId, threadId, message, opts, command) => {
|
|
7579
7947
|
const { api } = await requireApi(command);
|
|
7580
7948
|
const type = asThreadType(opts.group);
|
|
7949
|
+
const sendMessage = retrySendMethod(
|
|
7950
|
+
api.sendMessage.bind(api),
|
|
7951
|
+
command,
|
|
7952
|
+
(_payload, targetThreadId, targetThreadType) => ({
|
|
7953
|
+
kind: "msg.edit.resend",
|
|
7954
|
+
threadId: targetThreadId,
|
|
7955
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user"
|
|
7956
|
+
})
|
|
7957
|
+
);
|
|
7581
7958
|
const undoResponse = await api.undo(
|
|
7582
7959
|
{
|
|
7583
7960
|
msgId,
|
|
@@ -7586,7 +7963,7 @@ msg.command("edit <msgId> <cliMsgId> <threadId> <message>").option("-g, --group"
|
|
|
7586
7963
|
threadId,
|
|
7587
7964
|
type
|
|
7588
7965
|
);
|
|
7589
|
-
const sendResponse = await
|
|
7966
|
+
const sendResponse = await sendMessage(message, threadId, type);
|
|
7590
7967
|
output(
|
|
7591
7968
|
{
|
|
7592
7969
|
mode: "undo+send",
|
|
@@ -7603,6 +7980,16 @@ msg.command("upload <arg1> [arg2]").option("-u, --url <url>", "File URL (repeata
|
|
|
7603
7980
|
wrapAction(
|
|
7604
7981
|
async (arg1, arg2, opts, command) => {
|
|
7605
7982
|
const { api, profile } = await requireApi(command);
|
|
7983
|
+
const sendMessage = retrySendMethod(
|
|
7984
|
+
api.sendMessage.bind(api),
|
|
7985
|
+
command,
|
|
7986
|
+
(payload, targetThreadId, targetThreadType) => ({
|
|
7987
|
+
kind: "msg.upload",
|
|
7988
|
+
threadId: targetThreadId,
|
|
7989
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user",
|
|
7990
|
+
attachmentCount: payload && typeof payload === "object" && Array.isArray(payload.attachments) ? payload.attachments.length : void 0
|
|
7991
|
+
})
|
|
7992
|
+
);
|
|
7606
7993
|
const inputs = normalizeInputList(opts.url);
|
|
7607
7994
|
const urlInputs = inputs.filter((entry) => isHttpUrl(entry));
|
|
7608
7995
|
const localInputs = inputs.filter((entry) => !isHttpUrl(entry));
|
|
@@ -7678,7 +8065,7 @@ msg.command("upload <arg1> [arg2]").option("-u, --url <url>", "File URL (repeata
|
|
|
7678
8065
|
const response = await withUploadListener(
|
|
7679
8066
|
api,
|
|
7680
8067
|
command,
|
|
7681
|
-
async () =>
|
|
8068
|
+
async () => sendMessage(
|
|
7682
8069
|
{
|
|
7683
8070
|
msg: "",
|
|
7684
8071
|
attachments
|
|
@@ -8775,12 +9162,17 @@ ${replyContextText}` : replyContextText;
|
|
|
8775
9162
|
}
|
|
8776
9163
|
await emitWebhook(payload);
|
|
8777
9164
|
if (opts.echo && rawText.trim().length > 0) {
|
|
9165
|
+
const sendMessage = retrySendMethod(
|
|
9166
|
+
api.sendMessage.bind(api),
|
|
9167
|
+
command,
|
|
9168
|
+
(_sendPayload, targetThreadId, targetThreadType) => ({
|
|
9169
|
+
kind: "listen.echo",
|
|
9170
|
+
threadId: targetThreadId,
|
|
9171
|
+
threadType: targetThreadType === ThreadType3.Group ? "group" : "user"
|
|
9172
|
+
})
|
|
9173
|
+
);
|
|
8778
9174
|
try {
|
|
8779
|
-
await
|
|
8780
|
-
{ msg: processedText },
|
|
8781
|
-
message.threadId,
|
|
8782
|
-
message.type
|
|
8783
|
-
);
|
|
9175
|
+
await sendMessage({ msg: processedText }, message.threadId, message.type);
|
|
8784
9176
|
} catch (error) {
|
|
8785
9177
|
console.error(
|
|
8786
9178
|
`Echo failed: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -8886,7 +9278,7 @@ ${replyContextText}` : replyContextText;
|
|
|
8886
9278
|
code,
|
|
8887
9279
|
reason: reason || void 0,
|
|
8888
9280
|
delayMs: keepAliveRestartDelayMs,
|
|
8889
|
-
message:
|
|
9281
|
+
message: toErrorText2(error),
|
|
8890
9282
|
sessionId
|
|
8891
9283
|
},
|
|
8892
9284
|
command
|