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.
- package/lib/core/command/admin.d.ts +1 -1
- package/lib/index.cjs +110 -16
- package/lib/utils/media/media-helper.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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>;
|