koishi-plugin-best-cave 2.2.7 → 2.2.8
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.d.ts +2 -1
- package/lib/index.js +33 -22
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -39,7 +39,8 @@ export interface Config {
|
|
|
39
39
|
caveFormat: string;
|
|
40
40
|
enableSimilarity: boolean;
|
|
41
41
|
textThreshold: number;
|
|
42
|
-
|
|
42
|
+
imageWholeThreshold: number;
|
|
43
|
+
imagePartThreshold: number;
|
|
43
44
|
localPath?: string;
|
|
44
45
|
enableS3: boolean;
|
|
45
46
|
endpoint?: string;
|
package/lib/index.js
CHANGED
|
@@ -445,14 +445,14 @@ async function handleFileUploads(ctx, config, fileManager, logger2, reviewManage
|
|
|
445
445
|
const similarityScores = /* @__PURE__ */ new Map();
|
|
446
446
|
for (const existing of existingColorPHashes) {
|
|
447
447
|
const similarity = hashManager.calculateSimilarity(colorPHash, existing.hash);
|
|
448
|
-
if (similarity >= config.
|
|
448
|
+
if (similarity >= config.imageWholeThreshold) {
|
|
449
449
|
if (!similarityScores.has(existing.cave)) similarityScores.set(existing.cave, {});
|
|
450
450
|
similarityScores.get(existing.cave).colorSim = similarity;
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
for (const existing of existingDHashes) {
|
|
454
454
|
const similarity = hashManager.calculateSimilarity(dHash, existing.hash);
|
|
455
|
-
if (similarity >= config.
|
|
455
|
+
if (similarity >= config.imageWholeThreshold) {
|
|
456
456
|
if (!similarityScores.has(existing.cave)) similarityScores.set(existing.cave, {});
|
|
457
457
|
similarityScores.get(existing.cave).dSim = similarity;
|
|
458
458
|
}
|
|
@@ -475,7 +475,7 @@ async function handleFileUploads(ctx, config, fileManager, logger2, reviewManage
|
|
|
475
475
|
for (const existing of existingSubHashObjects) {
|
|
476
476
|
if (notifiedPartialCaves.has(existing.cave)) continue;
|
|
477
477
|
const similarity = hashManager.calculateSimilarity(newSubHash, existing.hash);
|
|
478
|
-
if (similarity >= config.
|
|
478
|
+
if (similarity >= config.imagePartThreshold) {
|
|
479
479
|
await session.send(`图片局部与回声洞(${existing.cave})的相似度为 ${(similarity * 100).toFixed(2)}%`);
|
|
480
480
|
notifiedPartialCaves.add(existing.cave);
|
|
481
481
|
}
|
|
@@ -710,7 +710,7 @@ var HashManager = class {
|
|
|
710
710
|
}
|
|
711
711
|
}
|
|
712
712
|
await flushBatch();
|
|
713
|
-
return `已补全 ${totalToProcessCount} 个回声洞的 ${totalHashesGenerated}
|
|
713
|
+
return `已补全 ${totalToProcessCount} 个回声洞的 ${totalHashesGenerated} 条哈希(失败 ${errorCount} 条)`;
|
|
714
714
|
}
|
|
715
715
|
/**
|
|
716
716
|
* @description 为单个回声洞对象生成所有类型的哈希。
|
|
@@ -768,20 +768,21 @@ var HashManager = class {
|
|
|
768
768
|
phash_color: /* @__PURE__ */ new Map(),
|
|
769
769
|
dhash_gray: /* @__PURE__ */ new Map()
|
|
770
770
|
};
|
|
771
|
-
const
|
|
771
|
+
const subHashGroups = /* @__PURE__ */ new Map();
|
|
772
772
|
for (const hash of allHashes) {
|
|
773
773
|
if (hashGroups[hash.type]) {
|
|
774
774
|
if (!hashGroups[hash.type].has(hash.cave)) hashGroups[hash.type].set(hash.cave, []);
|
|
775
775
|
hashGroups[hash.type].get(hash.cave).push(hash.hash);
|
|
776
776
|
} else if (hash.type.startsWith("sub_phash_")) {
|
|
777
|
-
if (!
|
|
778
|
-
|
|
777
|
+
if (!subHashGroups.has(hash.cave)) subHashGroups.set(hash.cave, []);
|
|
778
|
+
subHashGroups.get(hash.cave).push(hash.hash);
|
|
779
779
|
}
|
|
780
780
|
}
|
|
781
781
|
const similarPairs = {
|
|
782
782
|
text: /* @__PURE__ */ new Set(),
|
|
783
783
|
image_color: /* @__PURE__ */ new Set(),
|
|
784
|
-
image_dhash: /* @__PURE__ */ new Set()
|
|
784
|
+
image_dhash: /* @__PURE__ */ new Set(),
|
|
785
|
+
image_part: /* @__PURE__ */ new Set()
|
|
785
786
|
};
|
|
786
787
|
for (let i = 0; i < allCaveIds.length; i++) {
|
|
787
788
|
for (let j = i + 1; j < allCaveIds.length; j++) {
|
|
@@ -800,7 +801,7 @@ var HashManager = class {
|
|
|
800
801
|
for (const h1 of colorHashes1) {
|
|
801
802
|
for (const h22 of colorHashes2) {
|
|
802
803
|
const sim = this.calculateSimilarity(h1, h22);
|
|
803
|
-
if (sim >= this.config.
|
|
804
|
+
if (sim >= this.config.imageWholeThreshold) {
|
|
804
805
|
similarPairs.image_color.add(`${id1} & ${id2} = ${(sim * 100).toFixed(2)}%`);
|
|
805
806
|
}
|
|
806
807
|
}
|
|
@@ -810,27 +811,36 @@ var HashManager = class {
|
|
|
810
811
|
for (const h1 of dHashes1) {
|
|
811
812
|
for (const h22 of dHashes2) {
|
|
812
813
|
const sim = this.calculateSimilarity(h1, h22);
|
|
813
|
-
if (sim >= this.config.
|
|
814
|
+
if (sim >= this.config.imageWholeThreshold) {
|
|
814
815
|
similarPairs.image_dhash.add(`${id1} & ${id2} = ${(sim * 100).toFixed(2)}%`);
|
|
815
816
|
}
|
|
816
817
|
}
|
|
817
818
|
}
|
|
819
|
+
const subHashes1 = subHashGroups.get(id1) || [];
|
|
820
|
+
const subHashes2 = subHashGroups.get(id2) || [];
|
|
821
|
+
if (subHashes1.length > 0 && subHashes2.length > 0) {
|
|
822
|
+
let maxPartSim = 0;
|
|
823
|
+
for (const h1 of subHashes1) {
|
|
824
|
+
for (const h22 of subHashes2) {
|
|
825
|
+
const sim = this.calculateSimilarity(h1, h22);
|
|
826
|
+
if (sim > maxPartSim) {
|
|
827
|
+
maxPartSim = sim;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (maxPartSim >= this.config.imagePartThreshold) {
|
|
832
|
+
similarPairs.image_part.add(`${id1} & ${id2} = ${(maxPartSim * 100).toFixed(2)}%`);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
818
835
|
}
|
|
819
836
|
}
|
|
820
|
-
const
|
|
821
|
-
subHashToCaves.forEach((caves2) => {
|
|
822
|
-
if (caves2.size > 1) {
|
|
823
|
-
const sortedCaves = [...caves2].sort((a, b) => a - b).join(", ");
|
|
824
|
-
subHashDuplicates.push(`[${sortedCaves}]`);
|
|
825
|
-
}
|
|
826
|
-
});
|
|
827
|
-
const totalFindings = similarPairs.text.size + similarPairs.image_color.size + similarPairs.image_dhash.size + subHashDuplicates.length;
|
|
837
|
+
const totalFindings = similarPairs.text.size + similarPairs.image_color.size + similarPairs.image_dhash.size + similarPairs.image_part.size;
|
|
828
838
|
if (totalFindings === 0) return "未发现高相似度的内容";
|
|
829
|
-
let report = `已发现 ${totalFindings}
|
|
839
|
+
let report = `已发现 ${totalFindings} 组高相似度的内容:`;
|
|
830
840
|
if (similarPairs.text.size > 0) report += "\n文本近似:\n" + [...similarPairs.text].join("\n");
|
|
831
|
-
if (similarPairs.image_color.size > 0) report += "\n
|
|
841
|
+
if (similarPairs.image_color.size > 0) report += "\n图片颜色相似:\n" + [...similarPairs.image_color].join("\n");
|
|
832
842
|
if (similarPairs.image_dhash.size > 0) report += "\n图片结构相似:\n" + [...similarPairs.image_dhash].join("\n");
|
|
833
|
-
if (
|
|
843
|
+
if (similarPairs.image_part.size > 0) report += "\n图片局部近似:\n" + [...similarPairs.image_part].join("\n");
|
|
834
844
|
return report.trim();
|
|
835
845
|
}
|
|
836
846
|
/**
|
|
@@ -1009,7 +1019,8 @@ var Config = import_koishi3.Schema.intersect([
|
|
|
1009
1019
|
enableReview: import_koishi3.Schema.boolean().default(false).description("启用审核"),
|
|
1010
1020
|
enableSimilarity: import_koishi3.Schema.boolean().default(false).description("启用查重"),
|
|
1011
1021
|
textThreshold: import_koishi3.Schema.number().min(0).max(1).step(0.01).default(0.9).description("文本相似度阈值"),
|
|
1012
|
-
|
|
1022
|
+
imageWholeThreshold: import_koishi3.Schema.number().min(0).max(1).step(0.01).default(0.9).description("图片整体相似度阈值"),
|
|
1023
|
+
imagePartThreshold: import_koishi3.Schema.number().min(0).max(1).step(0.01).default(0.95).description("图片局部相似度阈值")
|
|
1013
1024
|
}).description("复核配置"),
|
|
1014
1025
|
import_koishi3.Schema.object({
|
|
1015
1026
|
localPath: import_koishi3.Schema.string().description("文件映射路径"),
|