koishi-plugin-echo-cave 1.31.1 → 1.31.2

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/lib/index.cjs CHANGED
@@ -18017,9 +18017,9 @@ var require_dist_cjs47 = __commonJS({
18017
18017
  }
18018
18018
  });
18019
18019
 
18020
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/ruleset.js
18020
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/ruleset.js
18021
18021
  var require_ruleset = __commonJS({
18022
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/ruleset.js"(exports2) {
18022
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/ruleset.js"(exports2) {
18023
18023
  "use strict";
18024
18024
  Object.defineProperty(exports2, "__esModule", { value: true });
18025
18025
  exports2.ruleSet = void 0;
@@ -18219,9 +18219,9 @@ var require_ruleset = __commonJS({
18219
18219
  }
18220
18220
  });
18221
18221
 
18222
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/endpointResolver.js
18222
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/endpointResolver.js
18223
18223
  var require_endpointResolver = __commonJS({
18224
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/endpointResolver.js"(exports2) {
18224
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/endpoint/endpointResolver.js"(exports2) {
18225
18225
  "use strict";
18226
18226
  Object.defineProperty(exports2, "__esModule", { value: true });
18227
18227
  exports2.defaultEndpointResolver = void 0;
@@ -18258,9 +18258,9 @@ var require_endpointResolver = __commonJS({
18258
18258
  }
18259
18259
  });
18260
18260
 
18261
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/auth/httpAuthSchemeProvider.js
18261
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/auth/httpAuthSchemeProvider.js
18262
18262
  var require_httpAuthSchemeProvider = __commonJS({
18263
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/auth/httpAuthSchemeProvider.js"(exports2) {
18263
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/auth/httpAuthSchemeProvider.js"(exports2) {
18264
18264
  "use strict";
18265
18265
  Object.defineProperty(exports2, "__esModule", { value: true });
18266
18266
  exports2.resolveHttpAuthSchemeConfig = exports2.defaultS3HttpAuthSchemeProvider = exports2.defaultS3HttpAuthSchemeParametersProvider = void 0;
@@ -18387,9 +18387,9 @@ var require_httpAuthSchemeProvider = __commonJS({
18387
18387
  }
18388
18388
  });
18389
18389
 
18390
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/S3ServiceException.js
18390
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/S3ServiceException.js
18391
18391
  var require_S3ServiceException = __commonJS({
18392
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/S3ServiceException.js"(exports2) {
18392
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/S3ServiceException.js"(exports2) {
18393
18393
  "use strict";
18394
18394
  Object.defineProperty(exports2, "__esModule", { value: true });
18395
18395
  exports2.S3ServiceException = exports2.__ServiceException = void 0;
@@ -18407,9 +18407,9 @@ var require_S3ServiceException = __commonJS({
18407
18407
  }
18408
18408
  });
18409
18409
 
18410
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/errors.js
18410
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/errors.js
18411
18411
  var require_errors = __commonJS({
18412
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/errors.js"(exports2) {
18412
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/models/errors.js"(exports2) {
18413
18413
  "use strict";
18414
18414
  Object.defineProperty(exports2, "__esModule", { value: true });
18415
18415
  exports2.ObjectAlreadyInActiveTierError = exports2.IdempotencyParameterMismatch = exports2.TooManyParts = exports2.InvalidWriteOffset = exports2.InvalidRequest = exports2.EncryptionTypeMismatch = exports2.NotFound = exports2.NoSuchKey = exports2.InvalidObjectState = exports2.NoSuchBucket = exports2.BucketAlreadyOwnedByYou = exports2.BucketAlreadyExists = exports2.ObjectNotInActiveTierError = exports2.AccessDenied = exports2.NoSuchUpload = void 0;
@@ -18616,9 +18616,9 @@ var require_errors = __commonJS({
18616
18616
  }
18617
18617
  });
18618
18618
 
18619
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/schemas/schemas_0.js
18619
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/schemas/schemas_0.js
18620
18620
  var require_schemas_0 = __commonJS({
18621
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/schemas/schemas_0.js"(exports2) {
18621
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/schemas/schemas_0.js"(exports2) {
18622
18622
  "use strict";
18623
18623
  Object.defineProperty(exports2, "__esModule", { value: true });
18624
18624
  exports2.CreateBucketMetadataTableConfigurationRequest$ = exports2.CreateBucketMetadataConfigurationRequest$ = exports2.CreateBucketConfiguration$ = exports2.CORSRule$ = exports2.CORSConfiguration$ = exports2.CopyPartResult$ = exports2.CopyObjectResult$ = exports2.CopyObjectRequest$ = exports2.CopyObjectOutput$ = exports2.ContinuationEvent$ = exports2.Condition$ = exports2.CompleteMultipartUploadRequest$ = exports2.CompleteMultipartUploadOutput$ = exports2.CompletedPart$ = exports2.CompletedMultipartUpload$ = exports2.CommonPrefix$ = exports2.Checksum$ = exports2.BucketLoggingStatus$ = exports2.BucketLifecycleConfiguration$ = exports2.BucketInfo$ = exports2.Bucket$ = exports2.BlockedEncryptionTypes$ = exports2.AnalyticsS3BucketDestination$ = exports2.AnalyticsExportDestination$ = exports2.AnalyticsConfiguration$ = exports2.AnalyticsAndOperator$ = exports2.AccessControlTranslation$ = exports2.AccessControlPolicy$ = exports2.AccelerateConfiguration$ = exports2.AbortMultipartUploadRequest$ = exports2.AbortMultipartUploadOutput$ = exports2.AbortIncompleteMultipartUpload$ = exports2.AbacStatus$ = exports2.errorTypeRegistries = exports2.TooManyParts$ = exports2.ObjectNotInActiveTierError$ = exports2.ObjectAlreadyInActiveTierError$ = exports2.NotFound$ = exports2.NoSuchUpload$ = exports2.NoSuchKey$ = exports2.NoSuchBucket$ = exports2.InvalidWriteOffset$ = exports2.InvalidRequest$ = exports2.InvalidObjectState$ = exports2.IdempotencyParameterMismatch$ = exports2.EncryptionTypeMismatch$ = exports2.BucketAlreadyOwnedByYou$ = exports2.BucketAlreadyExists$ = exports2.AccessDenied$ = exports2.S3ServiceException$ = void 0;
@@ -23815,13 +23815,13 @@ var require_schemas_0 = __commonJS({
23815
23815
  }
23816
23816
  });
23817
23817
 
23818
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/package.json
23818
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/package.json
23819
23819
  var require_package = __commonJS({
23820
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/package.json"(exports2, module2) {
23820
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/package.json"(exports2, module2) {
23821
23821
  module2.exports = {
23822
23822
  name: "@aws-sdk/client-s3",
23823
23823
  description: "AWS SDK for JavaScript S3 Client for Node.js, Browser and React Native",
23824
- version: "3.1029.0",
23824
+ version: "3.1030.0",
23825
23825
  scripts: {
23826
23826
  build: "concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs",
23827
23827
  "build:cjs": "node ../../scripts/compilation/inline client-s3",
@@ -23904,7 +23904,7 @@ var require_package = __commonJS({
23904
23904
  tslib: "^2.6.2"
23905
23905
  },
23906
23906
  devDependencies: {
23907
- "@aws-sdk/signature-v4-crt": "3.1029.0",
23907
+ "@aws-sdk/signature-v4-crt": "3.1030.0",
23908
23908
  "@smithy/snapshot-testing": "^2.0.5",
23909
23909
  "@tsconfig/node20": "20.1.8",
23910
23910
  "@types/node": "^20.14.8",
@@ -30686,9 +30686,9 @@ var require_dist_cjs67 = __commonJS({
30686
30686
  }
30687
30687
  });
30688
30688
 
30689
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.shared.js
30689
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.shared.js
30690
30690
  var require_runtimeConfig_shared = __commonJS({
30691
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.shared.js"(exports2) {
30691
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.shared.js"(exports2) {
30692
30692
  "use strict";
30693
30693
  Object.defineProperty(exports2, "__esModule", { value: true });
30694
30694
  exports2.getRuntimeConfig = void 0;
@@ -30748,9 +30748,9 @@ var require_runtimeConfig_shared = __commonJS({
30748
30748
  }
30749
30749
  });
30750
30750
 
30751
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.js
30751
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.js
30752
30752
  var require_runtimeConfig = __commonJS({
30753
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.js"(exports2) {
30753
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/runtimeConfig.js"(exports2) {
30754
30754
  "use strict";
30755
30755
  Object.defineProperty(exports2, "__esModule", { value: true });
30756
30756
  exports2.getRuntimeConfig = void 0;
@@ -31098,9 +31098,9 @@ var require_dist_cjs70 = __commonJS({
31098
31098
  }
31099
31099
  });
31100
31100
 
31101
- // node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/index.js
31101
+ // node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/index.js
31102
31102
  var require_dist_cjs71 = __commonJS({
31103
- "node_modules/.pnpm/@aws-sdk+client-s3@3.1029.0/node_modules/@aws-sdk/client-s3/dist-cjs/index.js"(exports2) {
31103
+ "node_modules/.pnpm/@aws-sdk+client-s3@3.1030.0/node_modules/@aws-sdk/client-s3/dist-cjs/index.js"(exports2) {
31104
31104
  "use strict";
31105
31105
  var middlewareExpectContinue = require_dist_cjs3();
31106
31106
  var middlewareFlexibleChecksums = require_dist_cjs19();
@@ -34465,11 +34465,14 @@ async function deleteCavesForOversizedMedia(ctx, caves, cfg) {
34465
34465
  }
34466
34466
  async function inspectCaveMediaRefs(ctx, shouldInclude) {
34467
34467
  const caves = await getAllCaves(ctx);
34468
+ let scannedRecords = 0;
34469
+ const failedRecordIds = [];
34468
34470
  const results = [];
34469
34471
  for (const cave of caves) {
34470
34472
  if (shouldInclude && !shouldInclude(cave.id)) {
34471
34473
  continue;
34472
34474
  }
34475
+ scannedRecords += 1;
34473
34476
  try {
34474
34477
  const refs = await collectMessageMediaRefs(ctx, cave.content);
34475
34478
  if (refs.length > 0) {
@@ -34479,10 +34482,15 @@ async function inspectCaveMediaRefs(ctx, shouldInclude) {
34479
34482
  });
34480
34483
  }
34481
34484
  } catch (error2) {
34485
+ failedRecordIds.push(cave.id);
34482
34486
  ctx.logger.warn(`Failed to inspect media refs for cave #${cave.id}: ${error2}`);
34483
34487
  }
34484
34488
  }
34485
- return results;
34489
+ return {
34490
+ failedRecordIds,
34491
+ results,
34492
+ scannedRecords
34493
+ };
34486
34494
  }
34487
34495
  async function rewriteMessageMediaStorage(ctx, content, targetChannelId, targetMode, transferMode, cfg, state2, stats, shouldRewrite, hooks) {
34488
34496
  const planSet = /* @__PURE__ */ new Set();
@@ -34869,7 +34877,7 @@ async function deleteMediaFilesFromMessage(ctx, content, cfg) {
34869
34877
  return element;
34870
34878
  });
34871
34879
  }
34872
- async function migrateLocalMediaToV2(ctx, cfg) {
34880
+ async function migrateLocalMediaToV2(ctx, cfg, createHooks) {
34873
34881
  const caves = await getAllCaves(ctx);
34874
34882
  const stats = createEmptyStats();
34875
34883
  const state2 = {
@@ -34878,6 +34886,8 @@ async function migrateLocalMediaToV2(ctx, cfg) {
34878
34886
  const failedRecordIds = [];
34879
34887
  for (const cave of caves) {
34880
34888
  let rewritten = null;
34889
+ let status = "skipped";
34890
+ const hooks = createHooks?.(cave.id);
34881
34891
  try {
34882
34892
  rewritten = await rewriteMessageMediaStorage(
34883
34893
  ctx,
@@ -34888,7 +34898,8 @@ async function migrateLocalMediaToV2(ctx, cfg) {
34888
34898
  cfg,
34889
34899
  state2,
34890
34900
  stats,
34891
- (fileRef, type) => isLegacyLocalMediaRef(ctx, fileRef, type)
34901
+ (fileRef, type) => isLegacyLocalMediaRef(ctx, fileRef, type),
34902
+ hooks
34892
34903
  );
34893
34904
  const mediaUrlFields = await collectStoredMessageMediaUrls(ctx, rewritten.content);
34894
34905
  const shouldUpdateMediaFields = hasMediaUrlFieldChanges(cave, mediaUrlFields);
@@ -34901,13 +34912,17 @@ async function migrateLocalMediaToV2(ctx, cfg) {
34901
34912
  ...mediaUrlFields
34902
34913
  });
34903
34914
  await runTransferPlans(rewritten.plans, "commit");
34915
+ status = "updated";
34904
34916
  }
34905
34917
  } catch (error2) {
34906
34918
  if (rewritten) {
34907
34919
  await runTransferPlans(rewritten.plans, "rollback");
34908
34920
  }
34909
34921
  failedRecordIds.push(cave.id);
34922
+ status = "failed";
34910
34923
  ctx.logger.warn(`Failed to migrate legacy local media for cave #${cave.id}: ${error2}`);
34924
+ } finally {
34925
+ await hooks?.onRecordProcessed?.(status);
34911
34926
  }
34912
34927
  }
34913
34928
  return {
@@ -34925,6 +34940,7 @@ async function migrateLocalMediaToS3(ctx, cfg, keepLocal, createHooks) {
34925
34940
  const failedRecordIds = [];
34926
34941
  for (const cave of caves) {
34927
34942
  let rewritten = null;
34943
+ let status = "skipped";
34928
34944
  const hooks = createHooks?.(cave.id);
34929
34945
  try {
34930
34946
  rewritten = await rewriteMessageMediaStorage(
@@ -34951,13 +34967,17 @@ async function migrateLocalMediaToS3(ctx, cfg, keepLocal, createHooks) {
34951
34967
  });
34952
34968
  await runTransferPlans(rewritten.plans, "commit");
34953
34969
  await hooks?.onMigrationCommitted?.();
34970
+ status = "updated";
34954
34971
  }
34955
34972
  } catch (error2) {
34956
34973
  if (rewritten) {
34957
34974
  await runTransferPlans(rewritten.plans, "rollback");
34958
34975
  }
34959
34976
  failedRecordIds.push(cave.id);
34977
+ status = "failed";
34960
34978
  ctx.logger.warn(`Failed to migrate local media to S3 for cave #${cave.id}: ${error2}`);
34979
+ } finally {
34980
+ await hooks?.onRecordProcessed?.(status);
34961
34981
  }
34962
34982
  }
34963
34983
  return {
@@ -34966,7 +34986,7 @@ async function migrateLocalMediaToS3(ctx, cfg, keepLocal, createHooks) {
34966
34986
  ...stats
34967
34987
  };
34968
34988
  }
34969
- async function mergeChannelCaves(ctx, cfg, sourceChannelId, targetChannelId, keepSource) {
34989
+ async function mergeChannelCaves(ctx, cfg, sourceChannelId, targetChannelId, keepSource, createHooks) {
34970
34990
  const sourceCaves = await getCavesByChannel(ctx, sourceChannelId);
34971
34991
  const targetMode = getStorageMode(cfg);
34972
34992
  const transferMode = keepSource ? "copy" : "move";
@@ -34978,6 +34998,8 @@ async function mergeChannelCaves(ctx, cfg, sourceChannelId, targetChannelId, kee
34978
34998
  let mergedRecords = 0;
34979
34999
  for (const cave of sourceCaves) {
34980
35000
  let rewritten = null;
35001
+ let status = "skipped";
35002
+ const hooks = createHooks?.(cave.id);
34981
35003
  try {
34982
35004
  rewritten = await rewriteMessageMediaStorage(
34983
35005
  ctx,
@@ -34987,7 +35009,9 @@ async function mergeChannelCaves(ctx, cfg, sourceChannelId, targetChannelId, kee
34987
35009
  transferMode,
34988
35010
  cfg,
34989
35011
  state2,
34990
- stats
35012
+ stats,
35013
+ void 0,
35014
+ hooks
34991
35015
  );
34992
35016
  if (keepSource) {
34993
35017
  const nextId = await getNextCavePublicId(ctx);
@@ -35018,12 +35042,16 @@ async function mergeChannelCaves(ctx, cfg, sourceChannelId, targetChannelId, kee
35018
35042
  }
35019
35043
  await runTransferPlans(rewritten.plans, "commit");
35020
35044
  mergedRecords += 1;
35045
+ status = "updated";
35021
35046
  } catch (error2) {
35022
35047
  if (rewritten) {
35023
35048
  await runTransferPlans(rewritten.plans, "rollback");
35024
35049
  }
35025
35050
  failedRecordIds.push(cave.id);
35051
+ status = "failed";
35026
35052
  ctx.logger.warn(`Failed to merge cave #${cave.id}: ${error2}`);
35053
+ } finally {
35054
+ await hooks?.onRecordProcessed?.(status);
35027
35055
  }
35028
35056
  }
35029
35057
  return {
@@ -35116,6 +35144,7 @@ async function listenForUserMessage(ctx, session, prompt, timeout, onMessage, on
35116
35144
  var import_node_fs2 = require("node:fs");
35117
35145
  var import_node_path2 = __toESM(require("node:path"), 1);
35118
35146
  var REINDEX_SPECIAL_OFFSET = 1e6;
35147
+ var ADMIN_BATCH_PROGRESS_INTERVAL = 50;
35119
35148
  var caveMaintenanceLock = false;
35120
35149
  function hasStringArray(value) {
35121
35150
  return Array.isArray(value) && value.every((item) => typeof item === "string");
@@ -35310,18 +35339,6 @@ function getBooleanLabel(session, value) {
35310
35339
  value ? "commands.cave.admin.common.boolean.keep" : "commands.cave.admin.common.boolean.drop"
35311
35340
  );
35312
35341
  }
35313
- function getMediaTypeLabel(session, type) {
35314
- switch (type) {
35315
- case "image":
35316
- return session.text("commands.cave.admin.common.mediaType.image");
35317
- case "video":
35318
- return session.text("commands.cave.admin.common.mediaType.video");
35319
- case "record":
35320
- return session.text("commands.cave.admin.common.mediaType.record");
35321
- default:
35322
- return session.text("commands.cave.admin.common.mediaType.file");
35323
- }
35324
- }
35325
35342
  function getMediaStorageLabel(session, storage) {
35326
35343
  return session.text(
35327
35344
  storage === "s3" ? "commands.cave.admin.common.mediaStorage.s3" : "commands.cave.admin.common.mediaStorage.local"
@@ -35339,6 +35356,30 @@ function appendFailedRecordSummary(session, message, failedRecordIds) {
35339
35356
  failedRecordIds: failedRecordIds.join(", ")
35340
35357
  });
35341
35358
  }
35359
+ async function sendBatchProgressIfNeeded(session, processedCount, totalCount, messageKey) {
35360
+ if (processedCount === 0 || processedCount % ADMIN_BATCH_PROGRESS_INTERVAL !== 0) {
35361
+ return;
35362
+ }
35363
+ await session.send(
35364
+ session.text(messageKey, {
35365
+ processedCount,
35366
+ totalCount
35367
+ })
35368
+ );
35369
+ }
35370
+ async function sendBatchedInspectResults(session, batch) {
35371
+ if (batch.length === 0) {
35372
+ return;
35373
+ }
35374
+ await session.send(
35375
+ batch.map(
35376
+ ({ id, refs }) => session.text("commands.cave.admin.inspect-media.messages.resultItem", {
35377
+ id,
35378
+ refs: refs.join("\n")
35379
+ })
35380
+ ).join("\n\n")
35381
+ );
35382
+ }
35342
35383
  function getAllRangesLabel(session) {
35343
35384
  return session.text("commands.cave.admin.inspect-media.messages.allRanges");
35344
35385
  }
@@ -35440,7 +35481,26 @@ async function mergeCavesBetweenChannels(ctx, session, cfg, sourceChannelId, tar
35440
35481
  if (!confirmed) {
35441
35482
  return;
35442
35483
  }
35443
- const result = await mergeChannelCaves(ctx, cfg, sourceChannelId, targetChannelId, keepSource);
35484
+ let processedCount = 0;
35485
+ const totalCount = sourceCaves.length;
35486
+ const result = await mergeChannelCaves(
35487
+ ctx,
35488
+ cfg,
35489
+ sourceChannelId,
35490
+ targetChannelId,
35491
+ keepSource,
35492
+ () => ({
35493
+ onRecordProcessed: async () => {
35494
+ processedCount += 1;
35495
+ await sendBatchProgressIfNeeded(
35496
+ session,
35497
+ processedCount,
35498
+ totalCount,
35499
+ "commands.cave.admin.merge.messages.progress"
35500
+ );
35501
+ }
35502
+ })
35503
+ );
35444
35504
  return appendFailedRecordSummary(
35445
35505
  session,
35446
35506
  session.text("commands.cave.admin.merge.messages.mergeDone", result),
@@ -35452,7 +35512,19 @@ async function migrateLegacyLocalMedia(ctx, session, cfg) {
35452
35512
  if (accessError) {
35453
35513
  return accessError;
35454
35514
  }
35455
- const result = await migrateLocalMediaToV2(ctx, cfg);
35515
+ const caves = await getAllCaves(ctx);
35516
+ let processedCount = 0;
35517
+ const result = await migrateLocalMediaToV2(ctx, cfg, () => ({
35518
+ onRecordProcessed: async () => {
35519
+ processedCount += 1;
35520
+ await sendBatchProgressIfNeeded(
35521
+ session,
35522
+ processedCount,
35523
+ caves.length,
35524
+ "commands.cave.admin.migrate-local-v2.messages.progress"
35525
+ );
35526
+ }
35527
+ }));
35456
35528
  return appendFailedRecordSummary(
35457
35529
  session,
35458
35530
  session.text("commands.cave.admin.migrate-local-v2.messages.migrateDone", result),
@@ -35487,21 +35559,18 @@ async function migrateMediaToS3(ctx, session, cfg, keepLocalOption) {
35487
35559
  if (!confirmed) {
35488
35560
  return;
35489
35561
  }
35490
- const result = await migrateLocalMediaToS3(ctx, cfg, keepLocal, (caveId) => {
35491
- const uploadedMediaTypes = [];
35562
+ const caves = await getAllCaves(ctx);
35563
+ let processedCount = 0;
35564
+ const result = await migrateLocalMediaToS3(ctx, cfg, keepLocal, () => {
35492
35565
  return {
35493
- onS3Upload: async (type) => {
35494
- uploadedMediaTypes.push(type);
35495
- },
35496
- onMigrationCommitted: async () => {
35497
- for (const mediaType of uploadedMediaTypes) {
35498
- await session.send(
35499
- session.text("commands.cave.admin.migrate-s3.messages.itemUploaded", {
35500
- caveId,
35501
- mediaType: getMediaTypeLabel(session, mediaType)
35502
- })
35503
- );
35504
- }
35566
+ onRecordProcessed: async () => {
35567
+ processedCount += 1;
35568
+ await sendBatchProgressIfNeeded(
35569
+ session,
35570
+ processedCount,
35571
+ caves.length,
35572
+ "commands.cave.admin.migrate-s3.messages.progress"
35573
+ );
35505
35574
  }
35506
35575
  };
35507
35576
  });
@@ -35521,8 +35590,8 @@ async function inspectMediaRefsForMigration(ctx, session, cfg, idRangesOption) {
35521
35590
  return session.text("commands.cave.admin.inspect-media.messages.invalidRange");
35522
35591
  }
35523
35592
  const displayRanges = idRangesOption?.trim() || getAllRangesLabel(session);
35524
- const results = await inspectCaveMediaRefs(ctx, (id) => isIdInRanges(id, idRanges));
35525
- if (results.length === 0) {
35593
+ const inspection = await inspectCaveMediaRefs(ctx, (id) => isIdInRanges(id, idRanges));
35594
+ if (inspection.results.length === 0 && inspection.failedRecordIds.length === 0) {
35526
35595
  return session.text("commands.cave.admin.inspect-media.messages.noMediaFound", {
35527
35596
  idRanges: displayRanges
35528
35597
  });
@@ -35532,7 +35601,7 @@ async function inspectMediaRefsForMigration(ctx, session, cfg, idRangesOption) {
35532
35601
  session,
35533
35602
  session.text("commands.cave.admin.inspect-media.messages.confirmSummary", {
35534
35603
  idRanges: displayRanges,
35535
- matchedCount: results.length
35604
+ matchedCount: inspection.results.length
35536
35605
  }),
35537
35606
  session.text("commands.cave.admin.inspect-media.messages.confirmRetry"),
35538
35607
  session.text("commands.cave.admin.inspect-media.messages.confirmTimeout"),
@@ -35541,14 +35610,31 @@ async function inspectMediaRefsForMigration(ctx, session, cfg, idRangesOption) {
35541
35610
  if (!confirmed) {
35542
35611
  return;
35543
35612
  }
35544
- for (const { id, refs } of results) {
35545
- await session.send(
35546
- session.text("commands.cave.admin.inspect-media.messages.resultItem", {
35547
- id,
35548
- refs: refs.join("\n")
35549
- })
35550
- );
35613
+ let processedCount = 0;
35614
+ const batchedResults = [];
35615
+ for (const result of inspection.results) {
35616
+ batchedResults.push(result);
35617
+ processedCount += 1;
35618
+ if (batchedResults.length === ADMIN_BATCH_PROGRESS_INTERVAL) {
35619
+ await sendBatchedInspectResults(session, batchedResults);
35620
+ batchedResults.length = 0;
35621
+ await session.send(
35622
+ session.text("commands.cave.admin.inspect-media.messages.progress", {
35623
+ processedCount,
35624
+ totalCount: inspection.results.length
35625
+ })
35626
+ );
35627
+ }
35551
35628
  }
35629
+ await sendBatchedInspectResults(session, batchedResults);
35630
+ return appendFailedRecordSummary(
35631
+ session,
35632
+ session.text("commands.cave.admin.inspect-media.messages.inspectDone", {
35633
+ scannedRecords: inspection.scannedRecords,
35634
+ matchedCount: inspection.results.length
35635
+ }),
35636
+ inspection.failedRecordIds
35637
+ );
35552
35638
  }
35553
35639
  async function backfillCaveMediaUrls(ctx, session, cfg, idRangesOption) {
35554
35640
  const accessError = ensureAdminPrivateAccess(session, cfg);
@@ -35583,6 +35669,7 @@ async function backfillCaveMediaUrls(ctx, session, cfg, idRangesOption) {
35583
35669
  if (!confirmed) {
35584
35670
  return;
35585
35671
  }
35672
+ let processedCount = 0;
35586
35673
  let updatedCount = 0;
35587
35674
  let skippedWithoutMedia = 0;
35588
35675
  const failedRecordIds = [];
@@ -35594,13 +35681,21 @@ async function backfillCaveMediaUrls(ctx, session, cfg, idRangesOption) {
35594
35681
  const mediaUrlFields = await collectStoredMessageMediaUrls(ctx, cave.content);
35595
35682
  if (areMediaUrlFieldsEmpty(mediaUrlFields)) {
35596
35683
  skippedWithoutMedia += 1;
35597
- continue;
35684
+ } else {
35685
+ await updateCaveByEntryId(ctx, cave.entryId, mediaUrlFields);
35686
+ updatedCount += 1;
35598
35687
  }
35599
- await updateCaveByEntryId(ctx, cave.entryId, mediaUrlFields);
35600
- updatedCount += 1;
35601
35688
  } catch (error2) {
35602
35689
  failedRecordIds.push(cave.id);
35603
35690
  ctx.logger.warn(`Failed to backfill media URLs for cave #${cave.id}: ${error2}`);
35691
+ } finally {
35692
+ processedCount += 1;
35693
+ await sendBatchProgressIfNeeded(
35694
+ session,
35695
+ processedCount,
35696
+ candidates.length,
35697
+ "commands.cave.admin.backfill-media-urls.messages.progress"
35698
+ );
35604
35699
  }
35605
35700
  }
35606
35701
  return appendFailedRecordSummary(
@@ -35714,11 +35809,32 @@ async function reindexCaveIds(ctx, session, cfg) {
35714
35809
  setCaveMaintenanceLock(true);
35715
35810
  try {
35716
35811
  const reindexedCaves = buildSequentialCaveSnapshot(caves);
35812
+ let processedCount = 0;
35813
+ const failedRecordIds = [];
35717
35814
  for (const cave of reindexedCaves) {
35718
- if (typeof cave.entryId !== "number") {
35719
- throw new Error(`missing_entry_id_for_cave_${cave.id}`);
35815
+ try {
35816
+ if (typeof cave.entryId !== "number") {
35817
+ throw new Error(`missing_entry_id_for_cave_${cave.id}`);
35818
+ }
35819
+ await updateCaveByEntryId(ctx, cave.entryId, { id: cave.id });
35820
+ processedCount += 1;
35821
+ await sendBatchProgressIfNeeded(
35822
+ session,
35823
+ processedCount,
35824
+ reindexedCaves.length,
35825
+ "commands.cave.admin.reindex.messages.progress"
35826
+ );
35827
+ } catch (error2) {
35828
+ failedRecordIds.push(cave.id);
35829
+ throw appendFailedRecordSummary(
35830
+ session,
35831
+ session.text("commands.cave.admin.reindex.messages.reindexFailed", {
35832
+ backupPath,
35833
+ error: error2 instanceof Error ? error2.message : String(error2)
35834
+ }),
35835
+ failedRecordIds
35836
+ );
35720
35837
  }
35721
- await updateCaveByEntryId(ctx, cave.entryId, { id: cave.id });
35722
35838
  }
35723
35839
  await verifyCaveSnapshot(ctx, reindexedCaves);
35724
35840
  return session.text("commands.cave.admin.reindex.messages.reindexDone", {
@@ -35727,7 +35843,7 @@ async function reindexCaveIds(ctx, session, cfg) {
35727
35843
  });
35728
35844
  } catch (error2) {
35729
35845
  ctx.logger.error(`Failed to reindex cave ids: ${error2}`);
35730
- return session.text("commands.cave.admin.reindex.messages.reindexFailed", {
35846
+ return typeof error2 === "string" ? error2 : session.text("commands.cave.admin.reindex.messages.reindexFailed", {
35731
35847
  backupPath,
35732
35848
  error: error2 instanceof Error ? error2.message : String(error2)
35733
35849
  });
@@ -35778,6 +35894,32 @@ async function restoreReindexBackup(ctx, session, cfg, backupPathInput) {
35778
35894
  ...toCaveSnapshotRecord(record),
35779
35895
  entryId: record.entryId
35780
35896
  }));
35897
+ let processedCount = 0;
35898
+ const failedRecordIds = [];
35899
+ for (const cave of restoreRecords) {
35900
+ try {
35901
+ if (typeof cave.entryId !== "number") {
35902
+ throw new Error(`missing_entry_id_for_cave_${cave.id}`);
35903
+ }
35904
+ processedCount += 1;
35905
+ await sendBatchProgressIfNeeded(
35906
+ session,
35907
+ processedCount,
35908
+ restoreRecords.length,
35909
+ "commands.cave.admin.restore-reindex.messages.progress"
35910
+ );
35911
+ } catch (error2) {
35912
+ failedRecordIds.push(cave.id);
35913
+ throw appendFailedRecordSummary(
35914
+ session,
35915
+ session.text("commands.cave.admin.restore-reindex.messages.restoreFailed", {
35916
+ backupPath,
35917
+ error: error2 instanceof Error ? error2.message : String(error2)
35918
+ }),
35919
+ failedRecordIds
35920
+ );
35921
+ }
35922
+ }
35781
35923
  await replaceCaveSnapshot(ctx, currentCaves, restoreRecords);
35782
35924
  await verifyRestoredSnapshot(ctx, backup.records);
35783
35925
  return session.text("commands.cave.admin.restore-reindex.messages.restoreDone", {
@@ -35786,7 +35928,7 @@ async function restoreReindexBackup(ctx, session, cfg, backupPathInput) {
35786
35928
  });
35787
35929
  } catch (error2) {
35788
35930
  ctx.logger.error(`Failed to restore cave reindex backup: ${error2}`);
35789
- return session.text("commands.cave.admin.restore-reindex.messages.restoreFailed", {
35931
+ return typeof error2 === "string" ? error2 : session.text("commands.cave.admin.restore-reindex.messages.restoreFailed", {
35790
35932
  backupPath,
35791
35933
  error: error2 instanceof Error ? error2.message : String(error2)
35792
35934
  });
@@ -37255,6 +37397,7 @@ var zh_CN_default = {
37255
37397
  confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
37256
37398
  confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u64CD\u4F5C\u3002",
37257
37399
  confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u64CD\u4F5C\u672A\u6267\u884C\u3002",
37400
+ progress: "\u23F3 \u7FA4\u804A\u56DE\u58F0\u6D1E\u5408\u5E76\u8FDB\u5EA6\uFF1A\u5DF2\u5904\u7406 {processedCount}/{totalCount} \u6761\u8BB0\u5F55\u3002",
37258
37401
  mergeDone: "\u2705 \u5408\u5E76\u5B8C\u6210\uFF1A\u5904\u7406\u4E86 {mergedRecords} \u6761\u8BB0\u5F55\uFF0C\u5A92\u4F53\u590D\u5236 {mediaCopied} \u4E2A\u3001\u79FB\u52A8 {mediaMoved} \u4E2A\u3001\u4E0A\u4F20 {mediaUploaded} \u4E2A\u3001\u5931\u8D25 {mediaFailed} \u4E2A\u3002"
37259
37402
  }
37260
37403
  },
@@ -37281,6 +37424,7 @@ var zh_CN_default = {
37281
37424
  "cave.admin.migrate-local-v2": {
37282
37425
  description: "\u8FC1\u79FB\u65E7\u672C\u5730\u5A92\u4F53\u76EE\u5F55\u5230 V2 \u5206\u7FA4\u5B58\u50A8\u683C\u5F0F",
37283
37426
  messages: {
37427
+ progress: "\u23F3 \u672C\u5730\u5A92\u4F53\u8FC1\u79FB\u8FDB\u5EA6\uFF1A\u5DF2\u5904\u7406 {processedCount}/{totalCount} \u6761\u8BB0\u5F55\u3002",
37284
37428
  migrateDone: "\u2705 \u672C\u5730\u5A92\u4F53\u8FC1\u79FB\u5B8C\u6210\uFF1A\u626B\u63CF {scannedRecords} \u6761\u8BB0\u5F55\uFF0C\u66F4\u65B0 {recordsUpdated} \u6761\u8BB0\u5F55\uFF0C\u590D\u5236 {mediaCopied} \u4E2A\u3001\u79FB\u52A8 {mediaMoved} \u4E2A\u3001\u5931\u8D25 {mediaFailed} \u4E2A\u3002"
37285
37429
  }
37286
37430
  },
@@ -37293,7 +37437,7 @@ var zh_CN_default = {
37293
37437
  confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
37294
37438
  confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u64CD\u4F5C\u3002",
37295
37439
  confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u64CD\u4F5C\u672A\u6267\u884C\u3002",
37296
- itemUploaded: "\u{1F4E6} \u5DF2\u4E0A\u4F20\u5230 S3\uFF1A\u56DE\u58F0\u6D1E #{caveId} \u7684{mediaType}",
37440
+ progress: "\u23F3 S3 \u8FC1\u79FB\u8FDB\u5EA6\uFF1A\u5DF2\u5904\u7406 {processedCount}/{totalCount} \u6761\u8BB0\u5F55\u3002",
37297
37441
  migrateDone: "\u2705 S3 \u8FC1\u79FB\u5B8C\u6210\uFF1A\u626B\u63CF {scannedRecords} \u6761\u8BB0\u5F55\uFF0C\u66F4\u65B0 {recordsUpdated} \u6761\u8BB0\u5F55\uFF0C\u4E0A\u4F20 {mediaUploaded} \u4E2A\u3001\u590D\u5236 {mediaCopied} \u4E2A\u3001\u79FB\u52A8 {mediaMoved} \u4E2A\u3001\u5220\u9664 {mediaDeleted} \u4E2A\u3001\u5931\u8D25 {mediaFailed} \u4E2A\u3002"
37298
37442
  }
37299
37443
  },
@@ -37304,10 +37448,12 @@ var zh_CN_default = {
37304
37448
  allRanges: "\u5168\u90E8",
37305
37449
  noMediaFound: "\u{1F50D} \u5728\u8303\u56F4 {idRanges} \u5185\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55\u5A92\u4F53\u94FE\u63A5\u3002",
37306
37450
  resultItem: "\u56DE\u58F0\u6D1E #{id}\n{refs}",
37307
- confirmSummary: "\u26A0\uFE0F \u5373\u5C06\u6267\u884C\u5A92\u4F53\u94FE\u63A5\u68C0\u6D4B\uFF08\u53EA\u8BFB\uFF09\n\u68C0\u6D4B\u8303\u56F4\uFF1A{idRanges}\n\u547D\u4E2D\u56DE\u58F0\u6D1E\u6570\uFF1A{matchedCount}\n\n\u672C\u6B21\u64CD\u4F5C\u4E0D\u4F1A\u4E0A\u4F20 S3\u3001\u4E0D\u4F1A\u5220\u9664\u672C\u5730\u5A92\u4F53\u3001\u4E0D\u4F1A\u6539\u5199\u6570\u636E\u5E93\uFF0C\u4F46\u4F1A\u9010\u6761\u53D1\u9001\u68C0\u6D4B\u7ED3\u679C\u3002\u8BF7\u5728 30 \u79D2\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u8F93\u5165\u201C\u53D6\u6D88\u201D\u53EF\u7EC8\u6B62\u3002",
37451
+ confirmSummary: "\u26A0\uFE0F \u5373\u5C06\u6267\u884C\u5A92\u4F53\u94FE\u63A5\u68C0\u6D4B\uFF08\u53EA\u8BFB\uFF09\n\u68C0\u6D4B\u8303\u56F4\uFF1A{idRanges}\n\u547D\u4E2D\u56DE\u58F0\u6D1E\u6570\uFF1A{matchedCount}\n\n\u672C\u6B21\u64CD\u4F5C\u4E0D\u4F1A\u4E0A\u4F20 S3\u3001\u4E0D\u4F1A\u5220\u9664\u672C\u5730\u5A92\u4F53\u3001\u4E0D\u4F1A\u6539\u5199\u6570\u636E\u5E93\uFF1B\u68C0\u6D4B\u7ED3\u679C\u4F1A\u6309\u6279\u6B21\u53D1\u9001\u8FDB\u5EA6\u4E0E\u660E\u7EC6\u3002\u8BF7\u5728 30 \u79D2\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u8F93\u5165\u201C\u53D6\u6D88\u201D\u53EF\u7EC8\u6B62\u3002",
37308
37452
  confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
37309
37453
  confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u68C0\u6D4B\u3002",
37310
- confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u68C0\u6D4B\u672A\u6267\u884C\u3002"
37454
+ confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u68C0\u6D4B\u672A\u6267\u884C\u3002",
37455
+ progress: "\u23F3 \u5A92\u4F53\u94FE\u63A5\u68C0\u6D4B\u8FDB\u5EA6\uFF1A\u5DF2\u53D1\u9001 {processedCount}/{totalCount} \u6761\u547D\u4E2D\u7ED3\u679C\u3002",
37456
+ inspectDone: "\u2705 \u5A92\u4F53\u94FE\u63A5\u68C0\u6D4B\u5B8C\u6210\uFF1A\u5171\u626B\u63CF {scannedRecords} \u6761\u8BB0\u5F55\uFF0C\u547D\u4E2D {matchedCount} \u6761\u56DE\u58F0\u6D1E\u3002"
37311
37457
  }
37312
37458
  },
37313
37459
  "cave.admin.backfill-media-urls": {
@@ -37319,6 +37465,7 @@ var zh_CN_default = {
37319
37465
  confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
37320
37466
  confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u5A92\u4F53 URL \u56DE\u586B\u3002",
37321
37467
  confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u5A92\u4F53 URL \u56DE\u586B\u672A\u6267\u884C\u3002",
37468
+ progress: "\u23F3 \u5A92\u4F53 URL \u56DE\u586B\u8FDB\u5EA6\uFF1A\u5DF2\u5904\u7406 {processedCount}/{totalCount} \u6761\u5019\u9009\u8BB0\u5F55\u3002",
37322
37469
  backfillDone: "\u2705 \u5A92\u4F53 URL \u56DE\u586B\u5B8C\u6210\uFF1A\u626B\u63CF {scannedRecords} \u6761\u5019\u9009\u8BB0\u5F55\uFF0C\u6210\u529F\u66F4\u65B0 {updatedCount} \u6761\uFF0C\u65E0\u5A92\u4F53\u53EF\u56DE\u586B {skippedWithoutMedia} \u6761\u3002"
37323
37470
  }
37324
37471
  },
@@ -37332,6 +37479,7 @@ var zh_CN_default = {
37332
37479
  confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
37333
37480
  confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u56DE\u58F0\u6D1E ID \u91CD\u6392\u3002",
37334
37481
  confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u56DE\u58F0\u6D1E ID \u91CD\u6392\u672A\u6267\u884C\u3002",
37482
+ progress: "\u23F3 \u56DE\u58F0\u6D1E ID \u91CD\u6392\u8FDB\u5EA6\uFF1A\u5DF2\u5904\u7406 {processedCount}/{totalCount} \u6761\u8BB0\u5F55\u3002",
37335
37483
  reindexDone: "\u2705 \u56DE\u58F0\u6D1E public ID \u91CD\u6392\u5B8C\u6210\uFF0C\u5171\u5904\u7406 {caveCount} \u6761\u8BB0\u5F55\u3002\u5907\u4EFD\u6587\u4EF6\uFF1A{backupPath}",
37336
37484
  reindexFailed: "\u274C \u56DE\u58F0\u6D1E ID \u91CD\u6392\u5931\u8D25\uFF0C\u5DF2\u505C\u6B62\u7EE7\u7EED\u5199\u5165\u3002\u53EF\u4F7F\u7528 cave.admin.restore-reindex \u6062\u590D\u5907\u4EFD\uFF1A{backupPath}\n\u9519\u8BEF\uFF1A{error}"
37337
37485
  }
@@ -37345,6 +37493,7 @@ var zh_CN_default = {
37345
37493
  confirmRetry: "\u26A0\uFE0F \u672A\u6536\u5230\u201C\u786E\u8BA4\u201D\u3002\u8BF7\u5728\u5269\u4F59\u65F6\u95F4\u5185\u8F93\u5165\u201C\u786E\u8BA4\u201D\u7EE7\u7EED\uFF0C\u6216\u8F93\u5165\u201C\u53D6\u6D88\u201D\u7EC8\u6B62\u3002",
37346
37494
  confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u3002",
37347
37495
  confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u672A\u6267\u884C\u3002",
37496
+ progress: "\u23F3 \u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u8FDB\u5EA6\uFF1A\u5DF2\u6821\u9A8C {processedCount}/{totalCount} \u6761\u8BB0\u5F55\u3002",
37348
37497
  restoreDone: "\u2705 \u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u5B8C\u6210\uFF0C\u5171\u6062\u590D {caveCount} \u6761\u8BB0\u5F55\u3002\u5907\u4EFD\u6587\u4EF6\uFF1A{backupPath}",
37349
37498
  restoreFailed: "\u274C \u56DE\u58F0\u6D1E\u5907\u4EFD\u6062\u590D\u5931\u8D25\uFF0C\u5DF2\u5C1D\u8BD5\u56DE\u6EDA\u5230\u6062\u590D\u524D\u72B6\u6001\uFF1A{backupPath}\n\u9519\u8BEF\uFF1A{error}"
37350
37499
  }
@@ -10,6 +10,7 @@ type MaybeMediaElement = {
10
10
  interface RewriteHooks {
11
11
  onS3Upload?: (type: MediaType, nextRef: string) => Promise<void>;
12
12
  onMigrationCommitted?: () => Promise<void>;
13
+ onRecordProcessed?: (status: 'updated' | 'skipped' | 'failed') => Promise<void>;
13
14
  }
14
15
  interface MediaSaveProgressState {
15
16
  progressMessageIds?: string[];
@@ -22,16 +23,21 @@ interface CaveMediaRefs {
22
23
  id: number;
23
24
  refs: string[];
24
25
  }
26
+ interface InspectCaveMediaRefsResult {
27
+ failedRecordIds: number[];
28
+ results: CaveMediaRefs[];
29
+ scannedRecords: number;
30
+ }
25
31
  export declare function processStoredMessageMedia(ctx: Context, content: string, cfg: Config, channelId: string, progressOptions?: MediaSaveProgressOptions): Promise<string>;
26
32
  export declare function collectStoredMessageMediaUrls(ctx: Context, content: string): Promise<CaveMediaUrlFields>;
27
- export declare function inspectCaveMediaRefs(ctx: Context, shouldInclude?: (caveId: number) => boolean): Promise<CaveMediaRefs[]>;
33
+ export declare function inspectCaveMediaRefs(ctx: Context, shouldInclude?: (caveId: number) => boolean): Promise<InspectCaveMediaRefsResult>;
28
34
  export declare function saveMedia(ctx: Context, mediaElement: Record<string, unknown>, type: MediaType, cfg: Config, channelId: string, progressOptions?: MediaSaveProgressOptions): Promise<string>;
29
35
  export declare function processMediaElement(ctx: Context, element: MaybeMediaElement, cfg: Config, channelId: string, progressOptions?: MediaSaveProgressOptions): Promise<MaybeMediaElement>;
30
36
  export declare function messageContainsMedia(content: unknown): Promise<boolean>;
31
37
  export declare function resolveMediaElementForSend(ctx: Context, element: MaybeMediaElement, cfg: Config): Promise<MaybeMediaElement>;
32
38
  export declare function checkAndCleanMediaFiles(ctx: Context, cfg: Config, type: MediaType): Promise<void>;
33
39
  export declare function deleteMediaFilesFromMessage(ctx: Context, content: string, cfg: Config): Promise<void>;
34
- export declare function migrateLocalMediaToV2(ctx: Context, cfg: Config): Promise<{
40
+ export declare function migrateLocalMediaToV2(ctx: Context, cfg: Config, createHooks?: (caveId: number) => RewriteHooks): Promise<{
35
41
  recordsUpdated: number;
36
42
  mediaCopied: number;
37
43
  mediaMoved: number;
@@ -53,7 +59,7 @@ export declare function migrateLocalMediaToS3(ctx: Context, cfg: Config, keepLoc
53
59
  scannedRecords: number;
54
60
  failedRecordIds: number[];
55
61
  }>;
56
- export declare function mergeChannelCaves(ctx: Context, cfg: Config, sourceChannelId: string, targetChannelId: string, keepSource: boolean): Promise<{
62
+ export declare function mergeChannelCaves(ctx: Context, cfg: Config, sourceChannelId: string, targetChannelId: string, keepSource: boolean, createHooks?: (caveId: number) => RewriteHooks): Promise<{
57
63
  recordsUpdated: number;
58
64
  mediaCopied: number;
59
65
  mediaMoved: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-echo-cave",
3
3
  "description": "Group echo cave",
4
- "version": "1.31.1",
4
+ "version": "1.31.2",
5
5
  "main": "lib/index.cjs",
6
6
  "typings": "lib/index.d.ts",
7
7
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "koishi": "^4.18.11"
37
37
  },
38
38
  "dependencies": {
39
- "@aws-sdk/client-s3": "^3.1029.0",
39
+ "@aws-sdk/client-s3": "^3.1030.0",
40
40
  "@aws-sdk/s3-request-presigner": "^3.1030.0",
41
41
  "@pynickle/koishi-plugin-adapter-onebot": "^1.0.0",
42
42
  "axios": "^1.15.0",