koishi-plugin-echo-cave 1.28.1 → 1.28.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.
@@ -3,4 +3,4 @@ import { Context, Session } from 'koishi';
3
3
  export declare function mergeCavesBetweenChannels(ctx: Context, session: Session, cfg: Config, sourceChannelId: string, targetChannelId: string, keepSourceOption?: string): Promise<string>;
4
4
  export declare function migrateLegacyLocalMedia(ctx: Context, session: Session, cfg: Config): Promise<string>;
5
5
  export declare function migrateMediaToS3(ctx: Context, session: Session, cfg: Config, keepLocalOption?: string): Promise<string>;
6
- export declare function inspectMediaRefsForMigration(ctx: Context, session: Session, cfg: Config): Promise<string>;
6
+ export declare function inspectMediaRefsForMigration(ctx: Context, session: Session, cfg: Config, idRangesOption?: string): Promise<string>;
package/lib/index.cjs CHANGED
@@ -33555,7 +33555,11 @@ function getLocalMediaV2Root(ctx) {
33555
33555
  return import_node_path.default.join(getLocalMediaRoot(ctx), "v2");
33556
33556
  }
33557
33557
  function getLocalMediaV2Dir(ctx, channelId, type) {
33558
- return import_node_path.default.join(getLocalMediaV2Root(ctx), encodeURIComponent(channelId), getMediaDirName(type));
33558
+ return import_node_path.default.join(
33559
+ getLocalMediaV2Root(ctx),
33560
+ encodeURIComponent(channelId),
33561
+ getMediaDirName(type)
33562
+ );
33559
33563
  }
33560
33564
  function getLegacyMediaDir(ctx, type) {
33561
33565
  return import_node_path.default.join(getLocalMediaRoot(ctx), getMediaDirName(type));
@@ -33840,7 +33844,12 @@ async function transferS3ObjectToChannel(cfg, source, type, targetChannelId, mod
33840
33844
  throw new Error("S3 bucket is not configured.");
33841
33845
  }
33842
33846
  const extension = import_node_path.default.extname(source.key).slice(1) || getDefaultExtension(type);
33843
- const key = buildS3Key(cfg, targetChannelId, type, `${(0, import_uuid2.v4)().replace(/-/g, "")}.${extension}`);
33847
+ const key = buildS3Key(
33848
+ cfg,
33849
+ targetChannelId,
33850
+ type,
33851
+ `${(0, import_uuid2.v4)().replace(/-/g, "")}.${extension}`
33852
+ );
33844
33853
  const target = { bucket, key };
33845
33854
  if (source.bucket === target.bucket && source.key === target.key) {
33846
33855
  return toS3Uri(source);
@@ -33919,7 +33928,13 @@ async function transferMediaRefToChannel(ctx, fileRef, type, targetChannelId, ta
33919
33928
  }
33920
33929
  const loaded2 = await loadMediaBuffer(ctx, fileRef, cfg);
33921
33930
  const extension2 = import_node_path.default.extname(loaded2.sourceKey).slice(1) || getDefaultExtension(type);
33922
- const targetPath = await writeLocalMedia(ctx, targetChannelId, type, loaded2.buffer, extension2);
33931
+ const targetPath = await writeLocalMedia(
33932
+ ctx,
33933
+ targetChannelId,
33934
+ type,
33935
+ loaded2.buffer,
33936
+ extension2
33937
+ );
33923
33938
  const plan2 = {
33924
33939
  nextRef: toFileUri(targetPath),
33925
33940
  rollback: createDeleteLocalFilePlan(targetPath).rollback
@@ -33948,7 +33963,13 @@ async function transferMediaRefToChannel(ctx, fileRef, type, targetChannelId, ta
33948
33963
  if (!source) {
33949
33964
  throw new Error(`Invalid S3 uri: ${fileRef}`);
33950
33965
  }
33951
- const nextRef2 = await transferS3ObjectToChannel(cfg, source, type, targetChannelId, transferMode);
33966
+ const nextRef2 = await transferS3ObjectToChannel(
33967
+ cfg,
33968
+ source,
33969
+ type,
33970
+ targetChannelId,
33971
+ transferMode
33972
+ );
33952
33973
  const targetLocation2 = parseS3Uri(nextRef2);
33953
33974
  const plan2 = {
33954
33975
  nextRef: nextRef2,
@@ -34034,7 +34055,9 @@ async function mutateMessageContent(ctx, content, handler) {
34034
34055
  const nextContent = await Promise.all(
34035
34056
  contentValue.map(async (child) => mutateElement(child))
34036
34057
  );
34037
- const hasChildChange = nextContent.some((child, index) => child !== contentValue[index]);
34058
+ const hasChildChange = nextContent.some(
34059
+ (child, index) => child !== contentValue[index]
34060
+ );
34038
34061
  if (hasChildChange) {
34039
34062
  changed = true;
34040
34063
  return {
@@ -34077,10 +34100,13 @@ async function collectMessageMediaRefs(ctx, content) {
34077
34100
  }
34078
34101
  return refs;
34079
34102
  }
34080
- async function inspectCaveMediaRefs(ctx) {
34103
+ async function inspectCaveMediaRefs(ctx, shouldInclude) {
34081
34104
  const caves = await ctx.database.get("echo_cave_v2", {});
34082
34105
  const results = [];
34083
34106
  for (const cave of caves) {
34107
+ if (shouldInclude && !shouldInclude(cave.id)) {
34108
+ continue;
34109
+ }
34084
34110
  try {
34085
34111
  const refs = await collectMessageMediaRefs(ctx, cave.content);
34086
34112
  if (refs.length > 0) {
@@ -34361,7 +34387,9 @@ async function checkAndCleanMediaFiles(ctx, cfg, type) {
34361
34387
  }
34362
34388
  const totalSize = fileInfos.reduce((sum, file) => sum + file.size, 0);
34363
34389
  if (totalSize <= maxSize) {
34364
- ctx.logger.debug(`${type} check completed in ${Date.now() - startTime}ms: no cleanup needed`);
34390
+ ctx.logger.debug(
34391
+ `${type} check completed in ${Date.now() - startTime}ms: no cleanup needed`
34392
+ );
34365
34393
  return;
34366
34394
  }
34367
34395
  fileInfos.sort((a5, b5) => a5.mtime - b5.mtime);
@@ -34591,7 +34619,9 @@ async function processForwardMessageContent(ctx, session, msg, cfg) {
34591
34619
  // src/core/parser/msg-parser.ts
34592
34620
  async function processMessageContent(ctx, msg, cfg, channelId) {
34593
34621
  return Promise.all(
34594
- msg.map(async (element) => await processMediaElement(ctx, element, cfg, channelId))
34622
+ msg.map(
34623
+ async (element) => await processMediaElement(ctx, element, cfg, channelId)
34624
+ )
34595
34625
  );
34596
34626
  }
34597
34627
 
@@ -34785,6 +34815,40 @@ function appendFailedRecordSummary(message, failedRecordIds) {
34785
34815
  return `${message}
34786
34816
  \u26A0\uFE0F \u5931\u8D25\u8BB0\u5F55 ID\uFF1A${failedRecordIds.join(", ")}`;
34787
34817
  }
34818
+ function parseIdRanges(value) {
34819
+ if (!value?.trim()) {
34820
+ return [];
34821
+ }
34822
+ const segments = value.split(",").map((segment) => segment.trim()).filter(Boolean);
34823
+ if (segments.length === 0) {
34824
+ return [];
34825
+ }
34826
+ const ranges = [];
34827
+ for (const segment of segments) {
34828
+ if (/^\d+$/.test(segment)) {
34829
+ const id = Number(segment);
34830
+ ranges.push({ start: id, end: id });
34831
+ continue;
34832
+ }
34833
+ const rangeMatch = segment.match(/^(\d+)-(\d+)$/);
34834
+ if (!rangeMatch) {
34835
+ return null;
34836
+ }
34837
+ const start = Number(rangeMatch[1]);
34838
+ const end = Number(rangeMatch[2]);
34839
+ if (start > end) {
34840
+ return null;
34841
+ }
34842
+ ranges.push({ start, end });
34843
+ }
34844
+ return ranges;
34845
+ }
34846
+ function isIdInRanges(id, ranges) {
34847
+ if (ranges.length === 0) {
34848
+ return true;
34849
+ }
34850
+ return ranges.some((range2) => id >= range2.start && id <= range2.end);
34851
+ }
34788
34852
  async function requestSecondConfirmation(ctx, session, summary, retryMessage, timeoutMessage, cancelledMessage) {
34789
34853
  return new Promise((resolve) => {
34790
34854
  listenForUserMessage(
@@ -34915,14 +34979,35 @@ async function migrateMediaToS3(ctx, session, cfg, keepLocalOption) {
34915
34979
  result.failedRecordIds
34916
34980
  );
34917
34981
  }
34918
- async function inspectMediaRefsForMigration(ctx, session, cfg) {
34982
+ async function inspectMediaRefsForMigration(ctx, session, cfg, idRangesOption) {
34919
34983
  const accessError = ensureAdminPrivateAccess(session, cfg);
34920
34984
  if (accessError) {
34921
34985
  return accessError;
34922
34986
  }
34923
- const results = await inspectCaveMediaRefs(ctx);
34987
+ const idRanges = parseIdRanges(idRangesOption);
34988
+ if (idRanges === null) {
34989
+ return session.text("commands.cave.admin.inspect-media.messages.invalidRange");
34990
+ }
34991
+ const displayRanges = idRangesOption?.trim() || "\u5168\u90E8";
34992
+ const results = await inspectCaveMediaRefs(ctx, (id) => isIdInRanges(id, idRanges));
34924
34993
  if (results.length === 0) {
34925
- return session.text("commands.cave.admin.inspect-media.messages.noMediaFound");
34994
+ return session.text("commands.cave.admin.inspect-media.messages.noMediaFound", {
34995
+ idRanges: displayRanges
34996
+ });
34997
+ }
34998
+ const confirmed = await requestSecondConfirmation(
34999
+ ctx,
35000
+ session,
35001
+ session.text("commands.cave.admin.inspect-media.messages.confirmSummary", {
35002
+ idRanges: displayRanges,
35003
+ matchedCount: results.length
35004
+ }),
35005
+ session.text("commands.cave.admin.inspect-media.messages.confirmRetry"),
35006
+ session.text("commands.cave.admin.inspect-media.messages.confirmTimeout"),
35007
+ session.text("commands.cave.admin.inspect-media.messages.confirmCancelled")
35008
+ );
35009
+ if (!confirmed) {
35010
+ return;
34926
35011
  }
34927
35012
  for (const { id, refs } of results) {
34928
35013
  await session.send(`\u56DE\u58F0\u6D1E #${id}
@@ -35050,7 +35135,9 @@ async function sendCaveMsg(ctx, session, caveMsg, cfg) {
35050
35135
  const { channelId } = session;
35051
35136
  let content = JSON.parse(caveMsg.content);
35052
35137
  content = await Promise.all(
35053
- content.map(async (element) => await resolveMediaElementForSend(ctx, element, cfg))
35138
+ content.map(
35139
+ async (element) => await resolveMediaElementForSend(ctx, element, cfg)
35140
+ )
35054
35141
  );
35055
35142
  const date2 = formatDate(caveMsg.createTime);
35056
35143
  const originName = await getUserName(ctx, session, caveMsg.originUserId);
@@ -35621,7 +35708,12 @@ var zh_CN_default = {
35621
35708
  "cave.admin.inspect-media": {
35622
35709
  description: "\u68C0\u6D4B\u56DE\u58F0\u6D1E\u4E2D\u7684\u5A92\u4F53\u94FE\u63A5\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u8FC1\u79FB",
35623
35710
  messages: {
35624
- noMediaFound: "\u{1F50D} \u672A\u68C0\u6D4B\u5230\u4EFB\u4F55\u5A92\u4F53\u94FE\u63A5\u3002"
35711
+ invalidRange: "\u274C \u8303\u56F4\u53C2\u6570\u683C\u5F0F\u65E0\u6548\u3002\u8BF7\u4F7F\u7528 0-100,105-240 \u6216 12,18-20 \u8FD9\u6837\u7684\u683C\u5F0F\u3002",
35712
+ noMediaFound: "\u{1F50D} \u5728\u8303\u56F4 {idRanges} \u5185\u672A\u68C0\u6D4B\u5230\u4EFB\u4F55\u5A92\u4F53\u94FE\u63A5\u3002",
35713
+ 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",
35714
+ 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",
35715
+ confirmCancelled: "\u{1F6D1} \u5DF2\u53D6\u6D88\u672C\u6B21\u68C0\u6D4B\u3002",
35716
+ confirmTimeout: "\u231B \u4E8C\u6B21\u786E\u8BA4\u8D85\u65F6\uFF0C\u68C0\u6D4B\u672A\u6267\u884C\u3002"
35625
35717
  }
35626
35718
  }
35627
35719
  }
@@ -35764,7 +35856,9 @@ function apply(ctx, cfg) {
35764
35856
  ctx.command("cave.rank [period:string]").action(
35765
35857
  async ({ session }, period) => await getRanking(ctx, session, cfg, period)
35766
35858
  );
35767
- ctx.command("cave.admin.merge <sourceChannelId:string> <targetChannelId:string> [keepSource:string]").action(
35859
+ ctx.command(
35860
+ "cave.admin.merge <sourceChannelId:string> <targetChannelId:string> [keepSource:string]"
35861
+ ).action(
35768
35862
  async ({ session }, sourceChannelId, targetChannelId, keepSource) => await mergeCavesBetweenChannels(
35769
35863
  ctx,
35770
35864
  session,
@@ -35780,8 +35874,8 @@ function apply(ctx, cfg) {
35780
35874
  ctx.command("cave.admin.migrate-s3 [keepLocal:string]").action(
35781
35875
  async ({ session }, keepLocal) => await migrateMediaToS3(ctx, session, cfg, keepLocal)
35782
35876
  );
35783
- ctx.command("cave.admin.inspect-media").action(
35784
- async ({ session }) => await inspectMediaRefsForMigration(ctx, session, cfg)
35877
+ ctx.command("cave.admin.inspect-media [idRanges:text]").action(
35878
+ async ({ session }, idRanges) => await inspectMediaRefsForMigration(ctx, session, cfg, idRanges)
35785
35879
  );
35786
35880
  }
35787
35881
  // Annotate the CommonJS export names for ESM import in node:
@@ -14,7 +14,7 @@ interface CaveMediaRefs {
14
14
  id: number;
15
15
  refs: string[];
16
16
  }
17
- export declare function inspectCaveMediaRefs(ctx: Context): Promise<CaveMediaRefs[]>;
17
+ export declare function inspectCaveMediaRefs(ctx: Context, shouldInclude?: (caveId: number) => boolean): Promise<CaveMediaRefs[]>;
18
18
  export declare function saveMedia(ctx: Context, mediaElement: Record<string, unknown>, type: MediaType, cfg: Config, channelId: string): Promise<string>;
19
19
  export declare function processMediaElement(ctx: Context, element: MaybeMediaElement, cfg: Config, channelId: string): Promise<MaybeMediaElement>;
20
20
  export declare function resolveMediaElementForSend(ctx: Context, element: MaybeMediaElement, cfg: Config): Promise<MaybeMediaElement>;
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.28.1",
4
+ "version": "1.28.2",
5
5
  "main": "lib/index.cjs",
6
6
  "typings": "lib/index.d.ts",
7
7
  "type": "module",