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 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
- const stats = await sendResult(ctx, session, config, result, logger);
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, // 当前正在进行的并发解析数
@@ -8,11 +8,11 @@ const utils_1 = require("../utils");
8
8
  exports.name = "bilibili";
9
9
  const linkRules = [
10
10
  {
11
- pattern: /(?:https?:\/\/)?(?:www\.bilibili\.com\/video\/)(([ab]v[0-9a-zA-Z]+))/gi,
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?:\/\/)?(?:b23\.tv\/([0-9a-zA-Z]+))/gi,
15
+ pattern: /(?:https?:\/\/)?b23\.tv\/([0-9a-zA-Z]+)/gi,
16
16
  type: "short",
17
17
  },
18
18
  ];
@@ -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, index) => {
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?:\/\/)?(?:www\.xiaohongshu\.com\/discovery\/item\/)([\w?=&\-.%]+)/gi,
14
+ pattern: /(?:https?:\/\/)?www\.xiaohongshu\.com\/discovery\/item\/([\w?=&\-.%]+)/gi,
15
15
  type: "discovery",
16
16
  },
17
17
  {
18
- pattern: /(?:https?:\/\/)?(?:www\.xiaohongshu\.com\/explore\/)([\w?=&\-.%]+)/gi,
18
+ pattern: /(?:https?:\/\/)?www\.xiaohongshu\.com\/explore\/([\w?=&\-.%]+)/gi,
19
19
  type: "explore",
20
20
  },
21
21
  {
22
- pattern: /(?:https?:\/\/)?(?:xhslink\.com\/(?:\w\/)?)([0-9a-zA-Z]+)/gi,
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
@@ -46,10 +46,6 @@ export interface PluginConfig {
46
46
  reportEnabled: boolean;
47
47
  reportUrl: string;
48
48
  }
49
- export interface SendResultStats {
50
- downloadTime: number;
51
- sendTime: number;
52
- }
53
49
  export interface BilibiliVideoInfo {
54
50
  data: {
55
51
  bvid: string;
package/lib/types.js CHANGED
@@ -1,4 +1,3 @@
1
1
  "use strict";
2
2
  // src/types.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- let session_global;
package/lib/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ParsedInfo, PluginConfig, SendResultStats } from './types';
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 sendResult_plain(ctx: Context, session: Session, config: PluginConfig, result: ParsedInfo, logger: Logger): Promise<SendResultStats>;
23
- export declare function sendResult_forward(ctx: Context, session: Session, config: PluginConfig, result: ParsedInfo, logger: Logger, mixed_sending?: boolean): Promise<SendResultStats>;
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: 10000,
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: 15000,
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 sendResult_plain(ctx, session, config, result, logger) {
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 !== null && sizeBytes > maxBytes) {
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 !== null && sizeBytes > maxBytes) {
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 { downloadTime, sendTime };
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
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "koishi-plugin-share-links-analysis",
3
3
  "description": "自用插件",
4
4
  "license": "MIT",
5
- "version": "0.8.2",
5
+ "version": "0.8.4",
6
6
  "main": "lib/index.js",
7
7
  "typings": "lib/index.d.ts",
8
8
  "files": [