koishi-plugin-video-parser-all 0.8.2 → 0.8.4
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 +115 -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,28 @@ 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
|
+
// 合并默认文案
|
|
432
|
+
const texts = {
|
|
433
|
+
waitingTipText: config.waitingTipText || '正在解析视频,请稍候...',
|
|
434
|
+
duplicateLinkText: config.duplicateLinkText || '请勿重复解析相同链接',
|
|
435
|
+
unsupportedPlatformText: config.unsupportedPlatformText || '不支持该平台链接',
|
|
436
|
+
invalidLinkText: config.invalidLinkText || '无效的视频链接',
|
|
437
|
+
cacheClearedText: config.cacheClearedText || '✅ 缓存已清空',
|
|
438
|
+
parseErrorPrefix: config.parseErrorPrefix || '❌ 解析失败:',
|
|
439
|
+
parseErrorItemFormat: config.parseErrorItemFormat || '【${url}】: ${msg}',
|
|
440
|
+
};
|
|
420
441
|
clearAllCache();
|
|
421
442
|
const http = axios_1.default.create({
|
|
422
443
|
timeout: config.timeout,
|
|
423
444
|
headers: {
|
|
424
|
-
'User-Agent': config.userAgent,
|
|
445
|
+
'User-Agent': config.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
425
446
|
'Referer': 'https://www.baidu.com/',
|
|
426
447
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
427
448
|
}
|
|
428
449
|
});
|
|
429
450
|
async function fetchApi(url) {
|
|
451
|
+
debugLog('INFO', `调用API解析: ${url}`);
|
|
430
452
|
for (let i = 0; i <= config.retryTimes; i++) {
|
|
431
453
|
try {
|
|
432
454
|
const res = await http.get('https://api.bugpk.com/api/short_videos', {
|
|
@@ -434,6 +456,7 @@ function apply(ctx, config) {
|
|
|
434
456
|
timeout: config.timeout
|
|
435
457
|
});
|
|
436
458
|
debugLog('INFO', `API响应: code=${res.data?.code}, msg=${res.data?.msg}`);
|
|
459
|
+
debugLog('DEBUG', 'API完整响应:', res.data);
|
|
437
460
|
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
|
|
438
461
|
return parseApiResponse(res.data, config.maxDescLength);
|
|
439
462
|
}
|
|
@@ -448,50 +471,55 @@ function apply(ctx, config) {
|
|
|
448
471
|
throw new Error('API请求全部失败');
|
|
449
472
|
}
|
|
450
473
|
async function parseUrl(url) {
|
|
451
|
-
debugLog('INFO',
|
|
452
|
-
|
|
453
|
-
realUrl =
|
|
454
|
-
debugLog('DEBUG',
|
|
474
|
+
debugLog('INFO', `开始解析链接: ${url}`);
|
|
475
|
+
// 尝试短链接解析
|
|
476
|
+
const realUrl = await resolveShortUrl(url);
|
|
477
|
+
debugLog('DEBUG', `重定向后的URL: ${realUrl}`);
|
|
455
478
|
const platform = getPlatformType(realUrl);
|
|
456
479
|
if (!platform) {
|
|
457
480
|
debugLog('WARN', `不支持的平台: ${realUrl}`);
|
|
458
|
-
return { success: false, msg:
|
|
481
|
+
return { success: false, msg: texts.unsupportedPlatformText };
|
|
459
482
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
483
|
+
// 优先使用原始短链接尝试,若失败再用重定向后的URL
|
|
484
|
+
const candidates = [url, realUrl];
|
|
485
|
+
let lastError = null;
|
|
486
|
+
for (const candidate of candidates) {
|
|
487
|
+
try {
|
|
488
|
+
const info = await fetchApi(candidate);
|
|
489
|
+
debugLog('INFO', `解析成功: ${info.title}`);
|
|
490
|
+
return { success: true, data: info };
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
lastError = getErrorMessage(error);
|
|
494
|
+
debugLog('ERROR', `候选链接解析失败: ${candidate} => ${lastError}`);
|
|
495
|
+
}
|
|
468
496
|
}
|
|
497
|
+
return { success: false, msg: lastError || '解析失败' };
|
|
469
498
|
}
|
|
470
499
|
async function processSingleUrl(session, url) {
|
|
500
|
+
debugLog('INFO', `处理单个URL: ${url}, 用户: ${session.userId}`);
|
|
471
501
|
const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
|
|
472
502
|
const now = Date.now();
|
|
473
503
|
const last = processed.get(hash);
|
|
474
504
|
if (last && (now - last) < config.sameLinkInterval * 1000) {
|
|
475
505
|
debugLog('WARN', `重复解析: ${url}`);
|
|
476
|
-
return { success: false, msg:
|
|
506
|
+
return { success: false, msg: texts.duplicateLinkText };
|
|
477
507
|
}
|
|
478
508
|
processed.set(hash, now);
|
|
479
509
|
const result = await parseUrl(url);
|
|
480
|
-
if (!result.success)
|
|
481
|
-
return
|
|
482
|
-
}
|
|
510
|
+
if (!result.success)
|
|
511
|
+
return result;
|
|
483
512
|
const text = generateFormattedText(result.data, config.unifiedMessageFormat);
|
|
484
|
-
return {
|
|
485
|
-
success: true,
|
|
486
|
-
data: { text, parsed: result.data }
|
|
487
|
-
};
|
|
513
|
+
return { success: true, data: { text, parsed: result.data } };
|
|
488
514
|
}
|
|
489
515
|
async function sendWithTimeout(session, content) {
|
|
516
|
+
debugLog('DEBUG', `发送消息: ${JSON.stringify(content)}`);
|
|
490
517
|
if (config.videoSendTimeout <= 0) {
|
|
491
518
|
try {
|
|
492
519
|
return await session.send(content);
|
|
493
520
|
}
|
|
494
521
|
catch (err) {
|
|
522
|
+
debugLog('ERROR', `发送消息失败: ${getErrorMessage(err)}`);
|
|
495
523
|
if (!config.ignoreSendError)
|
|
496
524
|
throw err;
|
|
497
525
|
return null;
|
|
@@ -504,6 +532,7 @@ function apply(ctx, config) {
|
|
|
504
532
|
]);
|
|
505
533
|
}
|
|
506
534
|
catch (err) {
|
|
535
|
+
debugLog('ERROR', `发送消息超时或失败: ${getErrorMessage(err)}`);
|
|
507
536
|
if (!config.ignoreSendError)
|
|
508
537
|
throw err;
|
|
509
538
|
return null;
|
|
@@ -525,11 +554,14 @@ function apply(ctx, config) {
|
|
|
525
554
|
items.push(res.data);
|
|
526
555
|
}
|
|
527
556
|
else {
|
|
528
|
-
|
|
557
|
+
const item = texts.parseErrorItemFormat
|
|
558
|
+
.replace(/\$\{url\}/g, url.length > 50 ? url.slice(0, 50) + '...' : url)
|
|
559
|
+
.replace(/\$\{msg\}/g, res.msg);
|
|
560
|
+
errors.push(item);
|
|
529
561
|
}
|
|
530
562
|
}
|
|
531
563
|
if (errors.length) {
|
|
532
|
-
await sendWithTimeout(session,
|
|
564
|
+
await sendWithTimeout(session, `${texts.parseErrorPrefix}\n${errors.join('\n')}`).catch(() => { });
|
|
533
565
|
await delay(500);
|
|
534
566
|
}
|
|
535
567
|
if (!items.length)
|
|
@@ -540,7 +572,9 @@ function apply(ctx, config) {
|
|
|
540
572
|
for (const item of items) {
|
|
541
573
|
const p = item.parsed;
|
|
542
574
|
const text = item.text;
|
|
543
|
-
|
|
575
|
+
debugLog('INFO', `开始发送内容,类型: ${p.type}, 标题: ${p.title}`);
|
|
576
|
+
if (text && config.showImageText) {
|
|
577
|
+
debugLog('DEBUG', '发送文本消息');
|
|
544
578
|
if (enableForward)
|
|
545
579
|
forwardMessages.push(buildForwardNode(session, text, botName));
|
|
546
580
|
else {
|
|
@@ -549,6 +583,7 @@ function apply(ctx, config) {
|
|
|
549
583
|
}
|
|
550
584
|
}
|
|
551
585
|
if (p.cover && p.type !== 'live_photo') {
|
|
586
|
+
debugLog('DEBUG', '发送封面图片:', p.cover);
|
|
552
587
|
if (enableForward)
|
|
553
588
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.cover), botName));
|
|
554
589
|
else {
|
|
@@ -561,9 +596,13 @@ function apply(ctx, config) {
|
|
|
561
596
|
if (config.downloadVideoBeforeSend) {
|
|
562
597
|
const fname = crypto_1.default.createHash('md5').update(p.video).digest('hex');
|
|
563
598
|
const dl = await downloadVideo(p.video, fname, config.userAgent, config.maxVideoSize, config.downloadThreads);
|
|
564
|
-
if (dl.success)
|
|
565
|
-
|
|
599
|
+
if (dl.success) {
|
|
600
|
+
const fileUrl = (0, url_1.pathToFileURL)(dl.filePath).href;
|
|
601
|
+
debugLog('INFO', `视频下载成功,发送文件: ${fileUrl}`);
|
|
602
|
+
return koishi_1.h.file(fileUrl);
|
|
603
|
+
}
|
|
566
604
|
}
|
|
605
|
+
debugLog('INFO', `发送视频链接: ${p.video}`);
|
|
567
606
|
return koishi_1.h.video(p.video);
|
|
568
607
|
};
|
|
569
608
|
if (enableForward) {
|
|
@@ -575,7 +614,8 @@ function apply(ctx, config) {
|
|
|
575
614
|
const vMsg = await sendVideo();
|
|
576
615
|
await sendWithTimeout(session, vMsg);
|
|
577
616
|
}
|
|
578
|
-
catch {
|
|
617
|
+
catch (e) {
|
|
618
|
+
debugLog('ERROR', `发送视频失败: ${getErrorMessage(e)},尝试直接发送链接`);
|
|
579
619
|
try {
|
|
580
620
|
await sendWithTimeout(session, koishi_1.h.video(p.video));
|
|
581
621
|
}
|
|
@@ -587,6 +627,7 @@ function apply(ctx, config) {
|
|
|
587
627
|
if (p.type === 'image' || p.type === 'live_photo') {
|
|
588
628
|
const mediaList = [];
|
|
589
629
|
if (p.type === 'live_photo' && p.live_photo?.length) {
|
|
630
|
+
debugLog('INFO', `发送实况图集,共 ${p.live_photo.length} 张`);
|
|
590
631
|
for (const lp of p.live_photo) {
|
|
591
632
|
if (lp.image)
|
|
592
633
|
mediaList.push({ type: 'image', url: lp.image });
|
|
@@ -595,6 +636,7 @@ function apply(ctx, config) {
|
|
|
595
636
|
}
|
|
596
637
|
}
|
|
597
638
|
else if (p.images?.length) {
|
|
639
|
+
debugLog('INFO', `发送图集,共 ${p.images.length} 张`);
|
|
598
640
|
p.images.forEach(url => mediaList.push({ type: 'image', url }));
|
|
599
641
|
}
|
|
600
642
|
if (enableForward) {
|
|
@@ -605,20 +647,25 @@ function apply(ctx, config) {
|
|
|
605
647
|
}
|
|
606
648
|
else {
|
|
607
649
|
for (const m of mediaList) {
|
|
650
|
+
debugLog('DEBUG', `发送${m.type}: ${m.url}`);
|
|
608
651
|
try {
|
|
609
652
|
await sendWithTimeout(session, m.type === 'image' ? koishi_1.h.image(m.url) : koishi_1.h.video(m.url));
|
|
610
653
|
await delay(200);
|
|
611
654
|
}
|
|
612
|
-
catch {
|
|
655
|
+
catch (e) {
|
|
656
|
+
debugLog('ERROR', `发送${m.type}失败: ${getErrorMessage(e)}`);
|
|
657
|
+
}
|
|
613
658
|
}
|
|
614
659
|
}
|
|
615
660
|
}
|
|
616
661
|
}
|
|
617
662
|
if (enableForward && forwardMessages.length) {
|
|
663
|
+
debugLog('INFO', `合并转发消息,共 ${forwardMessages.length} 条`);
|
|
618
664
|
try {
|
|
619
665
|
await sendWithTimeout(session, (0, koishi_1.h)('message', { forward: true }, forwardMessages.slice(0, 100)));
|
|
620
666
|
}
|
|
621
|
-
catch {
|
|
667
|
+
catch (e) {
|
|
668
|
+
debugLog('ERROR', `合并转发失败,降级逐条发送: ${getErrorMessage(e)}`);
|
|
622
669
|
for (const node of forwardMessages) {
|
|
623
670
|
try {
|
|
624
671
|
await sendWithTimeout(session, node.data.content);
|
|
@@ -633,28 +680,33 @@ function apply(ctx, config) {
|
|
|
633
680
|
if (!config.enable)
|
|
634
681
|
return;
|
|
635
682
|
const content = session.content?.trim() || '';
|
|
683
|
+
debugLog('INFO', `收到消息: "${content}"`);
|
|
636
684
|
const urls = extractUrl(content);
|
|
637
|
-
if (!urls.length)
|
|
685
|
+
if (!urls.length) {
|
|
686
|
+
debugLog('DEBUG', '消息中未检测到平台链接');
|
|
638
687
|
return;
|
|
688
|
+
}
|
|
689
|
+
debugLog('INFO', '检测到链接:', urls);
|
|
639
690
|
if (config.showWaitingTip) {
|
|
640
691
|
try {
|
|
641
|
-
await sendWithTimeout(session,
|
|
692
|
+
await sendWithTimeout(session, texts.waitingTipText);
|
|
642
693
|
}
|
|
643
694
|
catch { }
|
|
644
695
|
}
|
|
645
696
|
await flush(session, urls);
|
|
646
697
|
});
|
|
647
698
|
ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
|
|
699
|
+
debugLog('INFO', `手动解析指令: ${url}`);
|
|
648
700
|
const us = extractUrl(url);
|
|
649
701
|
if (!us.length) {
|
|
650
|
-
await sendWithTimeout(session,
|
|
702
|
+
await sendWithTimeout(session, texts.invalidLinkText);
|
|
651
703
|
return;
|
|
652
704
|
}
|
|
653
705
|
await flush(session, us);
|
|
654
706
|
});
|
|
655
707
|
ctx.command('clear-cache', '清空缓存').action(async ({ session }) => {
|
|
656
708
|
clearAllCache();
|
|
657
|
-
await sendWithTimeout(session,
|
|
709
|
+
await sendWithTimeout(session, texts.cacheClearedText);
|
|
658
710
|
});
|
|
659
711
|
setInterval(() => {
|
|
660
712
|
const now = Date.now();
|
package/package.json
CHANGED