karin-plugin-qgroup-file2openlist 0.0.30 → 0.0.31

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.
@@ -13,7 +13,7 @@
13
13
  "flat": false,
14
14
  "urlConcurrency": 3,
15
15
  "transferConcurrency": 3,
16
- "fileTimeoutSec": 3000,
16
+ "fileTimeoutSec": 30000,
17
17
  "retryTimes": 2,
18
18
  "retryDelayMs": 1500,
19
19
  "progressReportEvery": 10,
@@ -6,7 +6,7 @@ import "../chunk-PBBZ5KAD.js";
6
6
  import "../chunk-QB3GSENE.js";
7
7
  import {
8
8
  handleGroupFileUploadedAutoBackup
9
- } from "../chunk-KB5N77CR.js";
9
+ } from "../chunk-2Z6KEH2L.js";
10
10
  import "../chunk-YQPHJTEB.js";
11
11
  import {
12
12
  backupOpenListToOpenListCore,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  exportGroupFilesToDisk,
3
3
  syncGroupFilesToOpenListCore
4
- } from "../chunk-KB5N77CR.js";
4
+ } from "../chunk-2Z6KEH2L.js";
5
5
  import "../chunk-YQPHJTEB.js";
6
6
  import {
7
7
  formatErrorMessage,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  handleGroupSyncConfigCommand
3
- } from "../chunk-CE6KVBH4.js";
4
- import "../chunk-KB5N77CR.js";
3
+ } from "../chunk-DR4WWLFE.js";
4
+ import "../chunk-2Z6KEH2L.js";
5
5
  import "../chunk-YQPHJTEB.js";
6
6
  import "../chunk-NWUWEQLH.js";
7
7
  import "../chunk-DA4U55JC.js";
@@ -3,8 +3,8 @@ import {
3
3
  } from "../chunk-VVJJTF2N.js";
4
4
  import {
5
5
  runNightlyGroupBackup
6
- } from "../chunk-CE6KVBH4.js";
7
- import "../chunk-KB5N77CR.js";
6
+ } from "../chunk-DR4WWLFE.js";
7
+ import "../chunk-2Z6KEH2L.js";
8
8
  import "../chunk-YQPHJTEB.js";
9
9
  import "../chunk-NWUWEQLH.js";
10
10
  import {
@@ -38,7 +38,7 @@ ${randomMsg}`;
38
38
  /** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
39
39
  permission: "all"
40
40
  });
41
- var forwardMessage = karin.command(/^#测试转发$/, async (e) => {
41
+ var forwardMessage = karin.command(/^#测dscvdf试转发$/, async (e) => {
42
42
  if (!e.isPrivate) return false;
43
43
  const message = [
44
44
  segment.text("\u{1F31F} \u8FD9\u662F\u8F6C\u53D1\u7684\u7B2C\u4E00\u6761\u6D88\u606F \u{1F31F}"),
@@ -58,7 +58,7 @@ var forwardMessage = karin.command(/^#测试转发$/, async (e) => {
58
58
  /** 谁可以触发这个插件 'all' | 'master' | 'admin' | 'group.owner' | 'group.admin' */
59
59
  permission: "all"
60
60
  });
61
- var randomEmoji = karin.command(/^#随机表情$/, async (e) => {
61
+ var randomEmoji = karin.command(/^#随机sdccasc表情$/, async (e) => {
62
62
  if (!e.isPrivate) return false;
63
63
  const emojiUrls = [
64
64
  "https://i.imgur.com/XaUdU2C.gif",
@@ -80,7 +80,7 @@ var randomEmoji = karin.command(/^#随机表情$/, async (e) => {
80
80
  name: "\u968F\u673A\u8868\u60C5\u5305demo",
81
81
  permission: "all"
82
82
  });
83
- var dailyQuote = karin.command(/^#每日一言$/, async (e) => {
83
+ var dailyQuote = karin.command(/^#每日ds cas一言$/, async (e) => {
84
84
  if (!e.isPrivate) return false;
85
85
  const quotes = [
86
86
  "\u4ECA\u5929\u4E5F\u662F\u5145\u6EE1\u5E0C\u671B\u7684\u4E00\u5929\uFF01\u52A0\u6CB9\uFF01\u2728",
@@ -103,7 +103,7 @@ var dailyQuote = karin.command(/^#每日一言$/, async (e) => {
103
103
  name: "\u6BCF\u65E5\u4E00\u8A00demo",
104
104
  permission: "all"
105
105
  });
106
- var weatherForecast = karin.command(/^#今日天气$/, async (e) => {
106
+ var weatherForecast = karin.command(/^#今日 sdvsdv天气$/, async (e) => {
107
107
  if (!e.isPrivate) return false;
108
108
  const weathers = [
109
109
  "\u2600\uFE0F \u6674\u5929\uFF0C\u6E29\u5EA625\xB0C\uFF0C\u9002\u5408\u51FA\u95E8\u73A9\u800D~",
@@ -103,7 +103,13 @@ var getGroupFileListCompat = async (bot, groupId, folderId) => {
103
103
  }
104
104
  throw new Error("\u5F53\u524D\u9002\u914D\u5668\u4E0D\u652F\u6301\u83B7\u53D6\u7FA4\u6587\u4EF6\u5217\u8868\uFF08getGroupFileList / getGroupRootFiles / getGroupFilesByFolder \u5747\u4E0D\u53EF\u7528\uFF09");
105
105
  };
106
- var resolveGroupFileUrl = async (bot, contact, groupId, file) => {
106
+ var isRetryableResolveGroupFileUrlError = (error) => {
107
+ const msg = formatErrorMessage(error);
108
+ if (/缺少 fileId|群号无法转换为 number|未找到可用接口/.test(msg)) return false;
109
+ if (/不存在|not found|file not found/i.test(msg)) return false;
110
+ return /请求错误|sendApi|限流|频繁|风控|rate|limit|429|Too Many Requests|getFileUrl|nc_getFile|getGroupFileUrl|超时|timeout|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|UND_ERR|socket hang up/i.test(msg);
111
+ };
112
+ var resolveGroupFileUrlOnce = async (bot, contact, groupId, file) => {
107
113
  if (!file.fileId) throw new Error("\u7F3A\u5C11 fileId");
108
114
  const reasons = [];
109
115
  if (typeof bot?.getFileUrl === "function") {
@@ -160,6 +166,27 @@ var resolveGroupFileUrl = async (bot, contact, groupId, file) => {
160
166
  }
161
167
  throw new Error(reasons[0] ?? "\u65E0\u6CD5\u83B7\u53D6\u4E0B\u8F7D URL\uFF08\u672A\u627E\u5230\u53EF\u7528\u63A5\u53E3\uFF09");
162
168
  };
169
+ var resolveGroupFileUrl = async (bot, contact, groupId, file, options) => {
170
+ const retries = Math.max(0, Math.floor(options?.retries ?? 2));
171
+ const delayMs = Math.max(0, Math.floor(options?.delayMs ?? 1200));
172
+ const maxDelayMs = Math.max(delayMs, Math.floor(options?.maxDelayMs ?? 15e3));
173
+ let lastError;
174
+ for (let attempt = 0; attempt <= retries; attempt++) {
175
+ try {
176
+ return await resolveGroupFileUrlOnce(bot, contact, groupId, file);
177
+ } catch (error) {
178
+ lastError = error;
179
+ if (attempt >= retries) break;
180
+ if (!isRetryableResolveGroupFileUrlError(error)) break;
181
+ const backoff = delayMs * Math.pow(2, attempt);
182
+ const capped = Math.min(maxDelayMs, backoff);
183
+ const jittered = Math.floor(capped * (0.8 + Math.random() * 0.4));
184
+ if (jittered > 0) await sleep(jittered);
185
+ }
186
+ }
187
+ if (lastError) throw lastError;
188
+ throw new Error("\u65E0\u6CD5\u83B7\u53D6\u4E0B\u8F7D URL");
189
+ };
163
190
  var collectAllGroupFiles = async (bot, groupId, startFolderId) => {
164
191
  const files = [];
165
192
  const visitedFolders = /* @__PURE__ */ new Set();
@@ -366,6 +393,34 @@ var MAX_FILE_TIMEOUT_SEC = 3e3;
366
393
  var MIN_FILE_TIMEOUT_SEC = 10;
367
394
  var DEFAULT_PROGRESS_REPORT_EVERY = 10;
368
395
  var MAX_TRANSFER_CONCURRENCY = 5;
396
+ var createAsyncLimiter = (concurrency) => {
397
+ const limit = Math.max(1, Math.floor(concurrency) || 1);
398
+ let active = 0;
399
+ const queue = [];
400
+ const acquire = async () => {
401
+ if (active < limit) {
402
+ active++;
403
+ return;
404
+ }
405
+ await new Promise((resolve) => queue.push(resolve));
406
+ };
407
+ const release = () => {
408
+ const next = queue.shift();
409
+ if (next) {
410
+ next();
411
+ return;
412
+ }
413
+ active = Math.max(0, active - 1);
414
+ };
415
+ return async (fn) => {
416
+ await acquire();
417
+ try {
418
+ return await fn();
419
+ } finally {
420
+ release();
421
+ }
422
+ };
423
+ };
369
424
  var buildRemotePathForItem = (item, targetDir, flat) => {
370
425
  const relativeParts = (flat ? [item.name] : item.path.split("/")).filter(Boolean).map(safePathSegment);
371
426
  return normalizePosixPath(path2.posix.join(targetDir, ...relativeParts));
@@ -483,24 +538,38 @@ var syncGroupFilesToOpenListCore = async (params) => {
483
538
  report && await report(`\u6CA1\u6709\u9700\u8981\u540C\u6B65\u7684\u6587\u4EF6\uFF08\u589E\u91CF\u6A21\u5F0F\u5DF2\u8DF3\u8FC7 ${skipped} \u6761\uFF09\u3002`);
484
539
  return { total: limitedList.length, skipped, urlOk: 0, ok: 0, fail: 0 };
485
540
  }
541
+ report && await report("\u5C06\u6309\u9700\u89E3\u6790\u4E0B\u8F7D URL\uFF08\u907F\u514D\u6279\u91CF\u9884\u53D6\u5BFC\u81F4 URL \u8FC7\u671F/\u89E6\u53D1\u9650\u6D41\uFF09");
486
542
  const urlErrors = [];
487
- await runWithConcurrency(needSync, Math.max(1, Math.floor(urlConcurrency) || 1), async ({ item }) => {
488
- try {
489
- item.url = await resolveGroupFileUrl(bot, groupContact, groupId, item);
490
- } catch (error) {
491
- urlErrors.push({ fileId: item.fileId, path: item.path, message: formatErrorMessage(error) });
492
- }
493
- });
494
- const withUrl = needSync.filter(({ item }) => typeof item.url === "string" && item.url).map((v) => v);
495
- report && await report(`URL\u83B7\u53D6\u5B8C\u6210\uFF1A\u6210\u529F ${withUrl.length} / \u5931\u8D25 ${needSync.length - withUrl.length}\uFF08\u589E\u91CF\u8DF3\u8FC7 ${skipped}\uFF09`);
496
- if (!withUrl.length) {
497
- report && await report("\u6CA1\u6709\u53EF\u7528\u7684\u4E0B\u8F7D URL\uFF0C\u65E0\u6CD5\u540C\u6B65\u5230 OpenList");
498
- return { total: limitedList.length, skipped, urlOk: 0, ok: 0, fail: 0 };
499
- }
500
543
  const syncErrors = [];
544
+ const urlOkSet = /* @__PURE__ */ new Set();
501
545
  let okCount = 0;
502
546
  const shouldRefreshUrl = (message) => {
503
- return /403|URL已过期|url已过期|url可能已失效|需要重新获取|下载超时/.test(message);
547
+ return /401|403|404|URL已过期|url已过期|url可能已失效|需要重新获取|下载超时/.test(message);
548
+ };
549
+ const resolveUrlWithLimit = createAsyncLimiter(Math.max(1, Math.floor(urlConcurrency) || 1));
550
+ const urlResolveOptions = {
551
+ retries: 2,
552
+ delayMs: retryDelayMs > 0 ? Math.floor(retryDelayMs) : void 0,
553
+ maxDelayMs: 2e4
554
+ };
555
+ const ensureItemUrl = async (item, remotePath) => {
556
+ const existing = typeof item.url === "string" ? item.url.trim() : "";
557
+ if (existing) {
558
+ urlOkSet.add(remotePath);
559
+ return existing;
560
+ }
561
+ try {
562
+ const url = await resolveUrlWithLimit(
563
+ () => resolveGroupFileUrl(bot, groupContact, groupId, item, urlResolveOptions)
564
+ );
565
+ const cleaned = typeof url === "string" ? url.trim() : "";
566
+ if (!cleaned) throw new Error("\u8FD4\u56DE\u7A7A URL");
567
+ item.url = cleaned;
568
+ urlOkSet.add(remotePath);
569
+ return cleaned;
570
+ } catch (error) {
571
+ throw new Error(`URL\u83B7\u53D6\u5931\u8D25: ${formatErrorMessage(error)}`);
572
+ }
504
573
  };
505
574
  const transferOne = async (sourceUrl, targetUrl, expectedSize) => {
506
575
  await downloadAndUploadByWebDav({
@@ -516,7 +585,7 @@ var syncGroupFilesToOpenListCore = async (params) => {
516
585
  const transferInitial = Math.min(MAX_TRANSFER_CONCURRENCY, Math.max(1, Math.floor(transferConcurrency) || 1));
517
586
  const adaptiveTransfer = effectiveRateLimitBytesPerSec <= 0;
518
587
  const transferFn = async ({ item, remotePath }, index) => {
519
- logger2.info(`[\u7FA4\u6587\u4EF6\u540C\u6B65][${groupId}] \u540C\u6B65\u4E2D(${index + 1}/${withUrl.length}): ${item.path}`);
588
+ logger2.info(`[\u7FA4\u6587\u4EF6\u540C\u6B65][${groupId}] \u540C\u6B65\u4E2D(${index + 1}/${needSync.length}): ${item.path}`);
520
589
  const remoteDir = normalizePosixPath(path2.posix.dirname(remotePath));
521
590
  const targetUrl = `${davBaseUrl}${encodePathForUrl(remotePath)}`;
522
591
  let lastError;
@@ -525,8 +594,7 @@ var syncGroupFilesToOpenListCore = async (params) => {
525
594
  for (let attempt = 1; attempt <= attempts; attempt++) {
526
595
  try {
527
596
  await dirEnsurer.ensureDir(remoteDir);
528
- const currentUrl = item.url;
529
- if (!currentUrl) throw new Error("\u7F3A\u5C11\u4E0B\u8F7D URL");
597
+ const currentUrl = await ensureItemUrl(item, remotePath);
530
598
  await transferOne(currentUrl, targetUrl, item.size);
531
599
  okCount++;
532
600
  succeeded = true;
@@ -545,10 +613,7 @@ var syncGroupFilesToOpenListCore = async (params) => {
545
613
  const msg = formatErrorMessage(error);
546
614
  if (attempt < attempts) {
547
615
  if (shouldRefreshUrl(msg)) {
548
- try {
549
- item.url = await resolveGroupFileUrl(bot, groupContact, groupId, item);
550
- } catch {
551
- }
616
+ item.url = void 0;
552
617
  }
553
618
  const delay = Math.max(0, Math.floor(retryDelayMs) || 0) * Math.pow(2, attempt - 1);
554
619
  if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
@@ -557,19 +622,24 @@ var syncGroupFilesToOpenListCore = async (params) => {
557
622
  }
558
623
  }
559
624
  if (!succeeded && lastError) {
560
- syncErrors.push({
561
- path: item.path,
562
- fileId: item.fileId,
563
- message: formatErrorMessage(lastError)
564
- });
625
+ const message = formatErrorMessage(lastError);
626
+ if (message.startsWith("URL\u83B7\u53D6\u5931\u8D25:")) {
627
+ urlErrors.push({ fileId: item.fileId, path: item.path, message });
628
+ } else {
629
+ syncErrors.push({
630
+ path: item.path,
631
+ fileId: item.fileId,
632
+ message
633
+ });
634
+ }
565
635
  }
566
636
  if (safeProgressReportEvery > 0 && (index + 1) % safeProgressReportEvery === 0) {
567
- report && await report(`\u540C\u6B65\u8FDB\u5EA6\uFF1A${index + 1}/${withUrl.length}\uFF08\u6210\u529F ${okCount}\uFF09`);
637
+ report && await report(`\u540C\u6B65\u8FDB\u5EA6\uFF1A${index + 1}/${needSync.length}\uFF08\u6210\u529F ${okCount}\uFF09`);
568
638
  }
569
639
  };
570
640
  if (adaptiveTransfer) {
571
641
  logger2.info(`[\u7FA4\u6587\u4EF6\u540C\u6B65][${groupId}] \u672A\u914D\u7F6E\u9650\u901F\uFF0C\u5C06\u81EA\u9002\u5E94\u8C03\u6574\u4F20\u8F93\u5E76\u53D1\uFF08\u6700\u591A ${MAX_TRANSFER_CONCURRENCY}\uFF09`);
572
- await runWithAdaptiveConcurrency(withUrl, {
642
+ await runWithAdaptiveConcurrency(needSync, {
573
643
  initial: transferInitial,
574
644
  max: MAX_TRANSFER_CONCURRENCY,
575
645
  fn: transferFn,
@@ -578,13 +648,13 @@ var syncGroupFilesToOpenListCore = async (params) => {
578
648
  }
579
649
  });
580
650
  } else {
581
- await runWithConcurrency(withUrl, transferInitial, transferFn);
651
+ await runWithConcurrency(needSync, transferInitial, transferFn);
582
652
  }
583
653
  state.lastSyncAt = Date.now();
584
654
  writeGroupSyncState(groupId, state);
585
- const failCount = withUrl.length - okCount;
655
+ const failCount = needSync.length - okCount;
586
656
  report && await report(`\u540C\u6B65\u5B8C\u6210\uFF1A\u6210\u529F ${okCount} / \u5931\u8D25 ${failCount}\uFF08\u589E\u91CF\u8DF3\u8FC7 ${skipped}\uFF09`);
587
- if (failCount) {
657
+ if (syncErrors.length) {
588
658
  const preview = syncErrors.slice(0, 5).map((it) => `${it.path} (${it.fileId})
589
659
  ${it.message}`).join("\n\n");
590
660
  report && await report(`\u5931\u8D25\u793A\u4F8B\uFF08\u524D5\u6761\uFF09\uFF1A
@@ -596,7 +666,7 @@ ${it.message}`).join("\n\n");
596
666
  report && await report(`URL\u83B7\u53D6\u5931\u8D25\u793A\u4F8B\uFF08\u524D5\u6761\uFF09\uFF1A
597
667
  ${preview}`);
598
668
  }
599
- return { total: limitedList.length, skipped, urlOk: withUrl.length, ok: okCount, fail: failCount };
669
+ return { total: limitedList.length, skipped, urlOk: urlOkSet.size, ok: okCount, fail: failCount };
600
670
  });
601
671
  };
602
672
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  syncGroupFilesToOpenListCore
3
- } from "./chunk-KB5N77CR.js";
3
+ } from "./chunk-2Z6KEH2L.js";
4
4
  import {
5
5
  normalizePosixPath,
6
6
  readJsonSafe,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karin-plugin-qgroup-file2openlist",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "author": "429",
5
5
  "type": "module",
6
6
  "description": "karin plugin for QGroupFile backup",