koishi-plugin-share-links-analysis 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.js +4 -22
- package/lib/parsers/bilibili.js +2 -2
- package/lib/parsers/xiaoheihe.js +1 -1
- package/lib/parsers/xiaohongshu.js +3 -3
- package/lib/types.d.ts +0 -4
- package/lib/types.js +0 -1
- package/lib/utils.d.ts +13 -3
- package/lib/utils.js +73 -35
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -133,21 +133,6 @@ async function reportMetric(ctx, config, payload) {
|
|
|
133
133
|
}
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
-
async function sendResult(ctx, session, config, result, logger) {
|
|
137
|
-
if (!session.channel) {
|
|
138
|
-
return await (0, utils_1.sendResult_plain)(ctx, session, config, result, logger);
|
|
139
|
-
}
|
|
140
|
-
switch (config.useForward) {
|
|
141
|
-
case "plain":
|
|
142
|
-
return await (0, utils_1.sendResult_plain)(ctx, session, config, result, logger);
|
|
143
|
-
case 'forward':
|
|
144
|
-
return await (0, utils_1.sendResult_forward)(ctx, session, config, result, logger, false);
|
|
145
|
-
case "mixed":
|
|
146
|
-
return await (0, utils_1.sendResult_forward)(ctx, session, config, result, logger, true);
|
|
147
|
-
default:
|
|
148
|
-
return { downloadTime: 0, sendTime: 0 };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
136
|
function apply(ctx, config) {
|
|
152
137
|
// 数据库模型定义
|
|
153
138
|
ctx.model.extend('sla_cookie_cache', {
|
|
@@ -335,9 +320,8 @@ function apply(ctx, config) {
|
|
|
335
320
|
}
|
|
336
321
|
// === 性能统计变量 ===
|
|
337
322
|
const startTotal = Date.now();
|
|
323
|
+
const timeStats = { downloadTime: 0, sendTime: 0 };
|
|
338
324
|
let parseTime = 0;
|
|
339
|
-
let downloadTime = 0;
|
|
340
|
-
let sendTime = 0;
|
|
341
325
|
let isCache = false;
|
|
342
326
|
let status = "success";
|
|
343
327
|
let errorMsg = "";
|
|
@@ -417,9 +401,7 @@ function apply(ctx, config) {
|
|
|
417
401
|
// === 逻辑结束 ===
|
|
418
402
|
if (result) {
|
|
419
403
|
lastProcessedUrls[channelId][link.url] = Date.now();
|
|
420
|
-
|
|
421
|
-
downloadTime = stats.downloadTime;
|
|
422
|
-
sendTime = stats.sendTime;
|
|
404
|
+
await (0, utils_1.sendResult)(ctx, session, config, result, logger, timeStats);
|
|
423
405
|
}
|
|
424
406
|
else {
|
|
425
407
|
status = "failed";
|
|
@@ -465,8 +447,8 @@ function apply(ctx, config) {
|
|
|
465
447
|
// === Metrics (数值/指标) ===
|
|
466
448
|
time_total_ms: totalTime,
|
|
467
449
|
time_parse_ms: parseTime,
|
|
468
|
-
time_download_ms: downloadTime,
|
|
469
|
-
time_send_ms: sendTime,
|
|
450
|
+
time_download_ms: timeStats.downloadTime,
|
|
451
|
+
time_send_ms: timeStats.sendTime,
|
|
470
452
|
// 系统负载指标
|
|
471
453
|
memory_rss_mb: parseFloat(rssMB), // 当前进程内存占用
|
|
472
454
|
concurrent_tasks: pendingChecks.size, // 当前正在进行的并发解析数
|
package/lib/parsers/bilibili.js
CHANGED
|
@@ -8,11 +8,11 @@ const utils_1 = require("../utils");
|
|
|
8
8
|
exports.name = "bilibili";
|
|
9
9
|
const linkRules = [
|
|
10
10
|
{
|
|
11
|
-
pattern: /(?:https?:\/\/)?
|
|
11
|
+
pattern: /(?:https?:\/\/)?www\.bilibili\.com\/video\/([ab]v[0-9a-zA-Z]+)/gi,
|
|
12
12
|
type: "video",
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
|
-
pattern: /(?:https?:\/\/)?
|
|
15
|
+
pattern: /(?:https?:\/\/)?b23\.tv\/([0-9a-zA-Z]+)/gi,
|
|
16
16
|
type: "short",
|
|
17
17
|
},
|
|
18
18
|
];
|
package/lib/parsers/xiaoheihe.js
CHANGED
|
@@ -151,7 +151,7 @@ async function process(ctx, config, link, session) {
|
|
|
151
151
|
const mainContent = container.querySelector('.image-text__content')?.textContent?.trim() || '';
|
|
152
152
|
contentBlocks.push({ type: 'text', content: mainContent });
|
|
153
153
|
// 图片(轮播图)
|
|
154
|
-
container.querySelectorAll('.header-image__item-image img').forEach((img
|
|
154
|
+
container.querySelectorAll('.header-image__item-image img').forEach((img) => {
|
|
155
155
|
const src = img.src.replace(/\?.*$/, '');
|
|
156
156
|
contentBlocks.push({ type: 'image', content: src });
|
|
157
157
|
});
|
|
@@ -11,15 +11,15 @@ const utils_1 = require("../utils");
|
|
|
11
11
|
exports.name = "xiaohongshu";
|
|
12
12
|
const linkRules = [
|
|
13
13
|
{
|
|
14
|
-
pattern: /(?:https?:\/\/)?
|
|
14
|
+
pattern: /(?:https?:\/\/)?www\.xiaohongshu\.com\/discovery\/item\/([\w?=&\-.%]+)/gi,
|
|
15
15
|
type: "discovery",
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
pattern: /(?:https?:\/\/)?
|
|
18
|
+
pattern: /(?:https?:\/\/)?www\.xiaohongshu\.com\/explore\/([\w?=&\-.%]+)/gi,
|
|
19
19
|
type: "explore",
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
pattern: /(?:https?:\/\/)?
|
|
22
|
+
pattern: /(?:https?:\/\/)?xhslink\.com\/(?:\w\/)?([0-9a-zA-Z]+)/gi,
|
|
23
23
|
type: "short",
|
|
24
24
|
},
|
|
25
25
|
];
|
package/lib/types.d.ts
CHANGED
package/lib/types.js
CHANGED
package/lib/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ParsedInfo, PluginConfig
|
|
1
|
+
import { ParsedInfo, PluginConfig } from './types';
|
|
2
2
|
import { Context, Logger, Session } from "koishi";
|
|
3
3
|
import { Agent as HttpAgent } from 'http';
|
|
4
4
|
import { Agent as HttpsAgent } from 'https';
|
|
@@ -19,5 +19,15 @@ export declare function getEffectiveSettings(ctx: Context, guildId: string | und
|
|
|
19
19
|
nsfw: boolean;
|
|
20
20
|
}>;
|
|
21
21
|
export declare function isUserAdmin(session: Session, userId: string): Promise<boolean>;
|
|
22
|
-
export declare function
|
|
23
|
-
|
|
22
|
+
export declare function sendResult(ctx: Context, session: Session, config: PluginConfig, result: ParsedInfo, logger: Logger, statsRef: {
|
|
23
|
+
downloadTime: number;
|
|
24
|
+
sendTime: number;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
export declare function sendResult_plain(ctx: Context, session: Session, config: PluginConfig, result: ParsedInfo, logger: Logger, statsRef: {
|
|
27
|
+
downloadTime: number;
|
|
28
|
+
sendTime: number;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
export declare function sendResult_forward(ctx: Context, session: Session, config: PluginConfig, result: ParsedInfo, logger: Logger, mixed_sending: boolean | undefined, statsRef: {
|
|
31
|
+
downloadTime: number;
|
|
32
|
+
sendTime: number;
|
|
33
|
+
}): Promise<void>;
|
package/lib/utils.js
CHANGED
|
@@ -43,6 +43,7 @@ exports.getProxyAgent = getProxyAgent;
|
|
|
43
43
|
exports.getFileSize = getFileSize;
|
|
44
44
|
exports.getEffectiveSettings = getEffectiveSettings;
|
|
45
45
|
exports.isUserAdmin = isUserAdmin;
|
|
46
|
+
exports.sendResult = sendResult;
|
|
46
47
|
exports.sendResult_plain = sendResult_plain;
|
|
47
48
|
exports.sendResult_forward = sendResult_forward;
|
|
48
49
|
const koishi_1 = require("koishi");
|
|
@@ -255,14 +256,15 @@ async function tryHeadRequest(url, proxy, userAgent, logger) {
|
|
|
255
256
|
const u = new url_1.URL(url);
|
|
256
257
|
const agent = getProxyAgent(proxy, url);
|
|
257
258
|
const getter = u.protocol === 'https:' ? require('https').get : require('http').get;
|
|
259
|
+
const headers = { 'User-Agent': userAgent };
|
|
260
|
+
if (u.hostname.includes('bilibili.com')) {
|
|
261
|
+
headers['Referer'] = 'https://www.bilibili.com/';
|
|
262
|
+
}
|
|
258
263
|
const req = getter(url, {
|
|
259
264
|
agent,
|
|
260
265
|
method: 'HEAD',
|
|
261
|
-
timeout:
|
|
262
|
-
headers:
|
|
263
|
-
'User-Agent': userAgent,
|
|
264
|
-
'Referer': 'https://www.bilibili.com/'
|
|
265
|
-
}
|
|
266
|
+
timeout: 5000,
|
|
267
|
+
headers: headers
|
|
266
268
|
}, (res) => {
|
|
267
269
|
const len = res.headers['content-length'];
|
|
268
270
|
if (len && /^\d+$/.test(len)) {
|
|
@@ -286,18 +288,21 @@ async function tryHeadRequest(url, proxy, userAgent, logger) {
|
|
|
286
288
|
});
|
|
287
289
|
}
|
|
288
290
|
async function tryGetRequestForSize(url, proxy, userAgent, logger) {
|
|
289
|
-
proxy = undefined;
|
|
290
291
|
return new Promise((resolve) => {
|
|
291
292
|
const u = new url_1.URL(url);
|
|
292
293
|
const agent = getProxyAgent(proxy, url);
|
|
293
294
|
const getter = u.protocol === 'https:' ? require('https').get : require('http').get;
|
|
295
|
+
const headers = {
|
|
296
|
+
'User-Agent': userAgent,
|
|
297
|
+
'Range': 'bytes=0-1023'
|
|
298
|
+
};
|
|
299
|
+
if (u.hostname.includes('bilibili.com')) {
|
|
300
|
+
headers['Referer'] = 'https://www.bilibili.com/';
|
|
301
|
+
}
|
|
294
302
|
const req = getter(url, {
|
|
295
303
|
agent,
|
|
296
|
-
timeout:
|
|
297
|
-
headers:
|
|
298
|
-
'User-Agent': userAgent,
|
|
299
|
-
'Range': 'bytes=0-1023' // 只请求前1KB
|
|
300
|
-
}
|
|
304
|
+
timeout: 5000,
|
|
305
|
+
headers: headers
|
|
301
306
|
}, (res) => {
|
|
302
307
|
const contentRange = res.headers['content-range'];
|
|
303
308
|
if (contentRange) {
|
|
@@ -372,10 +377,27 @@ async function isUserAdmin(session, userId) {
|
|
|
372
377
|
return true;
|
|
373
378
|
}
|
|
374
379
|
}
|
|
375
|
-
async function
|
|
380
|
+
async function sendResult(ctx, session, config, result, logger, statsRef // 新增参数
|
|
381
|
+
) {
|
|
382
|
+
if (!session.channel) {
|
|
383
|
+
await sendResult_plain(ctx, session, config, result, logger, statsRef);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
switch (config.useForward) {
|
|
387
|
+
case "plain":
|
|
388
|
+
await sendResult_plain(ctx, session, config, result, logger, statsRef);
|
|
389
|
+
break;
|
|
390
|
+
case 'forward':
|
|
391
|
+
await sendResult_forward(ctx, session, config, result, logger, false, statsRef);
|
|
392
|
+
break;
|
|
393
|
+
case "mixed":
|
|
394
|
+
await sendResult_forward(ctx, session, config, result, logger, true, statsRef);
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// 2. 修改 sendResult_plain
|
|
399
|
+
async function sendResult_plain(ctx, session, config, result, logger, statsRef) {
|
|
376
400
|
logger.debug('进入普通发送');
|
|
377
|
-
let downloadTime = 0; // 下载耗时计时
|
|
378
|
-
let sendTime = 0; // 发送耗时计时
|
|
379
401
|
const localDownloadDir = config.localDownloadDir;
|
|
380
402
|
const onebotReadDir = config.onebotReadDir;
|
|
381
403
|
let mediaCoverUrl = result.coverUrl;
|
|
@@ -397,7 +419,7 @@ async function sendResult_plain(ctx, session, config, result, logger) {
|
|
|
397
419
|
logger.warn(`封面下载失败: ${result.coverUrl}`, e);
|
|
398
420
|
mediaCoverUrl = result.coverUrl;
|
|
399
421
|
}
|
|
400
|
-
downloadTime += Date.now() - t; // 累加耗时
|
|
422
|
+
statsRef.downloadTime += Date.now() - t; // 累加耗时
|
|
401
423
|
}
|
|
402
424
|
else {
|
|
403
425
|
mediaCoverUrl = result.coverUrl;
|
|
@@ -424,7 +446,7 @@ async function sendResult_plain(ctx, session, config, result, logger) {
|
|
|
424
446
|
urlMap[remoteUrl] = remoteUrl;
|
|
425
447
|
}
|
|
426
448
|
}));
|
|
427
|
-
downloadTime += Date.now() - t; // 累加耗时
|
|
449
|
+
statsRef.downloadTime += Date.now() - t; // 累加耗时
|
|
428
450
|
mediaMainbody = result.mainbody;
|
|
429
451
|
for (const [remote, local] of Object.entries(urlMap)) {
|
|
430
452
|
const escaped = remote.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -457,15 +479,19 @@ async function sendResult_plain(ctx, session, config, result, logger) {
|
|
|
457
479
|
if (config.Max_size !== undefined) {
|
|
458
480
|
const t = Date.now();
|
|
459
481
|
const sizeBytes = await getFileSize(remoteUrl, proxy, config.userAgent, logger);
|
|
460
|
-
downloadTime += Date.now() - t;
|
|
482
|
+
statsRef.downloadTime += Date.now() - t;
|
|
461
483
|
const maxBytes = config.Max_size * 1024 * 1024;
|
|
462
|
-
if (sizeBytes
|
|
484
|
+
if (sizeBytes === null) {
|
|
485
|
+
shouldSend = false;
|
|
486
|
+
sendPromises.push(session.send(`无法获取文件大小,已跳过发送`));
|
|
487
|
+
logger.info(`获取文件大小失败,放弃发送: ${remoteUrl}`);
|
|
488
|
+
}
|
|
489
|
+
else if (sizeBytes > maxBytes) {
|
|
463
490
|
shouldSend = false;
|
|
464
491
|
const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(2);
|
|
465
492
|
const maxMB = config.Max_size.toFixed(2);
|
|
466
493
|
sendPromises.push(session.send(`文件大小超限 (${sizeMB} MB > ${maxMB} MB)`));
|
|
467
494
|
logger.info(`文件大小超限 (${sizeMB} MB > ${maxMB} MB),跳过: ${remoteUrl}`);
|
|
468
|
-
sendPromises.push(session.send(`文件大小超限...`));
|
|
469
495
|
}
|
|
470
496
|
}
|
|
471
497
|
if (shouldSend) {
|
|
@@ -474,7 +500,7 @@ async function sendResult_plain(ctx, session, config, result, logger) {
|
|
|
474
500
|
if (config.usingLocal) {
|
|
475
501
|
const t = Date.now();
|
|
476
502
|
localUrl = await downloadAndMapUrl(ctx, remoteUrl, proxy, config.userAgent, localDownloadDir, onebotReadDir, logger, config.enableCache);
|
|
477
|
-
downloadTime += Date.now() - t;
|
|
503
|
+
statsRef.downloadTime += Date.now() - t;
|
|
478
504
|
}
|
|
479
505
|
if (!localUrl)
|
|
480
506
|
continue;
|
|
@@ -506,13 +532,11 @@ async function sendResult_plain(ctx, session, config, result, logger) {
|
|
|
506
532
|
}
|
|
507
533
|
const tSend = Date.now(); // 发送计时开始
|
|
508
534
|
await Promise.all(sendPromises);
|
|
509
|
-
sendTime = Date.now() - tSend; // 计算发送耗时
|
|
510
|
-
return { downloadTime, sendTime }; // 返回统计
|
|
535
|
+
statsRef.sendTime = Date.now() - tSend; // 计算发送耗时
|
|
511
536
|
}
|
|
512
|
-
async function sendResult_forward(ctx, session, config, result, logger, mixed_sending = false
|
|
537
|
+
async function sendResult_forward(ctx, session, config, result, logger, mixed_sending = false, statsRef // 接收引用
|
|
538
|
+
) {
|
|
513
539
|
logger.debug(mixed_sending ? '进入混合发送' : '进入合并发送');
|
|
514
|
-
let downloadTime = 0;
|
|
515
|
-
let sendTime = 0;
|
|
516
540
|
const localDownloadDir = config.localDownloadDir;
|
|
517
541
|
const onebotReadDir = config.onebotReadDir;
|
|
518
542
|
let mediaCoverUrl = result.coverUrl;
|
|
@@ -533,7 +557,7 @@ async function sendResult_forward(ctx, session, config, result, logger, mixed_se
|
|
|
533
557
|
logger.warn('封面下载失败', e);
|
|
534
558
|
mediaCoverUrl = '';
|
|
535
559
|
}
|
|
536
|
-
downloadTime += Date.now() - t;
|
|
560
|
+
statsRef.downloadTime += Date.now() - t;
|
|
537
561
|
}
|
|
538
562
|
else {
|
|
539
563
|
mediaCoverUrl = result.coverUrl;
|
|
@@ -552,7 +576,7 @@ async function sendResult_forward(ctx, session, config, result, logger, mixed_se
|
|
|
552
576
|
catch (e) {
|
|
553
577
|
logger.warn(`正文图片下载失败: ${url}`, e);
|
|
554
578
|
}
|
|
555
|
-
downloadTime += Date.now() - t;
|
|
579
|
+
statsRef.downloadTime += Date.now() - t;
|
|
556
580
|
}
|
|
557
581
|
else {
|
|
558
582
|
urlMap[url] = url;
|
|
@@ -623,9 +647,25 @@ async function sendResult_forward(ctx, session, config, result, logger, mixed_se
|
|
|
623
647
|
if (config.Max_size !== undefined) {
|
|
624
648
|
const t = Date.now();
|
|
625
649
|
const sizeBytes = await getFileSize(remoteUrl, proxy, config.userAgent, logger);
|
|
626
|
-
downloadTime += Date.now() - t;
|
|
650
|
+
statsRef.downloadTime += Date.now() - t;
|
|
627
651
|
const maxBytes = config.Max_size * 1024 * 1024;
|
|
628
|
-
if (sizeBytes
|
|
652
|
+
if (sizeBytes === null) {
|
|
653
|
+
shouldInclude = false;
|
|
654
|
+
logger.warn(`获取文件大小失败,放弃发送: ${remoteUrl}`);
|
|
655
|
+
forwardNodes.push({
|
|
656
|
+
type: 'node',
|
|
657
|
+
data: {
|
|
658
|
+
user_id: session.selfId,
|
|
659
|
+
nickname: '分享助手',
|
|
660
|
+
content: {
|
|
661
|
+
type: 'text', data: {
|
|
662
|
+
text: `无法获取文件大小,已跳过发送`
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
else if (sizeBytes > maxBytes) {
|
|
629
669
|
shouldInclude = false;
|
|
630
670
|
const sizeMB = (sizeBytes / (1024 * 1024)).toFixed(2);
|
|
631
671
|
const maxMB = config.Max_size.toFixed(2);
|
|
@@ -650,7 +690,7 @@ async function sendResult_forward(ctx, session, config, result, logger, mixed_se
|
|
|
650
690
|
if (config.usingLocal) {
|
|
651
691
|
const t = Date.now();
|
|
652
692
|
localUrl = await downloadAndMapUrl(ctx, remoteUrl, proxy, config.userAgent, localDownloadDir, onebotReadDir, logger, config.enableCache);
|
|
653
|
-
downloadTime += Date.now() - t;
|
|
693
|
+
statsRef.downloadTime += Date.now() - t;
|
|
654
694
|
}
|
|
655
695
|
if (!localUrl)
|
|
656
696
|
continue;
|
|
@@ -712,9 +752,8 @@ async function sendResult_forward(ctx, session, config, result, logger, mixed_se
|
|
|
712
752
|
});
|
|
713
753
|
}
|
|
714
754
|
}
|
|
715
|
-
if (forwardNodes.length === 0 && extraSendPromises.length === 0)
|
|
716
|
-
return
|
|
717
|
-
}
|
|
755
|
+
if (forwardNodes.length === 0 && extraSendPromises.length === 0)
|
|
756
|
+
return;
|
|
718
757
|
logger.debug(`解析结果: \n ${JSON.stringify(result, null, 2)}`);
|
|
719
758
|
if (!(session.onebot && session.onebot._request))
|
|
720
759
|
throw new Error("Onebot is not defined");
|
|
@@ -736,7 +775,6 @@ async function sendResult_forward(ctx, session, config, result, logger, mixed_se
|
|
|
736
775
|
if (promises.length > 0) {
|
|
737
776
|
const tSend = Date.now();
|
|
738
777
|
await Promise.all(promises);
|
|
739
|
-
sendTime = Date.now() - tSend;
|
|
778
|
+
statsRef.sendTime = Date.now() - tSend;
|
|
740
779
|
}
|
|
741
|
-
return { downloadTime, sendTime };
|
|
742
780
|
}
|