kokkoro-plugin-saucenao 2.0.2 → 2.0.3
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/README.md +2 -2
- package/package.json +1 -1
- package/src/index.ts +7 -3
- package/src/service.ts +2 -2
- package/src/util.ts +10 -5
package/README.md
CHANGED
|
@@ -17,11 +17,11 @@ bun add kokkoro-plugin-saucenao
|
|
|
17
17
|
/搜图
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
发送「/搜图」指令时附带图片,或引用包含图片的消息回复该指令。插件搜索首张图片的来源,并通过 QQ Markdown 返回结果,默认显示相似度最高的 3 条。
|
|
21
21
|
|
|
22
22
|
## 快捷方式
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
「搜图」与「/搜图」的用法相同。在普通群消息中使用快捷方式,需要开启「获取群内全部消息」权限。
|
|
25
25
|
|
|
26
26
|
```text
|
|
27
27
|
搜图
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -7,20 +7,24 @@ const logger = useLogger();
|
|
|
7
7
|
|
|
8
8
|
export default () => {
|
|
9
9
|
useCommand('/搜图', async context => {
|
|
10
|
-
const
|
|
10
|
+
const [reference] = context.msg_elements ?? [];
|
|
11
|
+
const attachments = context.message_type === 103 ? reference?.attachments : context.attachments;
|
|
12
|
+
const images = attachments?.filter(attachment => attachment.content_type?.startsWith('image/')) ?? [];
|
|
13
|
+
const [image] = images;
|
|
11
14
|
|
|
12
15
|
if (!image?.url) {
|
|
13
|
-
throw new Error('
|
|
16
|
+
throw new Error('未检测到图片,请在指令消息中附带图片,或引用包含图片的消息后重试。');
|
|
14
17
|
}
|
|
15
18
|
const result = await fetchImageSources(image.url, logger);
|
|
16
19
|
const { results } = result;
|
|
20
|
+
const notice = images.length > 1 ? '检测到消息中包含多张图片,已选取首张图片进行识别。' : undefined;
|
|
17
21
|
|
|
18
22
|
logger.info('已找到图片来源', { count: results.length });
|
|
19
23
|
|
|
20
24
|
await context.reply({
|
|
21
25
|
msg_type: 2,
|
|
22
26
|
markdown: {
|
|
23
|
-
content: await createMarkdown(results),
|
|
27
|
+
content: await createMarkdown(results, notice),
|
|
24
28
|
},
|
|
25
29
|
});
|
|
26
30
|
}).shortcut('搜图');
|
package/src/service.ts
CHANGED
|
@@ -146,7 +146,7 @@ export async function fetchImageSources(url: string, logger?: Logger): Promise<S
|
|
|
146
146
|
const response = await fetch(endpoint);
|
|
147
147
|
|
|
148
148
|
if (!response.ok) {
|
|
149
|
-
throw new Error(
|
|
149
|
+
throw new Error(`SauceNAO 请求失败,HTTP 状态码 ${response.status}`);
|
|
150
150
|
}
|
|
151
151
|
const result = <SauceNao>await response.json();
|
|
152
152
|
const { header, results } = result;
|
|
@@ -158,7 +158,7 @@ export async function fetchImageSources(url: string, logger?: Logger): Promise<S
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if (results.length === 0) {
|
|
161
|
-
throw new Error('
|
|
161
|
+
throw new Error('未找到图片来源');
|
|
162
162
|
}
|
|
163
163
|
return result;
|
|
164
164
|
}
|
package/src/util.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Image } from 'bun';
|
|
|
2
2
|
|
|
3
3
|
import { type ImageSource } from './service';
|
|
4
4
|
|
|
5
|
-
const { SAUCENAO_SIMILARITY_THRESHOLD = 50 } = import.meta.env;
|
|
5
|
+
const { SAUCENAO_SIMILARITY_THRESHOLD = '50' } = import.meta.env;
|
|
6
6
|
const LOW_SIMILARITY_THUMBNAIL = 'https://kokkoro.js.org/saucenao-low-similarity.jpg';
|
|
7
7
|
const similarityThreshold = Number(SAUCENAO_SIMILARITY_THRESHOLD);
|
|
8
8
|
|
|
@@ -28,8 +28,13 @@ function getUrl(data: ImageSource['data']) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
/** 将 SauceNAO 搜索结果转换为 QQ Markdown
|
|
32
|
-
export async function createMarkdown(results: readonly ImageSource[]): Promise<string> {
|
|
31
|
+
/** 将 SauceNAO 搜索结果转换为 QQ Markdown,可在标题下方展示提示。 */
|
|
32
|
+
export async function createMarkdown(results: readonly ImageSource[], notice?: string): Promise<string> {
|
|
33
|
+
const lines = ['## SauceNAO 搜图结果'];
|
|
34
|
+
|
|
35
|
+
if (notice) {
|
|
36
|
+
lines.push('', `> ${notice}`);
|
|
37
|
+
}
|
|
33
38
|
const content = await Promise.all(
|
|
34
39
|
results.map(async ({ data, header }, index) => {
|
|
35
40
|
const { index_name: indexName, similarity, thumbnail } = header;
|
|
@@ -45,7 +50,7 @@ export async function createMarkdown(results: readonly ImageSource[]): Promise<s
|
|
|
45
50
|
const response = await fetch(thumbnail);
|
|
46
51
|
|
|
47
52
|
if (!response.ok) {
|
|
48
|
-
throw new Error(
|
|
53
|
+
throw new Error(`缩略图下载失败,HTTP 状态码 ${response.status}`);
|
|
49
54
|
}
|
|
50
55
|
const { height, width } = await new Image(await response.blob()).metadata();
|
|
51
56
|
|
|
@@ -60,5 +65,5 @@ export async function createMarkdown(results: readonly ImageSource[]): Promise<s
|
|
|
60
65
|
}),
|
|
61
66
|
);
|
|
62
67
|
|
|
63
|
-
return ['
|
|
68
|
+
return [lines.join('\n'), ...content].join('\n***\n');
|
|
64
69
|
}
|