koishi-plugin-video-parser-all 0.8.2 → 0.8.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/lib/index.d.ts +16 -8
- package/lib/index.js +112 -63
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -3,9 +3,6 @@ export declare const name = "video-parser-all";
|
|
|
3
3
|
export declare const Config: Schema<{
|
|
4
4
|
enable?: boolean | null | undefined;
|
|
5
5
|
botName?: string | null | undefined;
|
|
6
|
-
showWaitingTip?: boolean | null | undefined;
|
|
7
|
-
waitingTipText?: string | null | undefined;
|
|
8
|
-
sameLinkInterval?: number | null | undefined;
|
|
9
6
|
debug?: boolean | null | undefined;
|
|
10
7
|
debugFile?: boolean | null | undefined;
|
|
11
8
|
} & import("cosmokit").Dict & {
|
|
@@ -14,7 +11,6 @@ export declare const Config: Schema<{
|
|
|
14
11
|
showImageText?: boolean | null | undefined;
|
|
15
12
|
showVideoFile?: boolean | null | undefined;
|
|
16
13
|
sendLivePhotoVideos?: boolean | null | undefined;
|
|
17
|
-
} & {
|
|
18
14
|
maxDescLength?: number | null | undefined;
|
|
19
15
|
} & {
|
|
20
16
|
timeout?: number | null | undefined;
|
|
@@ -33,12 +29,17 @@ export declare const Config: Schema<{
|
|
|
33
29
|
messageBufferDelay?: number | null | undefined;
|
|
34
30
|
} & {
|
|
35
31
|
autoClearCacheInterval?: number | null | undefined;
|
|
32
|
+
} & {
|
|
33
|
+
waitingTipText?: string | null | undefined;
|
|
34
|
+
duplicateLinkText?: string | null | undefined;
|
|
35
|
+
unsupportedPlatformText?: string | null | undefined;
|
|
36
|
+
invalidLinkText?: string | null | undefined;
|
|
37
|
+
cacheClearedText?: string | null | undefined;
|
|
38
|
+
parseErrorPrefix?: string | null | undefined;
|
|
39
|
+
parseErrorItemFormat?: string | null | undefined;
|
|
36
40
|
}, {
|
|
37
41
|
enable: boolean;
|
|
38
42
|
botName: string;
|
|
39
|
-
showWaitingTip: boolean;
|
|
40
|
-
waitingTipText: string;
|
|
41
|
-
sameLinkInterval: number;
|
|
42
43
|
debug: boolean;
|
|
43
44
|
debugFile: boolean;
|
|
44
45
|
} & import("cosmokit").Dict & {
|
|
@@ -47,7 +48,6 @@ export declare const Config: Schema<{
|
|
|
47
48
|
showImageText: boolean;
|
|
48
49
|
showVideoFile: boolean;
|
|
49
50
|
sendLivePhotoVideos: boolean;
|
|
50
|
-
} & {
|
|
51
51
|
maxDescLength: number;
|
|
52
52
|
} & {
|
|
53
53
|
timeout: number;
|
|
@@ -66,5 +66,13 @@ export declare const Config: Schema<{
|
|
|
66
66
|
messageBufferDelay: number;
|
|
67
67
|
} & {
|
|
68
68
|
autoClearCacheInterval: number;
|
|
69
|
+
} & {
|
|
70
|
+
waitingTipText: string;
|
|
71
|
+
duplicateLinkText: string;
|
|
72
|
+
unsupportedPlatformText: string;
|
|
73
|
+
invalidLinkText: string;
|
|
74
|
+
cacheClearedText: string;
|
|
75
|
+
parseErrorPrefix: string;
|
|
76
|
+
parseErrorItemFormat: string;
|
|
69
77
|
}>;
|
|
70
78
|
export declare function apply(ctx: Context, config: any): void;
|
package/lib/index.js
CHANGED
|
@@ -10,52 +10,57 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
10
10
|
const crypto_1 = __importDefault(require("crypto"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const url_1 = require("url");
|
|
13
14
|
const promises_1 = require("stream/promises");
|
|
14
15
|
const worker_threads_1 = require("worker_threads");
|
|
15
16
|
exports.name = 'video-parser-all';
|
|
16
17
|
exports.Config = koishi_1.Schema.intersect([
|
|
17
18
|
koishi_1.Schema.object({
|
|
18
19
|
enable: koishi_1.Schema.boolean().default(true).description('是否启用视频解析插件'),
|
|
19
|
-
botName: koishi_1.Schema.string().default('视频解析机器人').description('
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
sameLinkInterval: koishi_1.Schema.number().min(0).default(180).description('相同链接重复解析间隔(秒)'),
|
|
23
|
-
debug: koishi_1.Schema.boolean().default(false).description('开启调试模式'),
|
|
24
|
-
debugFile: koishi_1.Schema.boolean().default(false).description('调试日志写入文件'),
|
|
20
|
+
botName: koishi_1.Schema.string().default('视频解析机器人').description('合并转发消息中显示的机器人名称'),
|
|
21
|
+
debug: koishi_1.Schema.boolean().default(false).description('开启调试模式(详细日志输出至控制台)'),
|
|
22
|
+
debugFile: koishi_1.Schema.boolean().default(false).description('调试日志同时写入本地 debug.log 文件'),
|
|
25
23
|
}).description('基础设置'),
|
|
26
24
|
koishi_1.Schema.object({
|
|
27
25
|
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default(`标题:\${标题}\n作者:\${作者}\n点赞:\${点赞数}\n视频链接:\${视频链接}`).description('统一消息格式,可用变量:${标题} ${作者} ${简介} ${视频时长} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${发布时间} ${图片数量} ${作者ID} ${视频链接} ${封面} ${音乐作者} ${音乐标题}'),
|
|
28
|
-
}).description('
|
|
26
|
+
}).description('消息格式设置'),
|
|
29
27
|
koishi_1.Schema.object({
|
|
30
|
-
showImageText: koishi_1.Schema.boolean().default(true).description('
|
|
31
|
-
showVideoFile: koishi_1.Schema.boolean().default(true).description('
|
|
32
|
-
sendLivePhotoVideos: koishi_1.Schema.boolean().default(true).description('
|
|
28
|
+
showImageText: koishi_1.Schema.boolean().default(true).description('是否发送解析后的文字内容'),
|
|
29
|
+
showVideoFile: koishi_1.Schema.boolean().default(true).description('是否发送视频文件(关闭则只发送视频链接)'),
|
|
30
|
+
sendLivePhotoVideos: koishi_1.Schema.boolean().default(true).description('发送实况图集时是否附带短视频'),
|
|
31
|
+
maxDescLength: koishi_1.Schema.number().default(200).description('简介内容最大长度(字符),超出自动截断'),
|
|
33
32
|
}).description('内容显示设置'),
|
|
34
33
|
koishi_1.Schema.object({
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
timeout: koishi_1.Schema.number().min(0).default(180000).description('API 请求超时(毫秒)'),
|
|
35
|
+
videoSendTimeout: koishi_1.Schema.number().min(0).default(60000).description('视频消息发送超时(毫秒,0 为不限制)'),
|
|
36
|
+
userAgent: koishi_1.Schema.string().default('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36').description('API 请求 UA'),
|
|
37
|
+
}).description('网络与 API 设置'),
|
|
37
38
|
koishi_1.Schema.object({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}).description('网络与API设置'),
|
|
42
|
-
koishi_1.Schema.object({
|
|
43
|
-
ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略发送失败错误'),
|
|
44
|
-
retryTimes: koishi_1.Schema.number().min(0).default(3).description('API请求重试次数'),
|
|
45
|
-
retryInterval: koishi_1.Schema.number().min(0).default(1000).description('重试间隔时间(毫秒)'),
|
|
39
|
+
ignoreSendError: koishi_1.Schema.boolean().default(true).description('忽略消息发送失败,避免插件崩溃'),
|
|
40
|
+
retryTimes: koishi_1.Schema.number().min(0).default(3).description('API 请求重试次数'),
|
|
41
|
+
retryInterval: koishi_1.Schema.number().min(0).default(1000).description('重试间隔(毫秒)'),
|
|
46
42
|
}).description('错误与重试设置'),
|
|
47
43
|
koishi_1.Schema.object({
|
|
48
|
-
enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅OneBot平台)'),
|
|
49
|
-
downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('
|
|
50
|
-
maxVideoSize: koishi_1.Schema.number().min(0).default(0).description('
|
|
51
|
-
downloadThreads: koishi_1.Schema.number().min(0).max(10).default(0).description('多线程下载线程数(0
|
|
44
|
+
enableForward: koishi_1.Schema.boolean().default(false).description('启用合并转发(仅 OneBot 平台)'),
|
|
45
|
+
downloadVideoBeforeSend: koishi_1.Schema.boolean().default(false).description('发送前先下载视频到本地'),
|
|
46
|
+
maxVideoSize: koishi_1.Schema.number().min(0).default(0).description('视频下载大小限制(MB,0 为不限制)'),
|
|
47
|
+
downloadThreads: koishi_1.Schema.number().min(0).max(10).default(0).description('多线程下载线程数(0 为单线程)'),
|
|
52
48
|
}).description('发送方式设置'),
|
|
53
49
|
koishi_1.Schema.object({
|
|
54
50
|
messageBufferDelay: koishi_1.Schema.number().min(0).default(0).description('消息缓冲延迟(毫秒)'),
|
|
55
|
-
}).description('
|
|
51
|
+
}).description('消息缓冲'),
|
|
52
|
+
koishi_1.Schema.object({
|
|
53
|
+
autoClearCacheInterval: koishi_1.Schema.number().min(0).default(0).description('自动清理缓存间隔(分钟,0 为关闭)'),
|
|
54
|
+
}).description('缓存清理'),
|
|
56
55
|
koishi_1.Schema.object({
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
waitingTipText: koishi_1.Schema.string().default('正在解析视频,请稍候...').description('解析等待提示'),
|
|
57
|
+
duplicateLinkText: koishi_1.Schema.string().default('请勿重复解析相同链接').description('重复链接提示'),
|
|
58
|
+
unsupportedPlatformText: koishi_1.Schema.string().default('不支持该平台链接').description('不支持的平台提示'),
|
|
59
|
+
invalidLinkText: koishi_1.Schema.string().default('无效的视频链接').description('无效链接提示(parse 指令)'),
|
|
60
|
+
cacheClearedText: koishi_1.Schema.string().default('✅ 缓存已清空').description('缓存清理提示'),
|
|
61
|
+
parseErrorPrefix: koishi_1.Schema.string().default('❌ 解析失败:').description('解析失败消息前缀'),
|
|
62
|
+
parseErrorItemFormat: koishi_1.Schema.string().default('【${url}】: ${msg}').description('每条解析失败格式,可用 ${url} ${msg}'),
|
|
63
|
+
}).description('界面文字设置'),
|
|
59
64
|
]);
|
|
60
65
|
const processed = new Map();
|
|
61
66
|
const linkBuffer = new Map();
|
|
@@ -282,6 +287,7 @@ function pickBestQuality(videoBackup) {
|
|
|
282
287
|
.sort((a, b) => (b.bit_rate || 0) - (a.bit_rate || 0));
|
|
283
288
|
}
|
|
284
289
|
function parseApiResponse(raw, maxDescLen) {
|
|
290
|
+
debugLog('DEBUG', '原始API返回数据:', raw);
|
|
285
291
|
const data = raw?.data || {};
|
|
286
292
|
const extra = data.extra || {};
|
|
287
293
|
let type = data.type || '';
|
|
@@ -356,12 +362,14 @@ function parseApiResponse(raw, maxDescLen) {
|
|
|
356
362
|
else if (extra.create_time) {
|
|
357
363
|
publishTime = extra.create_time * 1000;
|
|
358
364
|
}
|
|
359
|
-
|
|
365
|
+
const result = {
|
|
360
366
|
type, title, desc, author, uid, avatar, cover,
|
|
361
367
|
video, videos, images, live_photo, music,
|
|
362
368
|
like, comment, collect, share, play,
|
|
363
369
|
duration, publishTime
|
|
364
370
|
};
|
|
371
|
+
debugLog('DEBUG', '解析后的数据:', result);
|
|
372
|
+
return result;
|
|
365
373
|
}
|
|
366
374
|
function generateFormattedText(p, format) {
|
|
367
375
|
const vars = {
|
|
@@ -386,7 +394,9 @@ function generateFormattedText(p, format) {
|
|
|
386
394
|
for (const [key, value] of Object.entries(vars)) {
|
|
387
395
|
result = result.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), value);
|
|
388
396
|
}
|
|
389
|
-
|
|
397
|
+
const final = result.replace(/^\s*\n/gm, '').trim();
|
|
398
|
+
debugLog('DEBUG', '生成格式化文本:', final);
|
|
399
|
+
return final;
|
|
390
400
|
}
|
|
391
401
|
function clearAllCache() {
|
|
392
402
|
processed.clear();
|
|
@@ -417,16 +427,27 @@ function buildForwardNode(session, content, botName) {
|
|
|
417
427
|
function apply(ctx, config) {
|
|
418
428
|
initDebug(config.debug, config.debugFile);
|
|
419
429
|
debugLog('INFO', '插件初始化开始');
|
|
430
|
+
debugLog('INFO', '当前配置:', config);
|
|
431
|
+
const texts = {
|
|
432
|
+
waitingTipText: config.waitingTipText || '正在解析视频,请稍候...',
|
|
433
|
+
duplicateLinkText: config.duplicateLinkText || '请勿重复解析相同链接',
|
|
434
|
+
unsupportedPlatformText: config.unsupportedPlatformText || '不支持该平台链接',
|
|
435
|
+
invalidLinkText: config.invalidLinkText || '无效的视频链接',
|
|
436
|
+
cacheClearedText: config.cacheClearedText || '✅ 缓存已清空',
|
|
437
|
+
parseErrorPrefix: config.parseErrorPrefix || '❌ 解析失败:',
|
|
438
|
+
parseErrorItemFormat: config.parseErrorItemFormat || '【${url}】: ${msg}',
|
|
439
|
+
};
|
|
420
440
|
clearAllCache();
|
|
421
441
|
const http = axios_1.default.create({
|
|
422
442
|
timeout: config.timeout,
|
|
423
443
|
headers: {
|
|
424
|
-
'User-Agent': config.userAgent,
|
|
444
|
+
'User-Agent': config.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
425
445
|
'Referer': 'https://www.baidu.com/',
|
|
426
446
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
427
447
|
}
|
|
428
448
|
});
|
|
429
449
|
async function fetchApi(url) {
|
|
450
|
+
debugLog('INFO', `调用API解析: ${url}`);
|
|
430
451
|
for (let i = 0; i <= config.retryTimes; i++) {
|
|
431
452
|
try {
|
|
432
453
|
const res = await http.get('https://api.bugpk.com/api/short_videos', {
|
|
@@ -434,6 +455,7 @@ function apply(ctx, config) {
|
|
|
434
455
|
timeout: config.timeout
|
|
435
456
|
});
|
|
436
457
|
debugLog('INFO', `API响应: code=${res.data?.code}, msg=${res.data?.msg}`);
|
|
458
|
+
debugLog('DEBUG', 'API完整响应:', res.data);
|
|
437
459
|
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
|
|
438
460
|
return parseApiResponse(res.data, config.maxDescLength);
|
|
439
461
|
}
|
|
@@ -448,50 +470,53 @@ function apply(ctx, config) {
|
|
|
448
470
|
throw new Error('API请求全部失败');
|
|
449
471
|
}
|
|
450
472
|
async function parseUrl(url) {
|
|
451
|
-
debugLog('INFO',
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
debugLog('DEBUG', `实际URL: ${realUrl}`);
|
|
473
|
+
debugLog('INFO', `开始解析链接: ${url}`);
|
|
474
|
+
const realUrl = await resolveShortUrl(url);
|
|
475
|
+
debugLog('DEBUG', `重定向后的URL: ${realUrl}`);
|
|
455
476
|
const platform = getPlatformType(realUrl);
|
|
456
477
|
if (!platform) {
|
|
457
478
|
debugLog('WARN', `不支持的平台: ${realUrl}`);
|
|
458
|
-
return { success: false, msg:
|
|
479
|
+
return { success: false, msg: texts.unsupportedPlatformText };
|
|
459
480
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
481
|
+
const candidates = [url, realUrl];
|
|
482
|
+
let lastError = null;
|
|
483
|
+
for (const candidate of candidates) {
|
|
484
|
+
try {
|
|
485
|
+
const info = await fetchApi(candidate);
|
|
486
|
+
debugLog('INFO', `解析成功: ${info.title}`);
|
|
487
|
+
return { success: true, data: info };
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
lastError = getErrorMessage(error);
|
|
491
|
+
debugLog('ERROR', `候选链接解析失败: ${candidate} => ${lastError}`);
|
|
492
|
+
}
|
|
468
493
|
}
|
|
494
|
+
return { success: false, msg: lastError || '解析失败' };
|
|
469
495
|
}
|
|
470
496
|
async function processSingleUrl(session, url) {
|
|
497
|
+
debugLog('INFO', `处理单个URL: ${url}, 用户: ${session.userId}`);
|
|
471
498
|
const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
|
|
472
499
|
const now = Date.now();
|
|
473
500
|
const last = processed.get(hash);
|
|
474
501
|
if (last && (now - last) < config.sameLinkInterval * 1000) {
|
|
475
502
|
debugLog('WARN', `重复解析: ${url}`);
|
|
476
|
-
return { success: false, msg:
|
|
503
|
+
return { success: false, msg: texts.duplicateLinkText };
|
|
477
504
|
}
|
|
478
505
|
processed.set(hash, now);
|
|
479
506
|
const result = await parseUrl(url);
|
|
480
|
-
if (!result.success)
|
|
481
|
-
return
|
|
482
|
-
}
|
|
507
|
+
if (!result.success)
|
|
508
|
+
return result;
|
|
483
509
|
const text = generateFormattedText(result.data, config.unifiedMessageFormat);
|
|
484
|
-
return {
|
|
485
|
-
success: true,
|
|
486
|
-
data: { text, parsed: result.data }
|
|
487
|
-
};
|
|
510
|
+
return { success: true, data: { text, parsed: result.data } };
|
|
488
511
|
}
|
|
489
512
|
async function sendWithTimeout(session, content) {
|
|
513
|
+
debugLog('DEBUG', `发送消息: ${JSON.stringify(content)}`);
|
|
490
514
|
if (config.videoSendTimeout <= 0) {
|
|
491
515
|
try {
|
|
492
516
|
return await session.send(content);
|
|
493
517
|
}
|
|
494
518
|
catch (err) {
|
|
519
|
+
debugLog('ERROR', `发送消息失败: ${getErrorMessage(err)}`);
|
|
495
520
|
if (!config.ignoreSendError)
|
|
496
521
|
throw err;
|
|
497
522
|
return null;
|
|
@@ -504,6 +529,7 @@ function apply(ctx, config) {
|
|
|
504
529
|
]);
|
|
505
530
|
}
|
|
506
531
|
catch (err) {
|
|
532
|
+
debugLog('ERROR', `发送消息超时或失败: ${getErrorMessage(err)}`);
|
|
507
533
|
if (!config.ignoreSendError)
|
|
508
534
|
throw err;
|
|
509
535
|
return null;
|
|
@@ -525,11 +551,14 @@ function apply(ctx, config) {
|
|
|
525
551
|
items.push(res.data);
|
|
526
552
|
}
|
|
527
553
|
else {
|
|
528
|
-
|
|
554
|
+
const item = texts.parseErrorItemFormat
|
|
555
|
+
.replace(/\$\{url\}/g, url.length > 50 ? url.slice(0, 50) + '...' : url)
|
|
556
|
+
.replace(/\$\{msg\}/g, res.msg);
|
|
557
|
+
errors.push(item);
|
|
529
558
|
}
|
|
530
559
|
}
|
|
531
560
|
if (errors.length) {
|
|
532
|
-
await sendWithTimeout(session,
|
|
561
|
+
await sendWithTimeout(session, `${texts.parseErrorPrefix}\n${errors.join('\n')}`).catch(() => { });
|
|
533
562
|
await delay(500);
|
|
534
563
|
}
|
|
535
564
|
if (!items.length)
|
|
@@ -540,7 +569,9 @@ function apply(ctx, config) {
|
|
|
540
569
|
for (const item of items) {
|
|
541
570
|
const p = item.parsed;
|
|
542
571
|
const text = item.text;
|
|
543
|
-
|
|
572
|
+
debugLog('INFO', `开始发送内容,类型: ${p.type}, 标题: ${p.title}`);
|
|
573
|
+
if (text && config.showImageText) {
|
|
574
|
+
debugLog('DEBUG', '发送文本消息');
|
|
544
575
|
if (enableForward)
|
|
545
576
|
forwardMessages.push(buildForwardNode(session, text, botName));
|
|
546
577
|
else {
|
|
@@ -549,6 +580,7 @@ function apply(ctx, config) {
|
|
|
549
580
|
}
|
|
550
581
|
}
|
|
551
582
|
if (p.cover && p.type !== 'live_photo') {
|
|
583
|
+
debugLog('DEBUG', '发送封面图片:', p.cover);
|
|
552
584
|
if (enableForward)
|
|
553
585
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.cover), botName));
|
|
554
586
|
else {
|
|
@@ -561,9 +593,13 @@ function apply(ctx, config) {
|
|
|
561
593
|
if (config.downloadVideoBeforeSend) {
|
|
562
594
|
const fname = crypto_1.default.createHash('md5').update(p.video).digest('hex');
|
|
563
595
|
const dl = await downloadVideo(p.video, fname, config.userAgent, config.maxVideoSize, config.downloadThreads);
|
|
564
|
-
if (dl.success)
|
|
565
|
-
|
|
596
|
+
if (dl.success) {
|
|
597
|
+
const fileUrl = (0, url_1.pathToFileURL)(dl.filePath).href;
|
|
598
|
+
debugLog('INFO', `视频下载成功,发送文件: ${fileUrl}`);
|
|
599
|
+
return koishi_1.h.file(fileUrl);
|
|
600
|
+
}
|
|
566
601
|
}
|
|
602
|
+
debugLog('INFO', `发送视频链接: ${p.video}`);
|
|
567
603
|
return koishi_1.h.video(p.video);
|
|
568
604
|
};
|
|
569
605
|
if (enableForward) {
|
|
@@ -575,7 +611,8 @@ function apply(ctx, config) {
|
|
|
575
611
|
const vMsg = await sendVideo();
|
|
576
612
|
await sendWithTimeout(session, vMsg);
|
|
577
613
|
}
|
|
578
|
-
catch {
|
|
614
|
+
catch (e) {
|
|
615
|
+
debugLog('ERROR', `发送视频失败: ${getErrorMessage(e)},尝试直接发送链接`);
|
|
579
616
|
try {
|
|
580
617
|
await sendWithTimeout(session, koishi_1.h.video(p.video));
|
|
581
618
|
}
|
|
@@ -587,6 +624,7 @@ function apply(ctx, config) {
|
|
|
587
624
|
if (p.type === 'image' || p.type === 'live_photo') {
|
|
588
625
|
const mediaList = [];
|
|
589
626
|
if (p.type === 'live_photo' && p.live_photo?.length) {
|
|
627
|
+
debugLog('INFO', `发送实况图集,共 ${p.live_photo.length} 张`);
|
|
590
628
|
for (const lp of p.live_photo) {
|
|
591
629
|
if (lp.image)
|
|
592
630
|
mediaList.push({ type: 'image', url: lp.image });
|
|
@@ -595,6 +633,7 @@ function apply(ctx, config) {
|
|
|
595
633
|
}
|
|
596
634
|
}
|
|
597
635
|
else if (p.images?.length) {
|
|
636
|
+
debugLog('INFO', `发送图集,共 ${p.images.length} 张`);
|
|
598
637
|
p.images.forEach(url => mediaList.push({ type: 'image', url }));
|
|
599
638
|
}
|
|
600
639
|
if (enableForward) {
|
|
@@ -605,20 +644,25 @@ function apply(ctx, config) {
|
|
|
605
644
|
}
|
|
606
645
|
else {
|
|
607
646
|
for (const m of mediaList) {
|
|
647
|
+
debugLog('DEBUG', `发送${m.type}: ${m.url}`);
|
|
608
648
|
try {
|
|
609
649
|
await sendWithTimeout(session, m.type === 'image' ? koishi_1.h.image(m.url) : koishi_1.h.video(m.url));
|
|
610
650
|
await delay(200);
|
|
611
651
|
}
|
|
612
|
-
catch {
|
|
652
|
+
catch (e) {
|
|
653
|
+
debugLog('ERROR', `发送${m.type}失败: ${getErrorMessage(e)}`);
|
|
654
|
+
}
|
|
613
655
|
}
|
|
614
656
|
}
|
|
615
657
|
}
|
|
616
658
|
}
|
|
617
659
|
if (enableForward && forwardMessages.length) {
|
|
660
|
+
debugLog('INFO', `合并转发消息,共 ${forwardMessages.length} 条`);
|
|
618
661
|
try {
|
|
619
662
|
await sendWithTimeout(session, (0, koishi_1.h)('message', { forward: true }, forwardMessages.slice(0, 100)));
|
|
620
663
|
}
|
|
621
|
-
catch {
|
|
664
|
+
catch (e) {
|
|
665
|
+
debugLog('ERROR', `合并转发失败,降级逐条发送: ${getErrorMessage(e)}`);
|
|
622
666
|
for (const node of forwardMessages) {
|
|
623
667
|
try {
|
|
624
668
|
await sendWithTimeout(session, node.data.content);
|
|
@@ -633,28 +677,33 @@ function apply(ctx, config) {
|
|
|
633
677
|
if (!config.enable)
|
|
634
678
|
return;
|
|
635
679
|
const content = session.content?.trim() || '';
|
|
680
|
+
debugLog('INFO', `收到消息: "${content}"`);
|
|
636
681
|
const urls = extractUrl(content);
|
|
637
|
-
if (!urls.length)
|
|
682
|
+
if (!urls.length) {
|
|
683
|
+
debugLog('DEBUG', '消息中未检测到平台链接');
|
|
638
684
|
return;
|
|
685
|
+
}
|
|
686
|
+
debugLog('INFO', '检测到链接:', urls);
|
|
639
687
|
if (config.showWaitingTip) {
|
|
640
688
|
try {
|
|
641
|
-
await sendWithTimeout(session,
|
|
689
|
+
await sendWithTimeout(session, texts.waitingTipText);
|
|
642
690
|
}
|
|
643
691
|
catch { }
|
|
644
692
|
}
|
|
645
693
|
await flush(session, urls);
|
|
646
694
|
});
|
|
647
695
|
ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
|
|
696
|
+
debugLog('INFO', `手动解析指令: ${url}`);
|
|
648
697
|
const us = extractUrl(url);
|
|
649
698
|
if (!us.length) {
|
|
650
|
-
await sendWithTimeout(session,
|
|
699
|
+
await sendWithTimeout(session, texts.invalidLinkText);
|
|
651
700
|
return;
|
|
652
701
|
}
|
|
653
702
|
await flush(session, us);
|
|
654
703
|
});
|
|
655
704
|
ctx.command('clear-cache', '清空缓存').action(async ({ session }) => {
|
|
656
705
|
clearAllCache();
|
|
657
|
-
await sendWithTimeout(session,
|
|
706
|
+
await sendWithTimeout(session, texts.cacheClearedText);
|
|
658
707
|
});
|
|
659
708
|
setInterval(() => {
|
|
660
709
|
const now = Date.now();
|
package/package.json
CHANGED