koishi-plugin-share-links-analysis 0.5.1 → 0.5.2
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.js +3 -3
- package/lib/parsers/bilibili.js +6 -3
- package/lib/parsers/twitter.js +8 -1
- package/lib/parsers/xiaohongshu.js +5 -1
- package/lib/types.d.ts +6 -1
- package/lib/utils.js +155 -153
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -35,7 +35,8 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
35
35
|
koishi_1.Schema.const("plain").description("普通发送"),
|
|
36
36
|
koishi_1.Schema.const("forward").description("合并转发"),
|
|
37
37
|
koishi_1.Schema.const("mixed").description("混合发送"),
|
|
38
|
-
]).default("
|
|
38
|
+
]).default("forward").description("发送模式"),
|
|
39
|
+
sendFiles: koishi_1.Schema.boolean().default(true).description("是否发送文件(视频等)"),
|
|
39
40
|
}).description("基础设置"),
|
|
40
41
|
koishi_1.Schema.object({
|
|
41
42
|
format: koishi_1.Schema.string().role('textarea').default(`{title}
|
|
@@ -45,8 +46,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
45
46
|
----------
|
|
46
47
|
{mainbody}
|
|
47
48
|
----------
|
|
48
|
-
{sourceUrl}
|
|
49
|
-
{video}`).description('图文/视频输出格式。<br/>可用占位符: `{title}`, `{cover}`, `{authorName}`, `{mainbody}`, `{stats}`, `{sourceUrl}`, `{video}`, `{videoUrl}`'),
|
|
49
|
+
{sourceUrl}`).description('图文/视频输出格式。<br/>可用占位符: `{title}`, `{cover}`, `{authorName}`, `{mainbody}`, `{stats}`, `{sourceUrl}`'),
|
|
50
50
|
}).description("格式化模板"),
|
|
51
51
|
koishi_1.Schema.object({
|
|
52
52
|
parseLimit: koishi_1.Schema.number().default(3).description("单对话多链接解析上限"),
|
package/lib/parsers/bilibili.js
CHANGED
|
@@ -174,15 +174,18 @@ async function process(ctx, config, link, session) {
|
|
|
174
174
|
const liked = (0, utils_1.numeral)(data.stat.like, config);
|
|
175
175
|
const coin = (0, utils_1.numeral)(data.stat.coin, config);
|
|
176
176
|
const favorite = (0, utils_1.numeral)(data.stat.favorite, config);
|
|
177
|
-
const statsString = `播放: ${play} | 弹幕: ${danmaku}
|
|
178
|
-
|
|
177
|
+
const statsString = `播放: ${play} | 弹幕: ${danmaku}\n点赞: ${liked} | 硬币: ${coin} | 收藏: ${favorite}`;
|
|
178
|
+
let files = [];
|
|
179
|
+
if (videoUrl) {
|
|
180
|
+
files = [{ type: "video", url: videoUrl }];
|
|
181
|
+
}
|
|
179
182
|
return {
|
|
180
183
|
platform: 'bilibili',
|
|
181
184
|
title: data.title,
|
|
182
185
|
authorName: data.owner.name,
|
|
183
186
|
mainbody: (0, utils_1.escapeHtml)(data.desc),
|
|
184
187
|
coverUrl: data.pic,
|
|
185
|
-
|
|
188
|
+
files: files,
|
|
186
189
|
sourceUrl: `https://www.bilibili.com/video/${data.bvid}`,
|
|
187
190
|
stats: statsString,
|
|
188
191
|
};
|
package/lib/parsers/twitter.js
CHANGED
|
@@ -104,6 +104,13 @@ async function process(ctx, config, link, session) {
|
|
|
104
104
|
}
|
|
105
105
|
const image = media?.images ? media?.images.map(img => koishi_1.h.image(img).toString()).join('\n') : '';
|
|
106
106
|
const mainbody = (0, utils_1.escapeHtml)(tweet_text) + image;
|
|
107
|
+
const videos = media?.videos;
|
|
108
|
+
let files = [];
|
|
109
|
+
if (videos) {
|
|
110
|
+
for (const video of videos) {
|
|
111
|
+
files.push({ type: "video", url: video.url });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
107
114
|
return {
|
|
108
115
|
platform: 'twitter',
|
|
109
116
|
title: `@${tweetData.user_screen_name} 的推文`,
|
|
@@ -111,7 +118,7 @@ async function process(ctx, config, link, session) {
|
|
|
111
118
|
mainbody: mainbody,
|
|
112
119
|
sourceUrl: link.url,
|
|
113
120
|
stats: statsString,
|
|
114
|
-
|
|
121
|
+
files: files,
|
|
115
122
|
coverUrl: media?.videos[0]?.preview_url,
|
|
116
123
|
};
|
|
117
124
|
}
|
|
@@ -263,13 +263,17 @@ async function process(ctx, config, link, session) {
|
|
|
263
263
|
const statsString = `点赞: ${liked} | 收藏: ${collected} | 评论: ${comment}`;
|
|
264
264
|
const image = images ? images.map(img => koishi_1.h.image(img).toString()).join('\n') : '';
|
|
265
265
|
const mainbody = (0, utils_1.escapeHtml)(noteData.desc.trim()) + image;
|
|
266
|
+
let files = [];
|
|
267
|
+
if (videoUrl) {
|
|
268
|
+
files = [{ type: "video", url: videoUrl }];
|
|
269
|
+
}
|
|
266
270
|
return {
|
|
267
271
|
platform: 'xiaohongshu',
|
|
268
272
|
title: noteData.title,
|
|
269
273
|
authorName: noteData.user.nickname,
|
|
270
274
|
mainbody: mainbody,
|
|
271
275
|
coverUrl: coverUrl,
|
|
272
|
-
|
|
276
|
+
files: files,
|
|
273
277
|
sourceUrl: urlToFetch,
|
|
274
278
|
stats: statsString,
|
|
275
279
|
};
|
package/lib/types.d.ts
CHANGED
|
@@ -10,10 +10,14 @@ export interface ParsedInfo {
|
|
|
10
10
|
authorName: string;
|
|
11
11
|
mainbody?: string;
|
|
12
12
|
coverUrl?: string;
|
|
13
|
-
|
|
13
|
+
files: FileInfo[];
|
|
14
14
|
sourceUrl: string;
|
|
15
15
|
stats: string;
|
|
16
16
|
}
|
|
17
|
+
export interface FileInfo {
|
|
18
|
+
type: 'video' | 'audio' | 'generic';
|
|
19
|
+
url: string;
|
|
20
|
+
}
|
|
17
21
|
export interface PluginConfig {
|
|
18
22
|
Video_ClarityPriority: '1' | '2';
|
|
19
23
|
Max_size: number;
|
|
@@ -21,6 +25,7 @@ export interface PluginConfig {
|
|
|
21
25
|
Min_Interval: number;
|
|
22
26
|
waitTip_Switch: false | string;
|
|
23
27
|
useForward: 'plain' | 'forward' | 'mixed';
|
|
28
|
+
sendFiles: boolean;
|
|
24
29
|
format: string;
|
|
25
30
|
parseLimit: number;
|
|
26
31
|
useNumeral: boolean;
|
package/lib/utils.js
CHANGED
|
@@ -275,7 +275,6 @@ async function sendResult_plain(session, config, result, logger) {
|
|
|
275
275
|
const localDownloadDir = config.localDownloadDir;
|
|
276
276
|
const onebotReadDir = config.onebotReadDir;
|
|
277
277
|
let mediaCoverUrl = result.coverUrl;
|
|
278
|
-
let mediaVideoUrl = result.videoUrl || null;
|
|
279
278
|
let mediaMainbody = result.mainbody;
|
|
280
279
|
let proxy = undefined;
|
|
281
280
|
if (config.proxy_settings[result.platform]) {
|
|
@@ -294,41 +293,6 @@ async function sendResult_plain(session, config, result, logger) {
|
|
|
294
293
|
mediaCoverUrl = '';
|
|
295
294
|
}
|
|
296
295
|
}
|
|
297
|
-
// --- 视频:先检查大小 ---
|
|
298
|
-
let videoExceedsLimit = false;
|
|
299
|
-
if (result.videoUrl) {
|
|
300
|
-
const sizeBytes = await getFileSize(result.videoUrl, proxy, config.userAgent, logger);
|
|
301
|
-
const maxBytes = config.Max_size !== undefined ? config.Max_size * 1024 * 1024 : undefined;
|
|
302
|
-
// 日志用 MB(保留 2 位小数)
|
|
303
|
-
const formatMB = (bytes) => (bytes / (1024 * 1024)).toFixed(2);
|
|
304
|
-
if (sizeBytes === null) {
|
|
305
|
-
logger.warn(`无法获取视频大小: ${result.videoUrl},默认允许下载`);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
const sizeMB = formatMB(sizeBytes);
|
|
309
|
-
if (maxBytes !== undefined && sizeBytes > maxBytes) {
|
|
310
|
-
videoExceedsLimit = true;
|
|
311
|
-
mediaVideoUrl = null;
|
|
312
|
-
const maxMB = config.Max_size.toFixed(2);
|
|
313
|
-
if (config.logLevel !== 'none') {
|
|
314
|
-
logger.info(`视频大小超限 (${sizeMB} MB > ${maxMB} MB): ${result.videoUrl}`);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
// 大小合规,执行下载
|
|
319
|
-
try {
|
|
320
|
-
mediaVideoUrl = await downloadAndMapUrl(result.videoUrl, proxy, config.userAgent, localDownloadDir, onebotReadDir, logger);
|
|
321
|
-
if (config.logLevel === 'full') {
|
|
322
|
-
logger.info(`视频已下载 (${sizeMB} MB): ${mediaVideoUrl}`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
catch (e) {
|
|
326
|
-
logger.warn(`视频下载失败: ${result.videoUrl}`, e);
|
|
327
|
-
mediaVideoUrl = null;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
296
|
// --- 下载 mainbody 中的图片 ---
|
|
333
297
|
if (result.mainbody) {
|
|
334
298
|
const imgMatches = [...result.mainbody.matchAll(/<img\s[^>]*src\s*=\s*["']?([^"'>\s]+)["']?/gi)];
|
|
@@ -359,34 +323,72 @@ async function sendResult_plain(session, config, result, logger) {
|
|
|
359
323
|
message = message.replace(/{sourceUrl}/g, escapeHtml(result.sourceUrl || ''));
|
|
360
324
|
message = message.replace(/{cover}/g, mediaCoverUrl ? koishi_1.h.image(mediaCoverUrl).toString() : '');
|
|
361
325
|
message = message.replace(/{stats}/g, escapeHtml(result.stats || ''));
|
|
362
|
-
//
|
|
363
|
-
if (result.videoUrl) {
|
|
364
|
-
message = message.replace(/{videoUrl}/g, escapeHtml(result.videoUrl));
|
|
365
|
-
if (videoExceedsLimit) {
|
|
366
|
-
const tip = escapeHtml(config.Max_size_tip);
|
|
367
|
-
message = message.replace(/{video}/g, tip);
|
|
368
|
-
}
|
|
369
|
-
else if (mediaVideoUrl) {
|
|
370
|
-
message = message.replace(/{video}/g, koishi_1.h.video(mediaVideoUrl).toString());
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
message = message.replace(/{video}/g, '');
|
|
374
|
-
}
|
|
375
|
-
if (config.logLevel === 'link_only') {
|
|
376
|
-
logger.info(`视频直链 (${result.platform}): ${result.videoUrl}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
else {
|
|
380
|
-
message = message.replace(/{video}/g, '');
|
|
381
|
-
message = message.replace(/{videoUrl}/g, '');
|
|
382
|
-
}
|
|
326
|
+
// 清理空行
|
|
383
327
|
const cleanMessage = message.split('\n').filter(line => line.trim() !== '' || line.includes('<')).join('\n');
|
|
384
328
|
if (config.logLevel === 'full') {
|
|
385
329
|
logger.info(`解析结果: \n ${JSON.stringify(result, null, 2)}`);
|
|
386
330
|
}
|
|
331
|
+
const sendPromises = [];
|
|
332
|
+
// 发送主消息
|
|
387
333
|
if (cleanMessage) {
|
|
388
|
-
|
|
334
|
+
sendPromises.push(session.send(koishi_1.h.quote(session.messageId) + cleanMessage));
|
|
335
|
+
}
|
|
336
|
+
// --- 发送 files 中的所有媒体(video/audio/generic)---
|
|
337
|
+
if (config.sendFiles && Array.isArray(result.files)) {
|
|
338
|
+
for (const file of result.files) {
|
|
339
|
+
const { type, url: remoteUrl } = file;
|
|
340
|
+
if (!['video', 'audio', 'generic'].includes(type))
|
|
341
|
+
continue;
|
|
342
|
+
let shouldSend = true;
|
|
343
|
+
if (config.Max_size !== undefined) {
|
|
344
|
+
const sizeBytes = await getFileSize(remoteUrl, proxy, config.userAgent, logger);
|
|
345
|
+
const maxBytes = config.Max_size * 1024 * 1024;
|
|
346
|
+
if (sizeBytes !== null && sizeBytes > maxBytes) {
|
|
347
|
+
shouldSend = false;
|
|
348
|
+
if (config.logLevel !== 'none') {
|
|
349
|
+
const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(2);
|
|
350
|
+
const maxMB = config.Max_size.toFixed(2);
|
|
351
|
+
sendPromises.push(session.send(`文件大小超限 (${sizeMB} MB > ${maxMB} MB)`));
|
|
352
|
+
logger.info(`文件大小超限 (${sizeMB} MB > ${maxMB} MB),跳过: ${remoteUrl}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (shouldSend) {
|
|
357
|
+
try {
|
|
358
|
+
const localUrl = await downloadAndMapUrl(remoteUrl, proxy, config.userAgent, localDownloadDir, onebotReadDir, logger);
|
|
359
|
+
if (!localUrl)
|
|
360
|
+
continue;
|
|
361
|
+
let element = null;
|
|
362
|
+
if (type === 'video') {
|
|
363
|
+
element = koishi_1.h.video(localUrl).toString();
|
|
364
|
+
}
|
|
365
|
+
else if (type === 'audio') {
|
|
366
|
+
element = koishi_1.h.audio(localUrl).toString();
|
|
367
|
+
}
|
|
368
|
+
else if (type === 'generic') {
|
|
369
|
+
// 注意:标准 OneBot v11 不支持 file,部分实现支持
|
|
370
|
+
// 若你环境不支持,可改用文本链接:element = escapeHtml(remoteUrl);
|
|
371
|
+
element = koishi_1.h.file(localUrl).toString();
|
|
372
|
+
}
|
|
373
|
+
if (element) {
|
|
374
|
+
sendPromises.push(session.send(element));
|
|
375
|
+
if (config.logLevel === 'link_only') {
|
|
376
|
+
logger.info(`${type} 直链 (${result.platform}): ${remoteUrl}`);
|
|
377
|
+
}
|
|
378
|
+
if (config.logLevel === 'full') {
|
|
379
|
+
const size = await getFileSize(remoteUrl, proxy, config.userAgent, logger);
|
|
380
|
+
const sizeMB = size ? (size / (1024 * 1024)).toFixed(2) : 'unknown';
|
|
381
|
+
logger.info(`${type} 已发送 (${sizeMB} MB): ${localUrl}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (e) {
|
|
386
|
+
logger.warn(`${type} 下载/发送失败: ${remoteUrl}`, e);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
389
390
|
}
|
|
391
|
+
await Promise.all(sendPromises);
|
|
390
392
|
}
|
|
391
393
|
async function sendResult_forward(session, config, result, logger, mixed_sending = false) {
|
|
392
394
|
if (config.logLevel === 'full') {
|
|
@@ -395,7 +397,6 @@ async function sendResult_forward(session, config, result, logger, mixed_sending
|
|
|
395
397
|
const localDownloadDir = config.localDownloadDir;
|
|
396
398
|
const onebotReadDir = config.onebotReadDir;
|
|
397
399
|
let mediaCoverUrl = result.coverUrl;
|
|
398
|
-
let mediaVideoUrl = result.videoUrl || null;
|
|
399
400
|
let mediaMainbody = result.mainbody;
|
|
400
401
|
let proxy = undefined;
|
|
401
402
|
if (config.proxy_settings[result.platform]) {
|
|
@@ -412,39 +413,6 @@ async function sendResult_forward(session, config, result, logger, mixed_sending
|
|
|
412
413
|
mediaCoverUrl = '';
|
|
413
414
|
}
|
|
414
415
|
}
|
|
415
|
-
// --- 视频大小检查 + 下载 ---
|
|
416
|
-
let videoExceedsLimit = false;
|
|
417
|
-
if (result.videoUrl) {
|
|
418
|
-
const sizeBytes = await getFileSize(result.videoUrl, proxy, config.userAgent, logger);
|
|
419
|
-
const maxBytes = config.Max_size !== undefined ? config.Max_size * 1024 * 1024 : undefined;
|
|
420
|
-
const formatMB = (bytes) => (bytes / (1024 * 1024)).toFixed(2);
|
|
421
|
-
if (sizeBytes === null) {
|
|
422
|
-
logger.warn(`无法获取视频大小: ${result.videoUrl},默认允许下载`);
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
const sizeMB = formatMB(sizeBytes);
|
|
426
|
-
if (maxBytes !== undefined && sizeBytes > maxBytes) {
|
|
427
|
-
videoExceedsLimit = true;
|
|
428
|
-
mediaVideoUrl = null;
|
|
429
|
-
const maxMB = config.Max_size.toFixed(2);
|
|
430
|
-
if (config.logLevel !== 'none') {
|
|
431
|
-
logger.info(`视频大小超限 (${sizeMB} MB > ${maxMB} MB): ${result.videoUrl}`);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
try {
|
|
436
|
-
mediaVideoUrl = await downloadAndMapUrl(result.videoUrl, proxy, config.userAgent, localDownloadDir, onebotReadDir, logger);
|
|
437
|
-
if (config.logLevel === 'full') {
|
|
438
|
-
logger.info(`视频已下载 (${sizeMB} MB): ${mediaVideoUrl}`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
catch (e) {
|
|
442
|
-
logger.warn('视频下载失败', e);
|
|
443
|
-
mediaVideoUrl = null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
416
|
// --- mainbody 图片 ---
|
|
449
417
|
if (result.mainbody) {
|
|
450
418
|
const imgUrls = [...result.mainbody.matchAll(/<img\s[^>]*src\s*=\s*["']?([^"'>\s]+)["']?/gi)].map(m => m[1]);
|
|
@@ -463,98 +431,130 @@ async function sendResult_forward(session, config, result, logger, mixed_sending
|
|
|
463
431
|
mediaMainbody = mediaMainbody.replace(new RegExp(escaped, 'g'), local);
|
|
464
432
|
}
|
|
465
433
|
}
|
|
466
|
-
// ===
|
|
434
|
+
// === 主消息(不含媒体文件)===
|
|
467
435
|
let message = config.format;
|
|
468
436
|
message = message.replace(/{title}/g, escapeHtml(result.title || ''));
|
|
469
437
|
message = message.replace(/{authorName}/g, escapeHtml(result.authorName || ''));
|
|
438
|
+
message = message.replace(/{mainbody}/g, mediaMainbody ?? '');
|
|
470
439
|
message = message.replace(/{sourceUrl}/g, escapeHtml(result.sourceUrl || ''));
|
|
440
|
+
message = message.replace(/{cover}/g, mediaCoverUrl ? koishi_1.h.image(mediaCoverUrl).toString() : '');
|
|
471
441
|
message = message.replace(/{stats}/g, escapeHtml(result.stats || ''));
|
|
472
|
-
// 处理 {videoUrl} 和 {video} 占位符逻辑(用于后续判断)
|
|
473
|
-
if (result.videoUrl) {
|
|
474
|
-
message = message.replace(/{videoUrl}/g, escapeHtml(result.videoUrl));
|
|
475
|
-
if (videoExceedsLimit) {
|
|
476
|
-
const tip = escapeHtml(config.Max_size_tip);
|
|
477
|
-
message = message.replace(/{video}/g, tip);
|
|
478
|
-
}
|
|
479
|
-
// 注意:这里不替换 {video} 为实际视频,留到转发节点构建时处理
|
|
480
|
-
}
|
|
481
|
-
const hasVideoInTemplate = message.includes('{video}');
|
|
482
|
-
const mediaMap = {};
|
|
483
|
-
if (mediaCoverUrl) {
|
|
484
|
-
mediaMap['{cover}'] = [{ type: 'image', data: { file: mediaCoverUrl } }];
|
|
485
|
-
}
|
|
486
|
-
else {
|
|
487
|
-
mediaMap['{cover}'] = [];
|
|
488
|
-
}
|
|
489
442
|
const lines = message.split('\n').filter(line => line.trim() !== '');
|
|
490
|
-
const
|
|
443
|
+
const mainSegments = [];
|
|
491
444
|
for (let i = 0; i < lines.length; i++) {
|
|
492
445
|
const line = lines[i];
|
|
493
446
|
const isLastLine = i === lines.length - 1;
|
|
494
|
-
const tokens = line.split(/(\{cover\}
|
|
447
|
+
const tokens = line.split(/(\{cover\})/g);
|
|
495
448
|
const currentLineSegments = [];
|
|
496
449
|
let hasTextContent = false;
|
|
497
450
|
for (const token of tokens) {
|
|
498
451
|
if (token === '{cover}') {
|
|
499
|
-
|
|
452
|
+
if (mediaCoverUrl) {
|
|
453
|
+
currentLineSegments.push({ type: 'image', data: { file: mediaCoverUrl } });
|
|
454
|
+
}
|
|
500
455
|
}
|
|
501
456
|
else if (token === '{mainbody}') {
|
|
502
457
|
const parsed = parseHtmlToSegments(mediaMainbody || '');
|
|
503
458
|
currentLineSegments.push(...parsed);
|
|
504
459
|
hasTextContent = parsed.some(seg => seg.type === 'text');
|
|
505
460
|
}
|
|
506
|
-
else if (token === '{video}') {
|
|
507
|
-
// 超限时替换为提示文本;否则留空(由转发节点处理)
|
|
508
|
-
if (videoExceedsLimit) {
|
|
509
|
-
const tip = config.Max_size_tip;
|
|
510
|
-
currentLineSegments.push({ type: 'text', data: { text: tip } });
|
|
511
|
-
hasTextContent = true;
|
|
512
|
-
}
|
|
513
|
-
// 否则不插入内容(视频将作为独立节点)
|
|
514
|
-
}
|
|
515
461
|
else if (token.trim() !== '') {
|
|
516
462
|
currentLineSegments.push({ type: 'text', data: { text: token } });
|
|
517
463
|
hasTextContent = true;
|
|
518
464
|
}
|
|
519
465
|
}
|
|
520
466
|
if (currentLineSegments.length > 0) {
|
|
521
|
-
|
|
467
|
+
mainSegments.push(...currentLineSegments);
|
|
522
468
|
}
|
|
523
469
|
if (!isLastLine && hasTextContent) {
|
|
524
|
-
|
|
470
|
+
mainSegments.push({ type: 'text', data: { text: '\n' } });
|
|
525
471
|
}
|
|
526
472
|
}
|
|
527
473
|
const forwardNodes = [];
|
|
528
|
-
if (
|
|
474
|
+
if (mainSegments.length > 0) {
|
|
529
475
|
forwardNodes.push({
|
|
530
476
|
type: 'node',
|
|
531
477
|
data: {
|
|
532
478
|
user_id: session.selfId,
|
|
533
479
|
nickname: '分享助手',
|
|
534
|
-
content:
|
|
480
|
+
content: mainSegments
|
|
535
481
|
}
|
|
536
482
|
});
|
|
537
483
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
484
|
+
// --- 处理 files 中的所有媒体 ---
|
|
485
|
+
const extraSendPromises = [];
|
|
486
|
+
if (config.sendFiles && Array.isArray(result.files)) {
|
|
487
|
+
for (const file of result.files) {
|
|
488
|
+
const { type, url: remoteUrl } = file;
|
|
489
|
+
if (!['video', 'audio', 'generic'].includes(type))
|
|
490
|
+
continue;
|
|
491
|
+
let shouldInclude = true;
|
|
492
|
+
if (config.Max_size !== undefined) {
|
|
493
|
+
const sizeBytes = await getFileSize(remoteUrl, proxy, config.userAgent, logger);
|
|
494
|
+
const maxBytes = config.Max_size * 1024 * 1024;
|
|
495
|
+
if (sizeBytes !== null && sizeBytes > maxBytes) {
|
|
496
|
+
shouldInclude = false;
|
|
497
|
+
if (config.logLevel !== 'none') {
|
|
498
|
+
const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(2);
|
|
499
|
+
const maxMB = config.Max_size.toFixed(2);
|
|
500
|
+
extraSendPromises.push(session.send(`文件大小超限 (${sizeMB} MB > ${maxMB} MB)`));
|
|
501
|
+
logger.info(`文件大小超限 (${sizeMB} MB > ${maxMB} MB),跳过: ${remoteUrl}`);
|
|
502
|
+
}
|
|
547
503
|
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
504
|
+
}
|
|
505
|
+
if (shouldInclude) {
|
|
506
|
+
try {
|
|
507
|
+
const localUrl = await downloadAndMapUrl(remoteUrl, proxy, config.userAgent, localDownloadDir, onebotReadDir, logger);
|
|
508
|
+
if (!localUrl)
|
|
509
|
+
continue;
|
|
510
|
+
if (!mixed_sending) {
|
|
511
|
+
// 作为转发节点发送
|
|
512
|
+
let segment = null;
|
|
513
|
+
if (type === 'video') {
|
|
514
|
+
segment = { type: 'video', data: { file: localUrl } };
|
|
515
|
+
}
|
|
516
|
+
else if (type === 'audio') {
|
|
517
|
+
segment = { type: 'audio', data: { file: localUrl } };
|
|
518
|
+
}
|
|
519
|
+
else if (type === 'generic') {
|
|
520
|
+
// 注意:标准 OneBot 转发节点不支持 file,这里降级为文本链接
|
|
521
|
+
segment = { type: 'text', data: { text: `📄 文件: ${remoteUrl}` } };
|
|
522
|
+
}
|
|
523
|
+
if (segment) {
|
|
524
|
+
forwardNodes.push({
|
|
525
|
+
type: 'node',
|
|
526
|
+
data: {
|
|
527
|
+
user_id: session.selfId,
|
|
528
|
+
nickname: '分享助手',
|
|
529
|
+
content: [segment]
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
// 混合模式:独立发送
|
|
536
|
+
let element = null;
|
|
537
|
+
if (type === 'video')
|
|
538
|
+
element = koishi_1.h.video(localUrl).toString();
|
|
539
|
+
else if (type === 'audio')
|
|
540
|
+
element = koishi_1.h.audio(localUrl).toString();
|
|
541
|
+
else if (type === 'generic')
|
|
542
|
+
element = koishi_1.h.file(localUrl).toString();
|
|
543
|
+
if (element) {
|
|
544
|
+
extraSendPromises.push(session.send(element));
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (config.logLevel === 'link_only') {
|
|
548
|
+
logger.info(`${type} 直链 (${result.platform}): ${remoteUrl}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
catch (e) {
|
|
552
|
+
logger.warn(`${type} 下载失败: ${remoteUrl}`, e);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
|
-
if (forwardNodes.length === 0)
|
|
557
|
+
if (forwardNodes.length === 0 && extraSendPromises.length === 0)
|
|
558
558
|
return;
|
|
559
559
|
if (config.logLevel === 'full') {
|
|
560
560
|
logger.info(`解析结果: \n ${JSON.stringify(result, null, 2)}`);
|
|
@@ -562,16 +562,18 @@ async function sendResult_forward(session, config, result, logger, mixed_sending
|
|
|
562
562
|
if (!(session.onebot && session.onebot._request))
|
|
563
563
|
throw new Error("Onebot is not defined");
|
|
564
564
|
const promises = [];
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
565
|
+
if (forwardNodes.length > 0) {
|
|
566
|
+
promises.push(session.onebot._request('send_group_forward_msg', {
|
|
567
|
+
group_id: session.guildId,
|
|
568
|
+
messages: forwardNodes,
|
|
569
|
+
news: [{ text: mediaMainbody || '-' }, { text: '点击查看详情 | Powered by furryaxw' }],
|
|
570
|
+
prompt: result.title || '',
|
|
571
|
+
summary: '分享解析',
|
|
572
|
+
source: result.title || ''
|
|
573
|
+
}));
|
|
574
|
+
}
|
|
575
|
+
if (mixed_sending && extraSendPromises.length > 0) {
|
|
576
|
+
promises.push(...extraSendPromises);
|
|
575
577
|
}
|
|
576
578
|
await Promise.all(promises);
|
|
577
579
|
}
|