koishi-plugin-video-parser-all 1.4.8 → 1.5.0
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 +18 -12
- package/lib/index.js +61 -49
- package/package.json +1 -1
- package/readme.md +163 -150
package/lib/index.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { Context, Schema } from 'koishi';
|
|
2
|
-
declare module 'koishi' {
|
|
3
|
-
interface Context {
|
|
4
|
-
downloads?: {
|
|
5
|
-
download(url: string, dest: string, options?: Record<string, unknown>): Promise<string>;
|
|
6
|
-
};
|
|
7
|
-
aria2?: any;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
2
|
export declare const name = "video-parser-all";
|
|
11
3
|
export declare const Config: Schema<{
|
|
12
4
|
enable?: boolean | null | undefined;
|
|
@@ -45,10 +37,17 @@ export declare const Config: Schema<{
|
|
|
45
37
|
} & {
|
|
46
38
|
showImageText?: boolean | null | undefined;
|
|
47
39
|
showCoverImage?: boolean | null | undefined;
|
|
40
|
+
showCoverFile?: boolean | null | undefined;
|
|
41
|
+
showImageFileNew?: boolean | null | undefined;
|
|
42
|
+
showAuthorAvatar?: boolean | null | undefined;
|
|
43
|
+
showAuthorAvatarFile?: boolean | null | undefined;
|
|
44
|
+
showAuthorAvatarText?: boolean | null | undefined;
|
|
45
|
+
authorAvatarText?: string | null | undefined;
|
|
48
46
|
showMusicCover?: boolean | null | undefined;
|
|
49
|
-
showImageFile?: boolean | null | undefined;
|
|
50
47
|
showVideoFile?: boolean | null | undefined;
|
|
51
|
-
|
|
48
|
+
forceDownloadCover?: boolean | null | undefined;
|
|
49
|
+
forceDownloadImageNew?: boolean | null | undefined;
|
|
50
|
+
forceDownloadAuthorAvatar?: boolean | null | undefined;
|
|
52
51
|
forceDownloadVideo?: boolean | null | undefined;
|
|
53
52
|
} & {
|
|
54
53
|
showMusicVoice?: boolean | null | undefined;
|
|
@@ -190,10 +189,17 @@ export declare const Config: Schema<{
|
|
|
190
189
|
} & {
|
|
191
190
|
showImageText: boolean;
|
|
192
191
|
showCoverImage: boolean;
|
|
192
|
+
showCoverFile: boolean;
|
|
193
|
+
showImageFileNew: boolean;
|
|
194
|
+
showAuthorAvatar: boolean;
|
|
195
|
+
showAuthorAvatarFile: boolean;
|
|
196
|
+
showAuthorAvatarText: boolean;
|
|
197
|
+
authorAvatarText: string;
|
|
193
198
|
showMusicCover: boolean;
|
|
194
|
-
showImageFile: boolean;
|
|
195
199
|
showVideoFile: boolean;
|
|
196
|
-
|
|
200
|
+
forceDownloadCover: boolean;
|
|
201
|
+
forceDownloadImageNew: boolean;
|
|
202
|
+
forceDownloadAuthorAvatar: boolean;
|
|
197
203
|
forceDownloadVideo: boolean;
|
|
198
204
|
} & {
|
|
199
205
|
showMusicVoice: boolean;
|
package/lib/index.js
CHANGED
|
@@ -103,15 +103,22 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
103
103
|
}).description('各平台解析开关'),
|
|
104
104
|
}).description('基本设置'),
|
|
105
105
|
koishi_1.Schema.object({
|
|
106
|
-
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n音乐标题:${音乐标题}\n音乐作者:${音乐作者}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('
|
|
106
|
+
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default('标题:${标题}\n作者:${作者}\n简介:${简介}\n音乐标题:${音乐标题}\n音乐作者:${音乐作者}\n点赞:${点赞数}\n收藏:${收藏数}\n转发:${转发数}\n播放:${播放数}\n评论:${评论数}\n图片数量:${图片数量}').description('文字格式,支持变量:${标题} ${作者} ${简介} ${视频时长} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${发布时间} ${图片数量} ${作者ID} ${音乐标题} ${音乐作者},空行自动隐藏'),
|
|
107
107
|
}).description('消息格式'),
|
|
108
108
|
koishi_1.Schema.object({
|
|
109
109
|
showImageText: koishi_1.Schema.boolean().default(true).description('发送文字内容'),
|
|
110
110
|
showCoverImage: koishi_1.Schema.boolean().default(true).description('发送封面图片'),
|
|
111
|
+
showCoverFile: koishi_1.Schema.boolean().default(true).description('封面是否以图片形式发送(关闭则只发送链接)'),
|
|
112
|
+
showImageFileNew: koishi_1.Schema.boolean().default(true).description('图片是否以图片形式发送(关闭则只发送链接)'),
|
|
113
|
+
showAuthorAvatar: koishi_1.Schema.boolean().default(true).description('发送作者头像图片'),
|
|
114
|
+
showAuthorAvatarFile: koishi_1.Schema.boolean().default(true).description('作者头像图片是否以图片形式发送(关闭则只发送链接)'),
|
|
115
|
+
showAuthorAvatarText: koishi_1.Schema.boolean().default(true).description('发送作者头像前显示文字提示'),
|
|
116
|
+
authorAvatarText: koishi_1.Schema.string().default('作者头像:').description('作者头像前显示的文字'),
|
|
111
117
|
showMusicCover: koishi_1.Schema.boolean().default(true).description('发送音乐封面图片'),
|
|
112
|
-
showImageFile: koishi_1.Schema.boolean().default(true).description('封面/图片是否以图片形式发送(关闭则只发送链接)'),
|
|
113
118
|
showVideoFile: koishi_1.Schema.boolean().default(true).description('视频是否以视频形式发送(关闭则只发送链接)'),
|
|
114
|
-
|
|
119
|
+
forceDownloadCover: koishi_1.Schema.boolean().default(false).description('强制下载封面'),
|
|
120
|
+
forceDownloadImageNew: koishi_1.Schema.boolean().default(false).description('强制下载图片'),
|
|
121
|
+
forceDownloadAuthorAvatar: koishi_1.Schema.boolean().default(false).description('强制下载作者头像'),
|
|
115
122
|
forceDownloadVideo: koishi_1.Schema.boolean().default(false).description('强制下载视频'),
|
|
116
123
|
}).description('媒体发送'),
|
|
117
124
|
koishi_1.Schema.object({
|
|
@@ -623,17 +630,6 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
|
|
|
623
630
|
const author_followers = parseCount(mapField('author_followers', () => extra.author_extra?.follower_count ?? data.author_extra?.follower_count ?? 0));
|
|
624
631
|
const author_signature = String(mapField('author_signature', () => extra.author_extra?.signature ?? data.author_extra?.signature ?? ''));
|
|
625
632
|
const admire = parseCount(mapField('admire', () => extra.statistics?.admire_count ?? data.statistics?.admire_count ?? 0));
|
|
626
|
-
let hashtags = '';
|
|
627
|
-
const tagsArray = mapField('hashtags', () => extra.hashtags ?? data.hashtags);
|
|
628
|
-
if (Array.isArray(tagsArray) && tagsArray.length > 0) {
|
|
629
|
-
const tagNames = tagsArray.map((t) => t?.name ? t.name : '').filter(Boolean);
|
|
630
|
-
hashtags = tagNames.map(name => `#${name}`).join(' ');
|
|
631
|
-
if (tagNames.length > 0 && desc) {
|
|
632
|
-
const escapedTags = tagNames.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
633
|
-
const tagRegex = new RegExp(`[##](${escapedTags.join('|')})\\b`, 'gi');
|
|
634
|
-
desc = desc.replace(tagRegex, '').replace(/\s{2,}/g, ' ').trim();
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
633
|
title = title.replace(/\[话题\]/g, '');
|
|
638
634
|
desc = desc.replace(/\[话题\]/g, '');
|
|
639
635
|
if (title && desc && title.trim() === desc.trim()) {
|
|
@@ -643,7 +639,7 @@ function parseApiResponse(raw, maxDescLen, fieldMapping) {
|
|
|
643
639
|
title = '';
|
|
644
640
|
if (desc.trim().startsWith('#'))
|
|
645
641
|
desc = '';
|
|
646
|
-
return { type, title, desc, author, uid, avatar, cover, video, videos, images, live_photo, music, like, comment, collect, share, play, duration, publishTime, author_followers, author_signature, admire
|
|
642
|
+
return { type, title, desc, author, uid, avatar, cover, video, videos, images, live_photo, music, like, comment, collect, share, play, duration, publishTime, author_followers, author_signature, admire };
|
|
647
643
|
}
|
|
648
644
|
const formatVarRegex = /\$\{([^}]+)\}/g;
|
|
649
645
|
function generateFormattedText(p, format) {
|
|
@@ -651,8 +647,6 @@ function generateFormattedText(p, format) {
|
|
|
651
647
|
const vars = {
|
|
652
648
|
'标题': p.title,
|
|
653
649
|
'作者': p.author,
|
|
654
|
-
'作者名称': p.author,
|
|
655
|
-
'author-name': p.author,
|
|
656
650
|
'简介': p.desc,
|
|
657
651
|
'视频时长': p.duration > 0 ? formatDuration(p.duration) : '',
|
|
658
652
|
'点赞数': String(p.like),
|
|
@@ -663,22 +657,8 @@ function generateFormattedText(p, format) {
|
|
|
663
657
|
'发布时间': p.publishTime ? formatPublishTime(p.publishTime) : '',
|
|
664
658
|
'图片数量': String(imageCount),
|
|
665
659
|
'作者ID': p.uid,
|
|
666
|
-
'author-id': p.uid,
|
|
667
|
-
'视频链接': p.video,
|
|
668
660
|
'音乐标题': p.music.title || '',
|
|
669
|
-
'music-title': p.music.title || '',
|
|
670
661
|
'音乐作者': p.music.author || '',
|
|
671
|
-
'music-author': p.music.author || '',
|
|
672
|
-
'音乐封面': p.music.cover || '',
|
|
673
|
-
'author-avatar': p.avatar,
|
|
674
|
-
'作者粉丝数': String(p.author_followers),
|
|
675
|
-
'author-followers': String(p.author_followers),
|
|
676
|
-
'作者签名': p.author_signature,
|
|
677
|
-
'author-signature': p.author_signature,
|
|
678
|
-
'赞赏数': String(p.admire),
|
|
679
|
-
'admire-count': String(p.admire),
|
|
680
|
-
'话题标签': p.hashtags,
|
|
681
|
-
'hashtags': p.hashtags,
|
|
682
662
|
};
|
|
683
663
|
const varReplacements = Object.entries(vars).map(([key, val]) => ({
|
|
684
664
|
regex: new RegExp(`\\$\\{${key}\\}`, 'g'),
|
|
@@ -773,9 +753,14 @@ function apply(ctx, config) {
|
|
|
773
753
|
const mediaDownloadTimeout = config.mediaDownloadTimeout ?? 120000;
|
|
774
754
|
const maxMediaSize = config.maxMediaSize ?? 0;
|
|
775
755
|
const downloadEngine = config.downloadEngine || 'internal';
|
|
776
|
-
|
|
756
|
+
const aria2Service = ctx.get('aria2');
|
|
757
|
+
const downloadsService = ctx.get('downloads');
|
|
758
|
+
if (downloadEngine === 'aria2' && !aria2Service) {
|
|
777
759
|
logger.warn('选择了 aria2 下载引擎,但未检测到 koishi-plugin-aria2-plus 服务,将回退到内置下载');
|
|
778
760
|
}
|
|
761
|
+
if (downloadEngine === 'downloads' && !downloadsService) {
|
|
762
|
+
logger.warn('选择了 downloads 下载引擎,但未检测到 koishi-plugin-downloads 服务,将回退到内置下载');
|
|
763
|
+
}
|
|
779
764
|
function getPlatformConfig(type) {
|
|
780
765
|
if (type.startsWith('custom_')) {
|
|
781
766
|
const name = type.slice(7);
|
|
@@ -853,9 +838,9 @@ function apply(ctx, config) {
|
|
|
853
838
|
}) || fileExts[0];
|
|
854
839
|
const fileName = `${filePrefix}_${Date.now()}_${(0, crypto_1.randomBytes)(4).toString('hex')}.${ext}`;
|
|
855
840
|
const filePath = path_1.default.resolve(cacheDir, fileName);
|
|
856
|
-
if (downloadEngine === 'downloads' &&
|
|
841
|
+
if (downloadEngine === 'downloads' && downloadsService) {
|
|
857
842
|
logger.info(`开始下载: ${url}`);
|
|
858
|
-
const dest = await
|
|
843
|
+
const dest = await downloadsService.download(url, path_1.default.join(cacheDir, fileName), {
|
|
859
844
|
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
|
|
860
845
|
timeout
|
|
861
846
|
});
|
|
@@ -868,9 +853,9 @@ function apply(ctx, config) {
|
|
|
868
853
|
}
|
|
869
854
|
return dest;
|
|
870
855
|
}
|
|
871
|
-
if (downloadEngine === 'aria2' &&
|
|
856
|
+
if (downloadEngine === 'aria2' && aria2Service) {
|
|
872
857
|
try {
|
|
873
|
-
const gid = await
|
|
858
|
+
const gid = await aria2Service.addUri([url], {
|
|
874
859
|
dir: cacheDir,
|
|
875
860
|
out: fileName,
|
|
876
861
|
split: 4,
|
|
@@ -887,10 +872,10 @@ function apply(ctx, config) {
|
|
|
887
872
|
let lastProgressTime = 0;
|
|
888
873
|
while (!completed) {
|
|
889
874
|
if (Date.now() - startTime > timeout) {
|
|
890
|
-
await
|
|
875
|
+
await aria2Service.remove(gid).catch(() => { });
|
|
891
876
|
throw new Error('aria2下载超时');
|
|
892
877
|
}
|
|
893
|
-
const status = await
|
|
878
|
+
const status = await aria2Service.tellStatus(gid);
|
|
894
879
|
if (status.status === 'complete') {
|
|
895
880
|
completed = true;
|
|
896
881
|
if (onProgress)
|
|
@@ -904,7 +889,7 @@ function apply(ctx, config) {
|
|
|
904
889
|
lastProgressTime = Date.now();
|
|
905
890
|
const total = parseInt(status.totalLength) || 0;
|
|
906
891
|
const completed = parseInt(status.completedLength) || 0;
|
|
907
|
-
const percent = total > 0 ? Math.round((completed / total) * 100) :
|
|
892
|
+
const percent = total > 0 ? Math.round((completed / total) * 100) : -1;
|
|
908
893
|
const speed = formatSpeed(parseInt(status.downloadSpeed) || 0);
|
|
909
894
|
onProgress(percent, speed);
|
|
910
895
|
}
|
|
@@ -951,16 +936,27 @@ function apply(ctx, config) {
|
|
|
951
936
|
let lastProgressTime = 0;
|
|
952
937
|
response.data.on('data', (chunk) => {
|
|
953
938
|
downloaded += chunk.length;
|
|
954
|
-
if (onProgress &&
|
|
939
|
+
if (onProgress && Date.now() - lastProgressTime > 500) {
|
|
955
940
|
lastProgressTime = Date.now();
|
|
956
|
-
|
|
957
|
-
|
|
941
|
+
if (contentLength > 0) {
|
|
942
|
+
const percent = Math.round((downloaded / contentLength) * 100);
|
|
943
|
+
onProgress(percent, '');
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
onProgress(-1, formatSpeed(downloaded / ((Date.now() - lastProgressTime) / 1000)));
|
|
947
|
+
}
|
|
958
948
|
}
|
|
959
949
|
});
|
|
960
950
|
try {
|
|
961
951
|
await (0, promises_2.pipeline)(response.data, writer);
|
|
962
|
-
if (onProgress)
|
|
963
|
-
|
|
952
|
+
if (onProgress) {
|
|
953
|
+
if (contentLength > 0) {
|
|
954
|
+
onProgress(100, '');
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
onProgress(-1, formatSpeed(downloaded / ((Date.now() - lastProgressTime) / 1000)));
|
|
958
|
+
}
|
|
959
|
+
}
|
|
964
960
|
return filePath;
|
|
965
961
|
}
|
|
966
962
|
catch (e) {
|
|
@@ -994,8 +990,13 @@ function apply(ctx, config) {
|
|
|
994
990
|
const prefixMap = { image: 'img', video: 'video', audio: 'music' };
|
|
995
991
|
const sendFunc = type === 'audio' ? koishi_1.h.audio : type === 'video' ? koishi_1.h.video : koishi_1.h.image;
|
|
996
992
|
const onProgress = forceDownload ? (percent, speed) => {
|
|
997
|
-
|
|
998
|
-
|
|
993
|
+
if (percent === -1) {
|
|
994
|
+
logger.info(`下载进度: 已下载 ${speed}`);
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
const text = `下载进度: ${percent}%` + (speed ? ` (${speed})` : '');
|
|
998
|
+
logger.info(text);
|
|
999
|
+
}
|
|
999
1000
|
} : undefined;
|
|
1000
1001
|
if (forceDownload) {
|
|
1001
1002
|
try {
|
|
@@ -1107,6 +1108,11 @@ function apply(ctx, config) {
|
|
|
1107
1108
|
const text = item.text;
|
|
1108
1109
|
if (text && config.showImageText)
|
|
1109
1110
|
forwardMessages.push(buildForwardNode(session, text, botName));
|
|
1111
|
+
if (config.showAuthorAvatar && p.avatar) {
|
|
1112
|
+
if (config.showAuthorAvatarText)
|
|
1113
|
+
forwardMessages.push(buildForwardNode(session, config.authorAvatarText || '作者头像:', botName));
|
|
1114
|
+
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.avatar), botName));
|
|
1115
|
+
}
|
|
1110
1116
|
if (p.cover && config.showCoverImage && p.type !== 'live_photo' && p.type !== 'image' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
1111
1117
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.cover), botName));
|
|
1112
1118
|
}
|
|
@@ -1145,12 +1151,18 @@ function apply(ctx, config) {
|
|
|
1145
1151
|
await sendWithTimeout(session, text);
|
|
1146
1152
|
await delay(300);
|
|
1147
1153
|
}
|
|
1154
|
+
if (config.showAuthorAvatar && p.avatar) {
|
|
1155
|
+
if (config.showAuthorAvatarText)
|
|
1156
|
+
await sendWithTimeout(session, config.authorAvatarText || '作者头像:');
|
|
1157
|
+
await sendMedia(session, p.avatar, 'image', config.forceDownloadAuthorAvatar, config.showAuthorAvatarFile).catch(() => { });
|
|
1158
|
+
await delay(300);
|
|
1159
|
+
}
|
|
1148
1160
|
if (p.cover && config.showCoverImage && p.type !== 'live_photo' && p.type !== 'image' && !(p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
1149
|
-
await sendMedia(session, p.cover, 'image', config.
|
|
1161
|
+
await sendMedia(session, p.cover, 'image', config.forceDownloadCover, config.showCoverFile).catch(() => { });
|
|
1150
1162
|
await delay(300);
|
|
1151
1163
|
}
|
|
1152
1164
|
if (config.showMusicCover && p.music.cover) {
|
|
1153
|
-
await sendMedia(session, p.music.cover, 'image',
|
|
1165
|
+
await sendMedia(session, p.music.cover, 'image', false, true).catch(() => { });
|
|
1154
1166
|
await delay(300);
|
|
1155
1167
|
}
|
|
1156
1168
|
if (p.video && (p.type === 'video' || (p.type === 'live' && !p.live_photo?.length && !p.images?.length))) {
|
|
@@ -1160,7 +1172,7 @@ function apply(ctx, config) {
|
|
|
1160
1172
|
if (p.type === 'image' || p.type === 'live_photo' || (p.type === 'live' && (p.live_photo?.length || p.images?.length))) {
|
|
1161
1173
|
const imageUrls = p.images?.length ? p.images : (p.live_photo?.map(lp => lp.image) ?? []);
|
|
1162
1174
|
for (const imgUrl of imageUrls) {
|
|
1163
|
-
await sendMedia(session, imgUrl, 'image', config.
|
|
1175
|
+
await sendMedia(session, imgUrl, 'image', config.forceDownloadImageNew, config.showImageFileNew).catch(() => { });
|
|
1164
1176
|
await delay(200);
|
|
1165
1177
|
}
|
|
1166
1178
|
}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -20,176 +20,189 @@ This is a **multi-platform video/image parsing plugin** developed for the Koishi
|
|
|
20
20
|
|
|
21
21
|
## 配置项说明 (Configuration)
|
|
22
22
|
|
|
23
|
-
### 基本设置
|
|
24
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
25
|
-
|
|
26
|
-
| `enable` | boolean | true | 启用插件 |
|
|
27
|
-
| `botName` | string | 视频解析机器人 | 合并转发中的昵称 |
|
|
28
|
-
| `showWaitingTip` | boolean | true | 显示等待提示 |
|
|
29
|
-
| `debug` | boolean | false | Debug 日志 |
|
|
30
|
-
| `platformEnabled` | object | 全开 | 各平台开关 |
|
|
31
|
-
|
|
32
|
-
### 消息格式
|
|
33
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
34
|
-
|
|
35
|
-
| `unifiedMessageFormat` | string | 见预设 | 文字格式,支持变量,空行自动隐藏 |
|
|
36
|
-
|
|
37
|
-
### 媒体发送
|
|
38
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
39
|
-
|
|
40
|
-
| `showImageText` | boolean | true | 发送文字内容 |
|
|
41
|
-
| `showCoverImage` | boolean | true | 发送封面图片 |
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
|
50
|
-
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
|
|
55
|
-
###
|
|
56
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
57
|
-
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
23
|
+
### 基本设置 (Basic Settings)
|
|
24
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
25
|
+
|----------------|-------------|-------------------|---------------------|
|
|
26
|
+
| `enable` | boolean | true | 启用插件 (Enable plugin) |
|
|
27
|
+
| `botName` | string | 视频解析机器人 | 合并转发中的昵称 (Nickname in forward messages) |
|
|
28
|
+
| `showWaitingTip` | boolean | true | 显示等待提示 (Show waiting tip) |
|
|
29
|
+
| `debug` | boolean | false | Debug 日志 (Debug logging) |
|
|
30
|
+
| `platformEnabled` | object | 全开 (All enabled) | 各平台开关 (Platform switches) |
|
|
31
|
+
|
|
32
|
+
### 消息格式 (Message Format)
|
|
33
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
34
|
+
|----------------|-------------|-------------------|---------------------|
|
|
35
|
+
| `unifiedMessageFormat` | string | 见预设 (See preset) | 文字格式,支持变量,空行自动隐藏 (Text format, supports variables, auto-hide empty lines) |
|
|
36
|
+
|
|
37
|
+
### 媒体发送 (Media Sending)
|
|
38
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
39
|
+
|----------------|-------------|-------------------|---------------------|
|
|
40
|
+
| `showImageText` | boolean | true | 发送文字内容 (Send text content) |
|
|
41
|
+
| `showCoverImage` | boolean | true | 发送封面图片 (Send cover image) |
|
|
42
|
+
| `showCoverFile` | boolean | true | 封面是否以图片形式发送(关闭则只发送链接)(Send cover as image, otherwise link only) |
|
|
43
|
+
| `showImageFileNew` | boolean | true | 图片是否以图片形式发送(关闭则只发送链接)(Send images as image, otherwise link only) |
|
|
44
|
+
| `showAuthorAvatar` | boolean | true | 发送作者头像图片 (Send author avatar image) |
|
|
45
|
+
| `showAuthorAvatarFile` | boolean | true | 作者头像图片是否以图片形式发送(关闭则只发送链接)(Send author avatar as image, otherwise link only) |
|
|
46
|
+
| `showAuthorAvatarText` | boolean | true | 发送作者头像前显示文字提示 (Show text hint before author avatar) |
|
|
47
|
+
| `authorAvatarText` | string | 作者头像: | 作者头像前显示的文字 (Text displayed before author avatar) |
|
|
48
|
+
| `showMusicCover` | boolean | true | 发送音乐封面图片 (Send music cover image) |
|
|
49
|
+
| `showVideoFile` | boolean | true | 视频是否以视频形式发送(关闭则只发送链接)(Send video as file, otherwise link only) |
|
|
50
|
+
| `forceDownloadCover` | boolean | false | 强制下载封面 (Force download cover) |
|
|
51
|
+
| `forceDownloadImageNew` | boolean | false | 强制下载图片 (Force download images) |
|
|
52
|
+
| `forceDownloadAuthorAvatar` | boolean | false | 强制下载作者头像 (Force download author avatar) |
|
|
53
|
+
| `forceDownloadVideo` | boolean | false | 强制下载视频 (Force download video) |
|
|
54
|
+
|
|
55
|
+
### 音乐语音(需 silk 和 ffmpeg)(Music Voice - requires silk & ffmpeg)
|
|
56
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
57
|
+
|----------------|-------------|-------------------|---------------------|
|
|
58
|
+
| `showMusicVoice` | boolean | false | 音乐链接以语音发送 (Send music as voice) |
|
|
59
|
+
| `showMusicVoiceFile` | boolean | true | 音乐链接是否以语音形式发送(关闭则只发送链接)(Send as voice file, otherwise link only) |
|
|
60
|
+
| `forceDownloadMusicVoice` | boolean | false | 强制下载音乐语音 (Force download music voice) |
|
|
61
|
+
|
|
62
|
+
### 性能与限制 (Performance & Limits)
|
|
63
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
64
|
+
|----------------|-------------|-------------------|---------------------|
|
|
65
|
+
| `maxDescLength` | number | 200 | 简介长度上限 (Max description length) |
|
|
66
|
+
| `maxConcurrent` | number | 3 | 解析最大并发数 (Max concurrent parsing) |
|
|
67
|
+
| `downloadConcurrency` | number | 3 | 下载线程数 (Download concurrency) |
|
|
68
|
+
| `mediaDownloadTimeout` | number | 120000 | 统一下载超时 (ms) (Download timeout) |
|
|
69
|
+
| `maxMediaSize` | number | 0 | 最大下载文件大小 (MB),0 为不限制 (Max file size, 0 = unlimited) |
|
|
70
|
+
| `downloadEngine` | string | internal | 下载引擎(internal / aria2 / downloads)(Download engine) |
|
|
71
|
+
|
|
72
|
+
### 网络与请求 (Network & Request)
|
|
73
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
74
|
+
|----------------|-------------|-------------------|---------------------|
|
|
75
|
+
| `timeout` | number | 180000 | API 超时 (ms) (API timeout) |
|
|
76
|
+
| `videoSendTimeout` | number | 180000 | 发送超时 (ms) (Send timeout) |
|
|
70
77
|
| `userAgent` | string | 见预设 | User-Agent |
|
|
71
|
-
| `proxy` | object | ... | HTTP/HTTPS 代理 |
|
|
72
|
-
| `customHeaders` | array | [] | 自定义请求头 |
|
|
73
|
-
|
|
74
|
-
### 发送与重试
|
|
75
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
76
|
-
|
|
77
|
-
| `ignoreSendError` | boolean | true | 忽略发送失败 |
|
|
78
|
-
| `retryTimes` | number | 3 | 重试次数 |
|
|
79
|
-
| `retryInterval` | number | 1000 | 重试间隔 (ms) |
|
|
80
|
-
| `enableForward` | boolean | false | 合并转发(OneBot/Satori) |
|
|
81
|
-
|
|
82
|
-
### 缓存与临时文件
|
|
83
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
84
|
-
|
|
85
|
-
| `deduplicationInterval` | number | 180 | 去重间隔 (s) |
|
|
86
|
-
| `cacheTTL` | number | 600 | 缓存时间 (s) |
|
|
87
|
-
| `cacheDir` | string | ./temp_cache | 统一临时目录 |
|
|
88
|
-
|
|
89
|
-
### API 与平台
|
|
90
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
91
|
-
|
|
92
|
-
| `platformDedicatedFirst` | object | 全关 | 优先专属 API |
|
|
93
|
-
| `customApis` | array | [] | 覆盖内置平台 API |
|
|
94
|
-
| `customPlatforms` | array | [] | 自定义新平台 |
|
|
95
|
-
| `globalFieldMapping` | string | 预设 | 全局字段映射 JSON |
|
|
96
|
-
|
|
97
|
-
### 界面文本
|
|
98
|
-
| 配置项 | 类型 | 默认值 | 说明 |
|
|
99
|
-
|
|
100
|
-
| `waitingTipText` | string | 正在解析... | 等待提示 |
|
|
101
|
-
| `unsupportedPlatformText` | string | 不支持该平台 | 不支持提示 |
|
|
102
|
-
| `invalidLinkText` | string | 无效链接 | 无效链接提示 |
|
|
103
|
-
| `parseErrorPrefix` | string | ❌ 解析失败: | 错误前缀 |
|
|
104
|
-
| `parseErrorItemFormat` | string | ... | 错误格式 |
|
|
78
|
+
| `proxy` | object | ... | HTTP/HTTPS 代理 (Proxy) |
|
|
79
|
+
| `customHeaders` | array | [] | 自定义请求头 (Custom headers) |
|
|
80
|
+
|
|
81
|
+
### 发送与重试 (Send & Retry)
|
|
82
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
83
|
+
|----------------|-------------|-------------------|---------------------|
|
|
84
|
+
| `ignoreSendError` | boolean | true | 忽略发送失败 (Ignore send errors) |
|
|
85
|
+
| `retryTimes` | number | 3 | 重试次数 (Retry times) |
|
|
86
|
+
| `retryInterval` | number | 1000 | 重试间隔 (ms) (Retry interval) |
|
|
87
|
+
| `enableForward` | boolean | false | 合并转发(OneBot/Satori)(Enable forward message) |
|
|
88
|
+
|
|
89
|
+
### 缓存与临时文件 (Cache & Temp Files)
|
|
90
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
91
|
+
|----------------|-------------|-------------------|---------------------|
|
|
92
|
+
| `deduplicationInterval` | number | 180 | 去重间隔 (s) (Deduplication interval) |
|
|
93
|
+
| `cacheTTL` | number | 600 | 缓存时间 (s) (Cache TTL) |
|
|
94
|
+
| `cacheDir` | string | ./temp_cache | 统一临时目录 (Temp directory) |
|
|
95
|
+
|
|
96
|
+
### API 与平台 (API & Platforms)
|
|
97
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
98
|
+
|----------------|-------------|-------------------|---------------------|
|
|
99
|
+
| `platformDedicatedFirst` | object | 全关 (All off) | 优先专属 API (Prioritize dedicated APIs) |
|
|
100
|
+
| `customApis` | array | [] | 覆盖内置平台 API (Override built-in APIs) |
|
|
101
|
+
| `customPlatforms` | array | [] | 自定义新平台 (Custom platforms) |
|
|
102
|
+
| `globalFieldMapping` | string | 预设 (Preset) | 全局字段映射 JSON (Global field mapping JSON) |
|
|
103
|
+
|
|
104
|
+
### 界面文本 (UI Text)
|
|
105
|
+
| 配置项 (Config) | 类型 (Type) | 默认值 (Default) | 说明 (Description) |
|
|
106
|
+
|----------------|-------------|-------------------|---------------------|
|
|
107
|
+
| `waitingTipText` | string | 正在解析... | 等待提示 (Waiting tip) |
|
|
108
|
+
| `unsupportedPlatformText` | string | 不支持该平台 | 不支持提示 (Unsupported platform tip) |
|
|
109
|
+
| `invalidLinkText` | string | 无效链接 | 无效链接提示 (Invalid link tip) |
|
|
110
|
+
| `parseErrorPrefix` | string | ❌ 解析失败: | 错误前缀 (Error prefix) |
|
|
111
|
+
| `parseErrorItemFormat` | string | ... | 错误格式 (Error format) |
|
|
105
112
|
|
|
106
113
|
## 支持的变量 (Supported Variables)
|
|
107
|
-
在 `unifiedMessageFormat`
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
| `${
|
|
113
|
-
| `${
|
|
114
|
-
| `${
|
|
115
|
-
| `${
|
|
116
|
-
| `${
|
|
117
|
-
| `${
|
|
118
|
-
| `${
|
|
119
|
-
| `${
|
|
120
|
-
| `${
|
|
121
|
-
| `${
|
|
122
|
-
| `${
|
|
123
|
-
| `${
|
|
124
|
-
| `${音乐标题}` | 音乐标题 |
|
|
125
|
-
| `${音乐作者}` | 音乐作者 |
|
|
114
|
+
在 `unifiedMessageFormat` 中可使用以下变量,空行自动隐藏。
|
|
115
|
+
The following variables can be used in `unifiedMessageFormat`, empty lines are auto-hidden.
|
|
116
|
+
|
|
117
|
+
| 变量 (Variable) | 说明 (Description) |
|
|
118
|
+
|----------------|--------------------|
|
|
119
|
+
| `${标题}` | 视频/图集标题 (Title) |
|
|
120
|
+
| `${作者}` | 作者名称 (Author name) |
|
|
121
|
+
| `${简介}` | 内容简介 (Description) |
|
|
122
|
+
| `${视频时长}` | 视频时长(时:分:秒)(Duration, hh:mm:ss) |
|
|
123
|
+
| `${点赞数}` | 点赞数量 (Like count) |
|
|
124
|
+
| `${收藏数}` | 收藏数量 (Favorite count) |
|
|
125
|
+
| `${转发数}` | 转发/分享数量 (Share count) |
|
|
126
|
+
| `${播放数}` | 播放量 (Play count) |
|
|
127
|
+
| `${评论数}` | 评论数量 (Comment count) |
|
|
128
|
+
| `${发布时间}` | 发布时间(格式化)(Publish time, formatted) |
|
|
129
|
+
| `${图片数量}` | 图集/实况图片数量 (Image count) |
|
|
130
|
+
| `${作者ID}` | 作者唯一标识ID (Author ID) |
|
|
131
|
+
| `${音乐标题}` | 音乐标题 (Music title) |
|
|
132
|
+
| `${音乐作者}` | 音乐作者 (Music author) |
|
|
126
133
|
|
|
127
134
|
## 依赖说明 (Dependencies)
|
|
128
|
-
### 音乐语音(可选)
|
|
129
|
-
若启用 `showMusicVoice`,请安装:
|
|
130
|
-
|
|
131
|
-
- `koishi-plugin-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
###
|
|
135
|
-
若启用 `downloadEngine: '
|
|
135
|
+
### 音乐语音(可选)(Music Voice - Optional)
|
|
136
|
+
若启用 `showMusicVoice`,请安装:
|
|
137
|
+
If you enable `showMusicVoice`, please install:
|
|
138
|
+
- `koishi-plugin-silk`:silk 编解码 (Silk codec)
|
|
139
|
+
- `koishi-plugin-ffmpeg`:音频重采样 (Audio resampling)
|
|
140
|
+
|
|
141
|
+
### aria2 下载引擎(可选)(aria2 Download Engine - Optional)
|
|
142
|
+
若启用 `downloadEngine: 'aria2'`,请安装可选依赖 `koishi-plugin-aria2-plus` 并配置该插件连接 aria2 服务。插件启动时会自动检测该服务,未安装或不可用时将降级为内置下载,不影响正常使用。
|
|
143
|
+
If you use `downloadEngine: 'aria2'`, please install the optional dependency `koishi-plugin-aria2-plus` and configure it to connect to an aria2 service. The plugin will auto-detect it; if not available, it will fall back to the built-in downloader without affecting normal usage.
|
|
144
|
+
|
|
145
|
+
### downloads 服务(可选)(Downloads Service - Optional)
|
|
146
|
+
若启用 `downloadEngine: 'downloads'`,请安装可选依赖 `koishi-plugin-downloads`,失败时回退到内置下载。
|
|
147
|
+
If you use `downloadEngine: 'downloads'`, please install the optional dependency `koishi-plugin-downloads`. It will fall back to the built-in downloader on failure.
|
|
136
148
|
|
|
137
149
|
## 支持的平台 (Supported Platforms)
|
|
138
150
|
|
|
139
|
-
> 以下为插件内置链接匹配规则,可根据用户发送的链接自动识别。所有匹配规则同时支持 HTTP 和 HTTPS 协议,并兼容多级路径(如短链后带 `/` 子路径)。
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
|
145
|
-
|
|
|
146
|
-
|
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
|
|
|
167
|
-
|
|
|
168
|
-
|
|
|
169
|
-
|
|
|
151
|
+
> 以下为插件内置链接匹配规则,可根据用户发送的链接自动识别。所有匹配规则同时支持 HTTP 和 HTTPS 协议,并兼容多级路径(如短链后带 `/` 子路径)。
|
|
152
|
+
> The following are the built-in link matching rules, which can automatically identify links sent by users. All rules support both HTTP and HTTPS, and are compatible with multi-level paths (e.g., short links followed by `/` subpaths).
|
|
153
|
+
|
|
154
|
+
| 平台名称 (Platform) | 关键词识别(匹配的域名/路径模式)(Keyword/Domain Patterns) | 解析能力 (Supported Content) |
|
|
155
|
+
|---------------------|--------------------------------------------------------------|------------------------------|
|
|
156
|
+
| 哔哩哔哩 (B站) Bilibili | `bilibili.com/video/`, `b23.tv`, `bili*.cn`, `b23.wtf`, `bili2233.cn` | 视频 (Video) |
|
|
157
|
+
| 抖音 Douyin | `douyin.com/video/`, `v.douyin.com` | 短视频、图集、实况 (Short video, Image, Live photo) |
|
|
158
|
+
| 快手 Kuaishou | `kuaishou.com/short-video/`, `v.kuaishou.com`, `kuaishou.com/f/` | 短视频、图集 (Short video, Image) |
|
|
159
|
+
| 小红书 Xiaohongshu | `xiaohongshu.com/discovery/item/`, `xhslink.com`, `xiaohongshu.com/explore/`, `xiaohongshu.com/board/` | 图文、视频 (Image, Video) |
|
|
160
|
+
| 微博 Weibo | `weibo.com/数字/`, `video.weibo.com/show`, `t.cn`, `m.weibo.cn` | 视频、图集 (Video, Image) |
|
|
161
|
+
| 西瓜视频 Xigua | `ixigua.com` | 短视频 (Short video) |
|
|
162
|
+
| YouTube | `youtube.com/watch`, `youtu.be`, `youtube.com/shorts/` | 视频 (Video) |
|
|
163
|
+
| TikTok | `tiktok.com/@/video/`, `vm.tiktok.com`, `vt.tiktok.com` | 短视频 (Short video) |
|
|
164
|
+
| AcFun(A站) | `acfun.cn/v/ac` | 视频 (Video) |
|
|
165
|
+
| 知乎 Zhihu | `zhihu.com/video/`, `zhihu.com/question/xxx/answer/xxx`, `zhuanlan.zhihu.com/p/`, `zhihu.com/zvideo/` | 视频、回答中的视频 (Video, answer video) |
|
|
166
|
+
| 微视 Weishi | `weishi.qq.com/weishi/feed/` | 短视频 (Short video) |
|
|
167
|
+
| 虎牙 Huya | `huya.com/video/` | 直播回放、视频 (Live replay, Video) |
|
|
168
|
+
| 好看视频 Haokan | `haokan.baidu.com/v?vid=` | 短视频 (Short video) |
|
|
169
|
+
| 美拍 Meipai | `meipai.com/media/` | 短视频 (Short video) |
|
|
170
|
+
| Twitter / X | `twitter.com/用户名/status/`, `x.com/用户名/status/` | 视频、图文 (Video, Image) |
|
|
171
|
+
| Instagram | `instagram.com/p/`, `instagram.com/reel/`, `instagram.com/share/` | 图文、Reels (Image, Reels) |
|
|
172
|
+
| 豆包(视频)Doubao Video | `doubao.com/video/`, `doubao.com/video-sharing` | 视频 (Video) |
|
|
173
|
+
| 豆包(图集)Doubao Image | `doubao.com/thread/` | 图文 (Image) |
|
|
174
|
+
| **即梦 (Jimeng)** | `jimeng.jianying.com`, `jimeng.cn`, `dreamina.jianying.com`, `dreamina.capcut.com` | AI视频、AI图片 (AI video, AI image) |
|
|
175
|
+
| 绿洲 Oasis | `oasis.weibo.com/v/` | 视频、图文 (Video, Image) |
|
|
176
|
+
| 视频号 WeChat Channels | `channels.weixin.qq.com`, `weixin.qq.com/sph/` | 短视频 (Short video) |
|
|
177
|
+
| 梨视频 Lishi | `pearvideo.com/video_`, `video.li` | 短视频 (Short video) |
|
|
178
|
+
| 全民直播 Quanmin | `quanmin.tv`, `quanmintv.cn` | 直播 (Live) |
|
|
179
|
+
| 皮皮搞笑 Pipigx | `h5.pipigx.com/pp/post/`, `ippzone.com` | 短视频 (Short video) |
|
|
180
|
+
| 皮皮虾 Pipixia | `pipix.com`, `pipixia.com` | 短视频 (Short video) |
|
|
181
|
+
| 最右 Zuiyou | `share.xiaochuankeji.cn/hybrid/share/post`, `izuiyou.com` | 短视频 (Short video) |
|
|
182
|
+
| 自定义平台 Custom | 通过 `customPlatforms` 配置添加 (Add via `customPlatforms`) | 取决于提供的 API (Depends on API) |
|
|
170
183
|
|
|
171
184
|
## 项目贡献者 (Contributors)
|
|
172
185
|
|
|
173
186
|
| 贡献者 (Contributor) | 贡献内容 (Contribution) |
|
|
174
187
|
|----------------------|-------------------------|
|
|
175
188
|
| Minecraft-1314 | 插件完整开发 (Complete plugin development) |
|
|
176
|
-
| ShiraiKuroko003 | 修复消息格式设置问题并且PR-1.2.5版本已修复 |
|
|
177
|
-
| cyavb | 提交功能建议-给自定义API添加KEY认证-已修复 |
|
|
178
|
-
| Keep785 | 提交Bug-无法正常关闭发送封面-已修复<br>提交Bug-解析问题-已修复 |
|
|
179
|
-
| dzt2008 + Apricityx | 提交Bug-会对非支持视频平台URL进行误解析-已修复 |
|
|
180
|
-
|
|
|
181
|
-
|
|
|
189
|
+
| ShiraiKuroko003 | 修复消息格式设置问题并且PR-1.2.5版本已修复 (Fixed message format issue, PR-1.2.5) |
|
|
190
|
+
| cyavb | 提交功能建议-给自定义API添加KEY认证-已修复 (Suggested custom API key auth - fixed) |
|
|
191
|
+
| Keep785 | 提交Bug-无法正常关闭发送封面-已修复<br>提交Bug-解析问题-已修复 (Reported bugs - cannot disable cover sending and parsing issues - fixed) |
|
|
192
|
+
| dzt2008 + Apricityx | 提交Bug-会对非支持视频平台URL进行误解析-已修复 (Reported incorrect parsing of unsupported URLs - fixed) |
|
|
193
|
+
| linyves | 提交Bug-小红书图集重复发送封面-已修复<br>提交Bug-话题显示异常 #**[话题]#-已修复<br>提交建议-Live Photo 全部按普通图片处理-已采纳<br>提交Bug-解析后会把作者头像一起发送-已修复 |
|
|
194
|
+
| JH-Ahua | BugPk-Api 支持 (BugPk-Api support) |
|
|
195
|
+
| shangxue | 灵感来源 (Inspiration) |
|
|
182
196
|
|
|
183
|
-
(欢迎通过 Issues 或 PR 加入贡献者列表)
|
|
197
|
+
(欢迎通过 Issues 或 PR 加入贡献者列表)
|
|
198
|
+
(Welcome to join the contributor list via Issues or PR)
|
|
184
199
|
|
|
185
200
|
## 许可协议 (License)
|
|
186
201
|
|
|
187
|
-
本项目采用 MIT 许可证,详情参见 [LICENSE](LICENSE) 文件。
|
|
188
|
-
|
|
202
|
+
本项目采用 MIT 许可证,详情参见 [LICENSE](LICENSE) 文件。
|
|
189
203
|
This project is licensed under the MIT License, see the [LICENSE](LICENSE) file for details.
|
|
190
204
|
|
|
191
205
|
## 支持我们 (Support Us)
|
|
192
206
|
|
|
193
|
-
如果这个项目对您有帮助,欢迎点亮右上角的 Star ⭐ 支持我们!
|
|
194
|
-
|
|
207
|
+
如果这个项目对您有帮助,欢迎点亮右上角的 Star ⭐ 支持我们!
|
|
195
208
|
If this project is helpful to you, please feel free to star it in the upper right corner ⭐ to support us!
|