koishi-plugin-video-parser-all 0.8.4 → 0.8.6
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 +4 -8
- package/lib/index.js +12 -174
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ 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
|
+
sameLinkInterval?: number | null | undefined;
|
|
6
8
|
debug?: boolean | null | undefined;
|
|
7
|
-
debugFile?: boolean | null | undefined;
|
|
8
9
|
} & import("cosmokit").Dict & {
|
|
9
10
|
unifiedMessageFormat?: string | null | undefined;
|
|
10
11
|
} & {
|
|
@@ -22,9 +23,6 @@ export declare const Config: Schema<{
|
|
|
22
23
|
retryInterval?: number | null | undefined;
|
|
23
24
|
} & {
|
|
24
25
|
enableForward?: boolean | null | undefined;
|
|
25
|
-
downloadVideoBeforeSend?: boolean | null | undefined;
|
|
26
|
-
maxVideoSize?: number | null | undefined;
|
|
27
|
-
downloadThreads?: number | null | undefined;
|
|
28
26
|
} & {
|
|
29
27
|
messageBufferDelay?: number | null | undefined;
|
|
30
28
|
} & {
|
|
@@ -40,8 +38,9 @@ export declare const Config: Schema<{
|
|
|
40
38
|
}, {
|
|
41
39
|
enable: boolean;
|
|
42
40
|
botName: string;
|
|
41
|
+
showWaitingTip: boolean;
|
|
42
|
+
sameLinkInterval: number;
|
|
43
43
|
debug: boolean;
|
|
44
|
-
debugFile: boolean;
|
|
45
44
|
} & import("cosmokit").Dict & {
|
|
46
45
|
unifiedMessageFormat: string;
|
|
47
46
|
} & {
|
|
@@ -59,9 +58,6 @@ export declare const Config: Schema<{
|
|
|
59
58
|
retryInterval: number;
|
|
60
59
|
} & {
|
|
61
60
|
enableForward: boolean;
|
|
62
|
-
downloadVideoBeforeSend: boolean;
|
|
63
|
-
maxVideoSize: number;
|
|
64
|
-
downloadThreads: number;
|
|
65
61
|
} & {
|
|
66
62
|
messageBufferDelay: number;
|
|
67
63
|
} & {
|
package/lib/index.js
CHANGED
|
@@ -10,19 +10,17 @@ 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");
|
|
14
|
-
const promises_1 = require("stream/promises");
|
|
15
|
-
const worker_threads_1 = require("worker_threads");
|
|
16
13
|
exports.name = 'video-parser-all';
|
|
17
14
|
exports.Config = koishi_1.Schema.intersect([
|
|
18
15
|
koishi_1.Schema.object({
|
|
19
16
|
enable: koishi_1.Schema.boolean().default(true).description('是否启用视频解析插件'),
|
|
20
17
|
botName: koishi_1.Schema.string().default('视频解析机器人').description('合并转发消息中显示的机器人名称'),
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
showWaitingTip: koishi_1.Schema.boolean().default(true).description('解析时显示等待提示'),
|
|
19
|
+
sameLinkInterval: koishi_1.Schema.number().min(0).default(180).description('相同链接重复解析间隔(秒)'),
|
|
20
|
+
debug: koishi_1.Schema.boolean().default(false).description('开启调试模式,在控制台输出详细日志'),
|
|
23
21
|
}).description('基础设置'),
|
|
24
22
|
koishi_1.Schema.object({
|
|
25
|
-
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default(
|
|
23
|
+
unifiedMessageFormat: koishi_1.Schema.string().role('textarea').default(`\${标题}\n\${作者}\n\${简介}\n点赞:\${点赞数}\n收藏:\${收藏数}\n转发:\${转发数}\n播放:\${播放数}\n评论:\${评论数}`).description('统一消息格式,可用变量:${标题} ${作者} ${简介} ${点赞数} ${收藏数} ${转发数} ${播放数} ${评论数} ${视频时长} ${发布时间} ${图片数量} ${作者ID} ${视频链接} ${封面} ${音乐作者} ${音乐标题}'),
|
|
26
24
|
}).description('消息格式设置'),
|
|
27
25
|
koishi_1.Schema.object({
|
|
28
26
|
showImageText: koishi_1.Schema.boolean().default(true).description('是否发送解析后的文字内容'),
|
|
@@ -42,9 +40,6 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
42
40
|
}).description('错误与重试设置'),
|
|
43
41
|
koishi_1.Schema.object({
|
|
44
42
|
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 为单线程)'),
|
|
48
43
|
}).description('发送方式设置'),
|
|
49
44
|
koishi_1.Schema.object({
|
|
50
45
|
messageBufferDelay: koishi_1.Schema.number().min(0).default(0).description('消息缓冲延迟(毫秒)'),
|
|
@@ -66,26 +61,12 @@ const processed = new Map();
|
|
|
66
61
|
const linkBuffer = new Map();
|
|
67
62
|
const logger = new koishi_1.Logger(exports.name);
|
|
68
63
|
let debugEnabled = false;
|
|
69
|
-
let debugFileEnabled = false;
|
|
70
|
-
let debugStream = null;
|
|
71
64
|
function debugLog(level, ...args) {
|
|
72
65
|
if (!debugEnabled)
|
|
73
66
|
return;
|
|
74
67
|
const timestamp = new Date().toISOString();
|
|
75
68
|
const message = `[${timestamp}] [${level}] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`;
|
|
76
69
|
logger.info(message);
|
|
77
|
-
if (debugFileEnabled && debugStream) {
|
|
78
|
-
debugStream.write(message + '\n');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function initDebug(enabled, fileEnabled) {
|
|
82
|
-
debugEnabled = enabled;
|
|
83
|
-
debugFileEnabled = fileEnabled;
|
|
84
|
-
if (fileEnabled && enabled) {
|
|
85
|
-
const logPath = path_1.default.join(process.cwd(), 'debug.log');
|
|
86
|
-
debugStream = fs_1.default.createWriteStream(logPath, { flags: 'a' });
|
|
87
|
-
debugStream.write(`\n=== Debug session started at ${new Date().toISOString()} ===\n`);
|
|
88
|
-
}
|
|
89
70
|
}
|
|
90
71
|
const PLATFORM_KEYWORDS = {
|
|
91
72
|
bilibili: ['bilibili', 'b23', 'www.bilibili.com', 'm.bilibili.com', 'b23.tv', 't.bilibili.com', 'bilibili.com/video', 'bilibili.com/opus', 'bilibili.com/bangumi'],
|
|
@@ -119,103 +100,6 @@ function getErrorMessage(error) {
|
|
|
119
100
|
return error.message;
|
|
120
101
|
return String(error);
|
|
121
102
|
}
|
|
122
|
-
async function getFileSize(url, userAgent) {
|
|
123
|
-
try {
|
|
124
|
-
const response = await axios_1.default.head(url, {
|
|
125
|
-
timeout: 10000,
|
|
126
|
-
headers: { 'User-Agent': userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }
|
|
127
|
-
});
|
|
128
|
-
const contentLength = response.headers['content-length'];
|
|
129
|
-
if (contentLength)
|
|
130
|
-
return Math.round(Number(contentLength) / 1024 / 1024 * 100) / 100;
|
|
131
|
-
}
|
|
132
|
-
catch (error) { }
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
async function downloadVideoThread(workerData) {
|
|
136
|
-
return new Promise((resolve, reject) => {
|
|
137
|
-
const worker = new worker_threads_1.Worker(__filename, { workerData });
|
|
138
|
-
worker.on('message', resolve);
|
|
139
|
-
worker.on('error', reject);
|
|
140
|
-
worker.on('exit', (code) => {
|
|
141
|
-
if (code !== 0)
|
|
142
|
-
reject(new Error(`下载线程异常退出,代码:${code}`));
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
if (!worker_threads_1.isMainThread) {
|
|
147
|
-
const { url, start, end, filename, userAgent } = worker_threads_1.workerData;
|
|
148
|
-
const filePath = path_1.default.join(process.cwd(), 'temp_videos', `${filename}_${start}_${end}.part`);
|
|
149
|
-
(0, axios_1.default)({
|
|
150
|
-
url,
|
|
151
|
-
method: 'GET',
|
|
152
|
-
responseType: 'stream',
|
|
153
|
-
timeout: 60000,
|
|
154
|
-
headers: {
|
|
155
|
-
'User-Agent': userAgent,
|
|
156
|
-
'Range': `bytes=${start}-${end}`
|
|
157
|
-
}
|
|
158
|
-
}).then(response => {
|
|
159
|
-
const writeStream = fs_1.default.createWriteStream(filePath);
|
|
160
|
-
response.data.pipe(writeStream);
|
|
161
|
-
writeStream.on('finish', () => worker_threads_1.parentPort?.postMessage({ success: true, filePath, start, end }));
|
|
162
|
-
writeStream.on('error', (error) => worker_threads_1.parentPort?.postMessage({ success: false, error: error.message }));
|
|
163
|
-
}).catch((error) => worker_threads_1.parentPort?.postMessage({ success: false, error: error.message }));
|
|
164
|
-
}
|
|
165
|
-
async function downloadVideo(url, filename, userAgent, maxSize, threads) {
|
|
166
|
-
const dir = path_1.default.join(process.cwd(), 'temp_videos');
|
|
167
|
-
if (!fs_1.default.existsSync(dir))
|
|
168
|
-
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
169
|
-
const filePath = path_1.default.join(dir, `${filename}.mp4`);
|
|
170
|
-
try {
|
|
171
|
-
if (url.endsWith('.m4a') || url.endsWith('.mp3'))
|
|
172
|
-
return { filePath: '', success: false };
|
|
173
|
-
const fileSize = await getFileSize(url, userAgent);
|
|
174
|
-
if (maxSize > 0 && fileSize > maxSize)
|
|
175
|
-
return { filePath: '', success: false };
|
|
176
|
-
if (threads <= 0 || fileSize === 0) {
|
|
177
|
-
const response = await (0, axios_1.default)({
|
|
178
|
-
url,
|
|
179
|
-
method: 'GET',
|
|
180
|
-
responseType: 'stream',
|
|
181
|
-
timeout: 60000,
|
|
182
|
-
headers: { 'User-Agent': userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }
|
|
183
|
-
});
|
|
184
|
-
const writeStream = fs_1.default.createWriteStream(filePath);
|
|
185
|
-
await (0, promises_1.pipeline)(response.data, writeStream);
|
|
186
|
-
return { filePath, success: true };
|
|
187
|
-
}
|
|
188
|
-
const totalSize = fileSize * 1024 * 1024;
|
|
189
|
-
const chunkSize = Math.ceil(totalSize / threads);
|
|
190
|
-
const promises = [];
|
|
191
|
-
for (let i = 0; i < threads; i++) {
|
|
192
|
-
const start = i * chunkSize;
|
|
193
|
-
const end = i === threads - 1 ? totalSize - 1 : start + chunkSize - 1;
|
|
194
|
-
promises.push(downloadVideoThread({ url, start, end, filename, userAgent }));
|
|
195
|
-
}
|
|
196
|
-
const results = await Promise.all(promises);
|
|
197
|
-
const writeStream = fs_1.default.createWriteStream(filePath);
|
|
198
|
-
for (const result of results) {
|
|
199
|
-
if (!result.success)
|
|
200
|
-
throw new Error(result.error);
|
|
201
|
-
const readStream = fs_1.default.createReadStream(result.filePath);
|
|
202
|
-
await (0, promises_1.pipeline)(readStream, writeStream, { end: false });
|
|
203
|
-
fs_1.default.unlinkSync(result.filePath);
|
|
204
|
-
}
|
|
205
|
-
writeStream.end();
|
|
206
|
-
return { filePath, success: true };
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
if (fs_1.default.existsSync(filePath))
|
|
210
|
-
fs_1.default.unlinkSync(filePath);
|
|
211
|
-
const partFiles = fs_1.default.readdirSync(dir).filter(file => file.startsWith(`${filename}_`) && file.endsWith('.part'));
|
|
212
|
-
partFiles.forEach(file => { try {
|
|
213
|
-
fs_1.default.unlinkSync(path_1.default.join(dir, file));
|
|
214
|
-
}
|
|
215
|
-
catch (e) { } });
|
|
216
|
-
return { filePath: '', success: false };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
103
|
function extractUrl(content) {
|
|
220
104
|
const urlMatches = content.match(/https?:\/\/[^\s\"\'\>]+/gi) || [];
|
|
221
105
|
return urlMatches.filter(url => {
|
|
@@ -425,10 +309,9 @@ function buildForwardNode(session, content, botName) {
|
|
|
425
309
|
return (0, koishi_1.h)('node', { user: { nickname: botName.substring(0, 15), user_id: session.selfId } }, messageContent);
|
|
426
310
|
}
|
|
427
311
|
function apply(ctx, config) {
|
|
428
|
-
|
|
312
|
+
debugEnabled = config.debug || false;
|
|
429
313
|
debugLog('INFO', '插件初始化开始');
|
|
430
314
|
debugLog('INFO', '当前配置:', config);
|
|
431
|
-
// 合并默认文案
|
|
432
315
|
const texts = {
|
|
433
316
|
waitingTipText: config.waitingTipText || '正在解析视频,请稍候...',
|
|
434
317
|
duplicateLinkText: config.duplicateLinkText || '请勿重复解析相同链接',
|
|
@@ -472,37 +355,30 @@ function apply(ctx, config) {
|
|
|
472
355
|
}
|
|
473
356
|
async function parseUrl(url) {
|
|
474
357
|
debugLog('INFO', `开始解析链接: ${url}`);
|
|
475
|
-
// 尝试短链接解析
|
|
476
358
|
const realUrl = await resolveShortUrl(url);
|
|
477
359
|
debugLog('DEBUG', `重定向后的URL: ${realUrl}`);
|
|
478
360
|
const platform = getPlatformType(realUrl);
|
|
479
361
|
if (!platform) {
|
|
480
|
-
debugLog('WARN', `不支持的平台: ${realUrl}`);
|
|
481
362
|
return { success: false, msg: texts.unsupportedPlatformText };
|
|
482
363
|
}
|
|
483
|
-
// 优先使用原始短链接尝试,若失败再用重定向后的URL
|
|
484
364
|
const candidates = [url, realUrl];
|
|
485
365
|
let lastError = null;
|
|
486
366
|
for (const candidate of candidates) {
|
|
487
367
|
try {
|
|
488
368
|
const info = await fetchApi(candidate);
|
|
489
|
-
debugLog('INFO', `解析成功: ${info.title}`);
|
|
490
369
|
return { success: true, data: info };
|
|
491
370
|
}
|
|
492
371
|
catch (error) {
|
|
493
372
|
lastError = getErrorMessage(error);
|
|
494
|
-
debugLog('ERROR', `候选链接解析失败: ${candidate} => ${lastError}`);
|
|
495
373
|
}
|
|
496
374
|
}
|
|
497
375
|
return { success: false, msg: lastError || '解析失败' };
|
|
498
376
|
}
|
|
499
377
|
async function processSingleUrl(session, url) {
|
|
500
|
-
debugLog('INFO', `处理单个URL: ${url}, 用户: ${session.userId}`);
|
|
501
378
|
const hash = crypto_1.default.createHash('md5').update(url).digest('hex');
|
|
502
379
|
const now = Date.now();
|
|
503
380
|
const last = processed.get(hash);
|
|
504
381
|
if (last && (now - last) < config.sameLinkInterval * 1000) {
|
|
505
|
-
debugLog('WARN', `重复解析: ${url}`);
|
|
506
382
|
return { success: false, msg: texts.duplicateLinkText };
|
|
507
383
|
}
|
|
508
384
|
processed.set(hash, now);
|
|
@@ -513,13 +389,11 @@ function apply(ctx, config) {
|
|
|
513
389
|
return { success: true, data: { text, parsed: result.data } };
|
|
514
390
|
}
|
|
515
391
|
async function sendWithTimeout(session, content) {
|
|
516
|
-
debugLog('DEBUG', `发送消息: ${JSON.stringify(content)}`);
|
|
517
392
|
if (config.videoSendTimeout <= 0) {
|
|
518
393
|
try {
|
|
519
394
|
return await session.send(content);
|
|
520
395
|
}
|
|
521
396
|
catch (err) {
|
|
522
|
-
debugLog('ERROR', `发送消息失败: ${getErrorMessage(err)}`);
|
|
523
397
|
if (!config.ignoreSendError)
|
|
524
398
|
throw err;
|
|
525
399
|
return null;
|
|
@@ -532,7 +406,6 @@ function apply(ctx, config) {
|
|
|
532
406
|
]);
|
|
533
407
|
}
|
|
534
408
|
catch (err) {
|
|
535
|
-
debugLog('ERROR', `发送消息超时或失败: ${getErrorMessage(err)}`);
|
|
536
409
|
if (!config.ignoreSendError)
|
|
537
410
|
throw err;
|
|
538
411
|
return null;
|
|
@@ -572,9 +445,7 @@ function apply(ctx, config) {
|
|
|
572
445
|
for (const item of items) {
|
|
573
446
|
const p = item.parsed;
|
|
574
447
|
const text = item.text;
|
|
575
|
-
debugLog('INFO', `开始发送内容,类型: ${p.type}, 标题: ${p.title}`);
|
|
576
448
|
if (text && config.showImageText) {
|
|
577
|
-
debugLog('DEBUG', '发送文本消息');
|
|
578
449
|
if (enableForward)
|
|
579
450
|
forwardMessages.push(buildForwardNode(session, text, botName));
|
|
580
451
|
else {
|
|
@@ -583,7 +454,6 @@ function apply(ctx, config) {
|
|
|
583
454
|
}
|
|
584
455
|
}
|
|
585
456
|
if (p.cover && p.type !== 'live_photo') {
|
|
586
|
-
debugLog('DEBUG', '发送封面图片:', p.cover);
|
|
587
457
|
if (enableForward)
|
|
588
458
|
forwardMessages.push(buildForwardNode(session, koishi_1.h.image(p.cover), botName));
|
|
589
459
|
else {
|
|
@@ -592,42 +462,21 @@ function apply(ctx, config) {
|
|
|
592
462
|
}
|
|
593
463
|
}
|
|
594
464
|
if (p.video && config.showVideoFile && (p.type === 'video' || p.type === 'live')) {
|
|
595
|
-
const
|
|
596
|
-
if (config.downloadVideoBeforeSend) {
|
|
597
|
-
const fname = crypto_1.default.createHash('md5').update(p.video).digest('hex');
|
|
598
|
-
const dl = await downloadVideo(p.video, fname, config.userAgent, config.maxVideoSize, config.downloadThreads);
|
|
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
|
-
}
|
|
604
|
-
}
|
|
605
|
-
debugLog('INFO', `发送视频链接: ${p.video}`);
|
|
606
|
-
return koishi_1.h.video(p.video);
|
|
607
|
-
};
|
|
465
|
+
const videoMsg = koishi_1.h.video(p.video);
|
|
608
466
|
if (enableForward) {
|
|
609
|
-
|
|
610
|
-
forwardMessages.push(buildForwardNode(session, vMsg, botName));
|
|
467
|
+
forwardMessages.push(buildForwardNode(session, videoMsg, botName));
|
|
611
468
|
}
|
|
612
469
|
else {
|
|
613
470
|
try {
|
|
614
|
-
|
|
615
|
-
await sendWithTimeout(session, vMsg);
|
|
616
|
-
}
|
|
617
|
-
catch (e) {
|
|
618
|
-
debugLog('ERROR', `发送视频失败: ${getErrorMessage(e)},尝试直接发送链接`);
|
|
619
|
-
try {
|
|
620
|
-
await sendWithTimeout(session, koishi_1.h.video(p.video));
|
|
621
|
-
}
|
|
622
|
-
catch { }
|
|
471
|
+
await sendWithTimeout(session, videoMsg);
|
|
623
472
|
}
|
|
473
|
+
catch { }
|
|
624
474
|
await delay(500);
|
|
625
475
|
}
|
|
626
476
|
}
|
|
627
477
|
if (p.type === 'image' || p.type === 'live_photo') {
|
|
628
478
|
const mediaList = [];
|
|
629
479
|
if (p.type === 'live_photo' && p.live_photo?.length) {
|
|
630
|
-
debugLog('INFO', `发送实况图集,共 ${p.live_photo.length} 张`);
|
|
631
480
|
for (const lp of p.live_photo) {
|
|
632
481
|
if (lp.image)
|
|
633
482
|
mediaList.push({ type: 'image', url: lp.image });
|
|
@@ -636,7 +485,6 @@ function apply(ctx, config) {
|
|
|
636
485
|
}
|
|
637
486
|
}
|
|
638
487
|
else if (p.images?.length) {
|
|
639
|
-
debugLog('INFO', `发送图集,共 ${p.images.length} 张`);
|
|
640
488
|
p.images.forEach(url => mediaList.push({ type: 'image', url }));
|
|
641
489
|
}
|
|
642
490
|
if (enableForward) {
|
|
@@ -647,25 +495,20 @@ function apply(ctx, config) {
|
|
|
647
495
|
}
|
|
648
496
|
else {
|
|
649
497
|
for (const m of mediaList) {
|
|
650
|
-
debugLog('DEBUG', `发送${m.type}: ${m.url}`);
|
|
651
498
|
try {
|
|
652
499
|
await sendWithTimeout(session, m.type === 'image' ? koishi_1.h.image(m.url) : koishi_1.h.video(m.url));
|
|
653
500
|
await delay(200);
|
|
654
501
|
}
|
|
655
|
-
catch
|
|
656
|
-
debugLog('ERROR', `发送${m.type}失败: ${getErrorMessage(e)}`);
|
|
657
|
-
}
|
|
502
|
+
catch { }
|
|
658
503
|
}
|
|
659
504
|
}
|
|
660
505
|
}
|
|
661
506
|
}
|
|
662
507
|
if (enableForward && forwardMessages.length) {
|
|
663
|
-
debugLog('INFO', `合并转发消息,共 ${forwardMessages.length} 条`);
|
|
664
508
|
try {
|
|
665
509
|
await sendWithTimeout(session, (0, koishi_1.h)('message', { forward: true }, forwardMessages.slice(0, 100)));
|
|
666
510
|
}
|
|
667
|
-
catch
|
|
668
|
-
debugLog('ERROR', `合并转发失败,降级逐条发送: ${getErrorMessage(e)}`);
|
|
511
|
+
catch {
|
|
669
512
|
for (const node of forwardMessages) {
|
|
670
513
|
try {
|
|
671
514
|
await sendWithTimeout(session, node.data.content);
|
|
@@ -680,13 +523,9 @@ function apply(ctx, config) {
|
|
|
680
523
|
if (!config.enable)
|
|
681
524
|
return;
|
|
682
525
|
const content = session.content?.trim() || '';
|
|
683
|
-
debugLog('INFO', `收到消息: "${content}"`);
|
|
684
526
|
const urls = extractUrl(content);
|
|
685
|
-
if (!urls.length)
|
|
686
|
-
debugLog('DEBUG', '消息中未检测到平台链接');
|
|
527
|
+
if (!urls.length)
|
|
687
528
|
return;
|
|
688
|
-
}
|
|
689
|
-
debugLog('INFO', '检测到链接:', urls);
|
|
690
529
|
if (config.showWaitingTip) {
|
|
691
530
|
try {
|
|
692
531
|
await sendWithTimeout(session, texts.waitingTipText);
|
|
@@ -696,7 +535,6 @@ function apply(ctx, config) {
|
|
|
696
535
|
await flush(session, urls);
|
|
697
536
|
});
|
|
698
537
|
ctx.command('parse <url>', '手动解析视频').action(async ({ session }, url) => {
|
|
699
|
-
debugLog('INFO', `手动解析指令: ${url}`);
|
|
700
538
|
const us = extractUrl(url);
|
|
701
539
|
if (!us.length) {
|
|
702
540
|
await sendWithTimeout(session, texts.invalidLinkText);
|
package/package.json
CHANGED