yz-yuki-plugin 2.0.7-13 → 2.0.7-15

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/CHANGELOG.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # 2.0.7
2
+ * 优化动态检查日志
3
+ * 优化文字动态内容及排版,修复微博视频图片混合的动态显示缺失问题
4
+ * 优化获取B站文章动态内容
2
5
  * 优化订阅数据展示
3
6
  * 修复同一up订阅多个群聊订阅,推送类型合并的问题
4
7
  * 添加白名单关键词过滤功能
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # YUKI-PLUGIN
4
4
 
5
- - 一个适用于 `Yunzai 系列机器人框架` 的B站动态、B站视频链接解析和微博动态订阅推送的插件
5
+ - 一个适用于 `Yunzai 系列机器人框架` 的B站动态、微博动态订阅推送和B站视频链接解析的插件
6
6
 
7
7
  - 支持 群聊/私聊 订阅B站动态和微博动态,支持定时推送,支持手动触发推送,支持简单查询B站/微博用户信息。
8
8
 
@@ -36,7 +36,27 @@ chromium-browser --version
36
36
  ## 选择安装方式
37
37
  按照网络情况以及使用的bot框架是`Yunzaijs`还是`Yunzai-V3`,选择对应的安装方式。
38
38
 
39
- ### ***(一)YunzaiJS***
39
+ ### ***(一)Yunzai-V3***
40
+
41
+ > 仅支持Yunzai-V3(TRSS/Miao)的分支,选择仓库,安装到 `yunzai/plugins`:
42
+
43
+ gitee仓库:
44
+ ```
45
+ git clone --branch main3 https://gitee.com/snowtafir/yuki-plugin.git ./plugins/yuki-plugin
46
+ ```
47
+
48
+ github仓库:
49
+ ```
50
+ git clone --branch main3 https://github.com/snowtafir/yuki-plugin.git ./plugins/yuki-plugin
51
+ ```
52
+
53
+ * 安装依赖
54
+
55
+ ```shell
56
+ pnpm install --filter=yuki-plugin
57
+ ```
58
+
59
+ ### ***(二)YunzaiJS***
40
60
  > 选择其中一种方式安装插件:
41
61
 
42
62
  1. npm包安装到`yunzaijs/node_modules`的方式,仅YunzaiJS支持:
@@ -86,26 +106,6 @@ git clone --branch main https://github.com/snowtafir/yuki-plugin.git ./plugins/y
86
106
  yarn install
87
107
  ```
88
108
 
89
- ### ***(二)Yunzai-V3***
90
-
91
- > 仅支持Yunzai-V3(TRSS/Miao)的分支,选择仓库,安装到 `yunzai/plugins`:
92
-
93
- gitee仓库:
94
- ```
95
- git clone --branch main3 https://gitee.com/snowtafir/yuki-plugin.git ./plugins/yuki-plugin
96
- ```
97
-
98
- github仓库:
99
- ```
100
- git clone --branch main3 https://github.com/snowtafir/yuki-plugin.git ./plugins/yuki-plugin
101
- ```
102
-
103
- * 安装依赖
104
-
105
- ```shell
106
- pnpm install --filter=yuki-plugin
107
- ```
108
-
109
109
  # 📦二、插件配置
110
110
 
111
111
  > [!IMPORTANT]
@@ -255,3 +255,4 @@ yarn install
255
255
  | [TRSS-Yunzai](https://gitee.com/TimeRainStarSky/Yunzai) | 时雨🌌星空的 TRSS-Yunzai |
256
256
  | [Miao-Yunzai](https://gitee.com/yoimiya-kokomi/Miao-Yunzai) | 喵喵的 Miao-Yunzai |
257
257
  | [Yunzai-Bot](https://gitee.com/Le-niao/Yunzai-Bot) | 乐神的 Yunzai-Bot |
258
+ |[jsxp](https://github.com/lemonade-lab/lvyjs/tree/main/packages/jsxp) | 一个可以在 tsx 环境中,使用 puppeteer 对 tsx 组件进行截图的库 |
@@ -383,17 +383,19 @@ message.use(async (e) => {
383
383
  messages.push('\n>>>>>>群组B站订阅<<<<<<');
384
384
  Object.keys(subData.group).forEach(groupId => {
385
385
  messages.push(`\n<群组${groupId}>:`);
386
- subData.group[groupId].forEach((item) => {
387
- const types = new Set();
388
- if (item.type && item.type.length) {
389
- item.type.forEach((typeItem) => {
390
- if (typeMap[typeItem]) {
391
- types.add(typeMap[typeItem]);
392
- }
393
- });
394
- }
395
- messages.push(`${item.uid}:${item.name} ${types.size ? `[${Array.from(types).join('、')}]` : ' [全部动态]'}`);
396
- });
386
+ if (subData.group) {
387
+ subData.group[groupId].forEach((item) => {
388
+ const types = new Set();
389
+ if (item.type && item.type.length) {
390
+ item.type.forEach((typeItem) => {
391
+ if (typeMap[typeItem]) {
392
+ types.add(typeMap[typeItem]);
393
+ }
394
+ });
395
+ }
396
+ messages.push(`${item.uid}:${item.name} ${types.size ? `[${Array.from(types).join('、')}]` : ' [全部动态]'}`);
397
+ });
398
+ }
397
399
  });
398
400
  }
399
401
  else {
@@ -404,17 +406,19 @@ message.use(async (e) => {
404
406
  messages.push('\n>>>>>>私聊B站订阅<<<<<<');
405
407
  Object.keys(subData.private).forEach(userId => {
406
408
  messages.push(`\n<用户${userId}>:`);
407
- subData.private[userId].forEach((item) => {
408
- const types = new Set();
409
- if (item.type && item.type.length) {
410
- item.type.forEach((typeItem) => {
411
- if (typeMap[typeItem]) {
412
- types.add(typeMap[typeItem]);
413
- }
414
- });
415
- }
416
- messages.push(`${item.uid}:${item.name} ${types.size ? `[${Array.from(types).join('、')}]` : ' [全部动态]'}`);
417
- });
409
+ if (subData.private) {
410
+ subData.private[userId].forEach((item) => {
411
+ const types = new Set();
412
+ if (item.type && item.type.length) {
413
+ item.type.forEach((typeItem) => {
414
+ if (typeMap[typeItem]) {
415
+ types.add(typeMap[typeItem]);
416
+ }
417
+ });
418
+ }
419
+ messages.push(`${item.uid}:${item.name} ${types.size ? `[${Array.from(types).join('、')}]` : ' [全部动态]'}`);
420
+ });
421
+ }
418
422
  });
419
423
  }
420
424
  else {
@@ -486,9 +490,9 @@ message.use(async (e) => {
486
490
  e.reply('用户不存在,输入的uid无效。');
487
491
  return;
488
492
  }
489
- const message = [`昵称:${data?.name}`, `\n性别:${data?.sex}`, `\n等级:${data?.level}`];
493
+ const message = [`--------------------`, `\n昵称:${data?.name}`, `\n性别:${data?.sex}`, `\n等级:${data?.level}`, `\n--------------------`];
490
494
  if (data.live_room) {
491
- message.push(`\n***********\n---直播信息---`, `\n直播标题:${data?.live_room?.title}`, `\n直播房间:${data?.live_room?.roomid}`, `\n直播状态:${data?.live_room?.liveStatus ? '直播中' : '未开播'}`, `\n观看人数:${data?.live_room?.watched_show?.num}人`);
495
+ message.push(`\n>>>>>直播间信息<<<<<`, `\n标题:${data?.live_room?.title}`, `\n房间:${data?.live_room?.roomid}`, `\n状态:${data?.live_room?.liveStatus ? '直播中' : '未开播'}`, `\n观看人数:${data?.live_room?.watched_show?.num}`);
492
496
  e.reply(`直播链接:${data?.live_room?.url}`);
493
497
  }
494
498
  e.reply(message);
@@ -83,9 +83,9 @@ const Content = ({ data }) => {
83
83
  contentCss,
84
84
  boxGrid,
85
85
  React.createElement("div", { className: "content" },
86
- picItems,
87
86
  React.createElement("div", { className: "content-text-title", style: { marginBottom: '10px' } }, data.title && React.createElement("h1", null, data.title)),
88
- React.createElement("div", { className: "content-text", dangerouslySetInnerHTML: { __html: data.content || '' } }))));
87
+ React.createElement("div", { className: "content-text", dangerouslySetInnerHTML: { __html: data.content || '' } }),
88
+ picItems)));
89
89
  case 'DYNAMIC_TYPE_WORD':
90
90
  return (React.createElement(React.Fragment, null,
91
91
  contentCss,
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { BOT_NAME, ConfigController } from 'yunzaijs';
2
+ import { ConfigController, BOT_NAME } from 'yunzaijs';
3
3
  import Config from '../../utils/config.js';
4
4
  import path from 'path';
5
- import { createRequire, _paths } from '../../utils/paths.js';
5
+ import { _paths, createRequire } from '../../utils/paths.js';
6
6
 
7
7
  // Footer.tsx
8
8
  const require = createRequire(import.meta.url);
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { BOT_NAME, ConfigController } from 'yunzaijs';
2
+ import { ConfigController, BOT_NAME } from 'yunzaijs';
3
3
  import Config from '../../utils/config.js';
4
4
  import path from 'path';
5
- import { createRequire, _paths } from '../../utils/paths.js';
5
+ import { _paths, createRequire } from '../../utils/paths.js';
6
6
 
7
7
  //help.tsx
8
8
  const require = createRequire(import.meta.url);
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { BOT_NAME, ConfigController } from 'yunzaijs';
2
+ import { ConfigController, BOT_NAME } from 'yunzaijs';
3
3
  import Config from '../../utils/config.js';
4
4
  import path from 'path';
5
- import { createRequire, _paths } from '../../utils/paths.js';
5
+ import { _paths, createRequire } from '../../utils/paths.js';
6
6
 
7
7
  const require = createRequire(import.meta.url);
8
8
  const botVersion = ConfigController.package?.version;
@@ -102,42 +102,28 @@ class BiliQuery {
102
102
  formatData.data.title = desc?.title;
103
103
  additional = data?.modules?.module_dynamic?.additional;
104
104
  // 文章内容过长,则尝试获取全文
105
+ let content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text, additional) || '';
105
106
  if (String(desc?.summary?.text).length >= 480) {
106
107
  const fullArticleContent = await this.getFullArticleContent(this.formatUrl(desc?.jump_url));
107
108
  if (fullArticleContent) {
108
109
  const { readInfo, articleType } = fullArticleContent;
109
- // 文章链接类型为 cv(旧类型) 或者 opus(新类型)
110
- if (articleType === 'cv') {
111
- formatData.data.content = this.praseFullOldTypeArticleContent(readInfo?.content);
112
- if (String(formatData.data.content).length < 100) {
113
- formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text, additional) || '';
114
- formatData.data.pics = pics;
115
- }
116
- else {
117
- formatData.data.pics = [];
118
- }
119
- }
120
- else if (articleType === 'opus') {
121
- const FullNewTypeArticleContent = this.praseFullNewTypeArticleContent(readInfo?.paragraphs);
122
- if (FullNewTypeArticleContent) {
123
- const { content, img } = FullNewTypeArticleContent;
124
- formatData.data.content = content;
125
- formatData.data.pics = img && img.length > 0 ? img : pics;
126
- if (content && content.length < 100) {
127
- formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text, additional);
128
- }
110
+ // 文章类型为 cv(旧类型) 或者 opus(新类型)
111
+ if (articleType === 'opus') {
112
+ const newTypeContent = this.praseFullNewTypeArticleContent(readInfo?.paragraphs);
113
+ if (newTypeContent) {
114
+ content = newTypeContent.content || content;
115
+ pics = newTypeContent.img && newTypeContent.img.length > 0 ? newTypeContent.img : pics;
129
116
  }
130
117
  }
131
- else {
132
- formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text, additional) || '';
133
- formatData.data.pics = pics;
118
+ else if (articleType === 'cv') {
119
+ const oldTypeContent = this.praseFullOldTypeArticleContent(readInfo?.content);
120
+ content = oldTypeContent || content;
134
121
  }
122
+ content = String(content).length < 100 ? this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text, additional) : content;
135
123
  }
136
124
  }
137
- else {
138
- formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text, additional) || '';
139
- formatData.data.pics = pics;
140
- }
125
+ formatData.data.content = content;
126
+ formatData.data.pics = pics;
141
127
  }
142
128
  else if (majorType === 'MAJOR_TYPE_ARTICLE') {
143
129
  desc = data?.modules?.module_dynamic?.major?.article || {};
@@ -251,7 +237,7 @@ class BiliQuery {
251
237
  };
252
238
  /**获取完整B站文章内容
253
239
  * @param postUrl - 文章链接: https://www.bilibili.com/read/cvxxxx 或者 https://www.bilibili.com/opus/xxxx
254
- * @returns {JSON} 完整的B站文章内容json数据
240
+ * @returns 完整的B站文章内容json数据
255
241
  */
256
242
  static async getFullArticleContent(postUrl) {
257
243
  let { cookie } = await readSyncCookie();
@@ -262,24 +248,22 @@ class BiliQuery {
262
248
  responseType: 'text'
263
249
  });
264
250
  const text = response.data;
265
- let match, readInfo, articleType;
266
- if (/^https:\/\/www.bilibili.com\/read\/cv/.test(postUrl)) {
267
- match = String(text).match(/"readInfo":([\s\S]+?),"readViewInfo":/);
268
- if (match) {
269
- const full_json_text = match[1];
270
- readInfo = JSON.parse(full_json_text);
271
- articleType = 'cv';
272
- return { readInfo, articleType };
273
- }
251
+ let matchCV, matchOPUS, readInfo, articleType;
252
+ matchCV = String(text).match(/"readInfo":([\s\S]+?),"readViewInfo":/);
253
+ matchOPUS = String(text).match(/"module_content"\s*:\s*([\s\S]+?)\s*,\s*"module_type"\s*:\s*"MODULE_TYPE_CONTENT"/);
254
+ if (matchOPUS) {
255
+ logger.info(`文章内容新`);
256
+ const full_json_text = matchOPUS[1];
257
+ readInfo = JSON.parse(full_json_text);
258
+ articleType = 'opus';
259
+ return { readInfo, articleType };
274
260
  }
275
- else if (/^https:\/\/www.bilibili.com\/opus\//.test(postUrl)) {
276
- match = String(text).match(/"module_content":([\s\S]+?),\s*"module_type":"MODULE_TYPE_CONTENT"/);
277
- if (match) {
278
- const full_json_text = match[1];
279
- readInfo = JSON.parse(full_json_text);
280
- articleType = 'opus';
281
- return { readInfo, articleType };
282
- }
261
+ else if (matchCV) {
262
+ logger.info(`文章内容旧`);
263
+ const full_json_text = matchCV[1];
264
+ readInfo = JSON.parse(full_json_text);
265
+ articleType = 'cv';
266
+ return { readInfo, articleType };
283
267
  }
284
268
  }
285
269
  catch (err) {
@@ -399,9 +383,15 @@ class BiliQuery {
399
383
  */
400
384
  static async formatTextDynamicData(upName, data, isForward, setData) {
401
385
  const BiliDrawDynamicLinkUrl = 'https://m.bilibili.com/dynamic/';
402
- let desc, msg = [], pics = [], author, majorType, content, dynamicTitle;
403
- let title = `B站【${upName}】动态推送:\n`;
386
+ let desc, msg = [], pics = [], author, majorType, content, dynamicTitle, module_stat;
387
+ let msg_meta = `B站【${upName}】动态推送:\n`;
404
388
  let dynamicType = data.type;
389
+ function formatNumber(num) {
390
+ if (num >= 10000) {
391
+ return `${(num / 10000).toFixed(1)}万`;
392
+ }
393
+ return num.toString();
394
+ }
405
395
  switch (dynamicType) {
406
396
  case 'DYNAMIC_TYPE_AV':
407
397
  // 处理视频动态
@@ -409,14 +399,22 @@ class BiliQuery {
409
399
  author = data?.modules?.module_author;
410
400
  if (!desc && !author)
411
401
  return;
412
- title = `B站【${upName}】视频动态推送:\n`;
402
+ module_stat = data?.modules?.module_stat;
403
+ msg_meta = `B站【${upName}】视频动态推送:\n`;
413
404
  msg = [
414
- title,
415
- `-----------------------------\n`,
416
- `标题:${desc.title}\n`,
405
+ msg_meta,
406
+ `\n--------------------`,
407
+ `\n${desc.title}`, // 标题
408
+ `\n--------------------`,
409
+ `\n视频简介:`,
417
410
  `${desc.desc}\n`,
418
- `链接:${this.formatUrl(desc.jump_url)}\n`,
419
- `时间:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
411
+ `\n--------------------`,
412
+ `投稿:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
413
+ `\n--------------------`,
414
+ `\n${desc?.stat?.danmaku}弹幕 • ${desc?.stat?.play}播放`,
415
+ `\n${formatNumber(module_stat?.like?.count)}点赞 • ${formatNumber(module_stat?.comment?.count)}评论 • ${formatNumber(module_stat?.forward?.count)}转发`,
416
+ `\n--------------------`,
417
+ `\n链接:${this.formatUrl(desc.jump_url)}`
420
418
  ];
421
419
  pics = [Segment.image(desc?.cover)];
422
420
  return { msg, pics, dynamicType };
@@ -441,13 +439,19 @@ class BiliQuery {
441
439
  }
442
440
  if (!desc && !author)
443
441
  return;
444
- title = `B站【${upName}】动态推送:\n`;
442
+ module_stat = data?.modules?.module_stat;
443
+ msg_meta = `B站【${upName}】图文动态推送:\n`;
445
444
  msg = [
446
- title,
447
- `-----------------------------\n`,
448
- `内容:${this.dynamicContentLimit(content, setData)}\n`,
449
- `链接:${BiliDrawDynamicLinkUrl}${data.id_str}\n`,
450
- `时间:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
445
+ msg_meta,
446
+ `\n--------------------`,
447
+ `\n正文:`,
448
+ `\n${this.dynamicContentLimit(content, setData)}`,
449
+ `\n--------------------`,
450
+ `\n投稿:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
451
+ `\n--------------------`,
452
+ `\n${formatNumber(module_stat?.like?.count)}点赞 • ${formatNumber(module_stat?.comment?.count)}评论 • ${formatNumber(module_stat?.forward?.count)}转发`,
453
+ `\n--------------------`,
454
+ `\n链接:${BiliDrawDynamicLinkUrl}${data.id_str}\n`
451
455
  ];
452
456
  return { msg, pics, dynamicType };
453
457
  case 'DYNAMIC_TYPE_DRAW':
@@ -480,6 +484,7 @@ class BiliQuery {
480
484
  }
481
485
  if (!desc && !pics && !author)
482
486
  return;
487
+ module_stat = data?.modules?.module_stat;
483
488
  const dynamicPicCountLimit = setData.pushPicCountLimit || 3;
484
489
  if (pics.length > dynamicPicCountLimit) {
485
490
  pics.length = dynamicPicCountLimit;
@@ -487,13 +492,18 @@ class BiliQuery {
487
492
  pics = pics.map((item) => {
488
493
  return Segment.image(item);
489
494
  });
490
- title = `B站【${upName}】图文动态推送:\n`;
495
+ msg_meta = `B站【${upName}】图文动态推送:\n`;
491
496
  msg = [
492
- title,
493
- `-----------------------------\n`,
494
- `${this.dynamicContentLimit(content, setData)}\n`,
495
- `链接:${BiliDrawDynamicLinkUrl}${data.id_str}\n`,
496
- `时间:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
497
+ msg_meta,
498
+ `\n--------------------`,
499
+ `\n正文:`,
500
+ `\n${this.dynamicContentLimit(content, setData)}`,
501
+ `\n--------------------`,
502
+ `\n投稿:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
503
+ `\n--------------------`,
504
+ `\n${formatNumber(module_stat?.like?.count)}点赞 • ${formatNumber(module_stat?.comment?.count)}评论 • ${formatNumber(module_stat?.forward?.count)}转发`,
505
+ `\n--------------------`,
506
+ `\n链接:${BiliDrawDynamicLinkUrl}${data.id_str}`
497
507
  ];
498
508
  return { msg, pics, dynamicType };
499
509
  case 'DYNAMIC_TYPE_ARTICLE':
@@ -527,16 +537,24 @@ class BiliQuery {
527
537
  }
528
538
  if (!desc && !author)
529
539
  return;
540
+ module_stat = data?.modules?.module_stat;
530
541
  pics = pics.map((item) => {
531
542
  return Segment.image(item);
532
543
  });
533
- title = `B站【${upName}】文章动态推送:\n`;
544
+ msg_meta = `B站【${upName}】文章动态推送:\n`;
534
545
  msg = [
535
- title,
536
- `-----------------------------\n`,
537
- `标题:${dynamicTitle}\n`,
538
- `链接:${this.formatUrl(desc.jump_url)}\n`,
539
- `时间:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
546
+ msg_meta,
547
+ `\n--------------------`,
548
+ `\n${dynamicTitle}`,
549
+ `\n--------------------`,
550
+ `\n正文:`,
551
+ `\n${this.dynamicContentLimit(content, setData)}`,
552
+ `\n--------------------`,
553
+ `\n投稿:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
554
+ `\n--------------------`,
555
+ `\n${formatNumber(module_stat?.like?.count)}点赞 • ${formatNumber(module_stat?.comment?.count)}评论 • ${formatNumber(module_stat?.forward?.count)}转发`,
556
+ `\n--------------------`,
557
+ `\n链接:${this.formatUrl(desc.jump_url)}`
540
558
  ];
541
559
  return { msg, pics, dynamicType };
542
560
  case 'DYNAMIC_TYPE_FORWARD':
@@ -548,6 +566,7 @@ class BiliQuery {
548
566
  return;
549
567
  if (!data.orig)
550
568
  return;
569
+ module_stat = data?.modules?.module_stat;
551
570
  isForward = true;
552
571
  let orig = await this.formatTextDynamicData(upName, data.orig, isForward, setData);
553
572
  let origContent = [];
@@ -558,14 +577,19 @@ class BiliQuery {
558
577
  else {
559
578
  return 'continue';
560
579
  }
561
- title = `B站【${upName}】转发动态推送:\n`;
580
+ msg_meta = `B站【${upName}】转发动态推送:\n`;
562
581
  msg = [
563
- title,
564
- `-----------------------------\n`,
565
- `${this.dynamicContentLimit(content, setData)}\n`,
566
- `链接:${BiliDrawDynamicLinkUrl}${data.id_str}\n`,
567
- `时间:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}\n`,
568
- '\n---以下为转发内容---\n',
582
+ msg_meta,
583
+ `\n--------------------`,
584
+ `\n正文:`,
585
+ `\n${this.dynamicContentLimit(content, setData)}`,
586
+ `\n--------------------`,
587
+ `\n投稿:${author ? moment(author.pub_ts * 1000).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
588
+ `\n--------------------`,
589
+ `\n${formatNumber(module_stat?.like?.count)}点赞 • ${formatNumber(module_stat?.comment?.count)}评论 • ${formatNumber(module_stat?.forward?.count)}转发`,
590
+ `\n--------------------`,
591
+ `\n链接:${BiliDrawDynamicLinkUrl}${data.id_str}\n`,
592
+ '\n>>>>以下为转发内容<<<<\n',
569
593
  ...origContent
570
594
  ];
571
595
  return { msg, pics, dynamicType };
@@ -578,8 +602,18 @@ class BiliQuery {
578
602
  desc = desc?.live_play_info;
579
603
  if (!desc)
580
604
  return;
581
- title = `B站【${upName}】直播动态推送:\n`;
582
- msg = [title, `-----------------------------\n`, `标题:${desc.title}\n`, `链接:https:${desc.link}`];
605
+ msg_meta = `B站【${upName}】直播动态推送:\n`;
606
+ msg = [
607
+ msg_meta,
608
+ `\n--------------------`,
609
+ `\n${desc.title}`,
610
+ `\n--------------------`,
611
+ `\n分区:${desc?.parent_area_name} (${desc?.area_name})`,
612
+ `\n开播:${moment(desc.live_start_time * 1000).format('YYYY年MM月DD日 HH:mm:ss')}`,
613
+ `\n--------------------`,
614
+ `\n${formatNumber(desc?.watched_show?.num)}人看过`,
615
+ `\n链接:https:${desc.link}`
616
+ ];
583
617
  pics = [Segment.image(desc.cover)];
584
618
  return { msg, pics, dynamicType };
585
619
  default:
@@ -60,6 +60,7 @@ class BiliTask {
60
60
  async processBiliData(biliPushData, biliConfigData, uidMap, dynamicList) {
61
61
  let getDataRandomDelay = biliConfigData?.getDataRandomDelay || 8000; // 获取相邻up动态数据的随机延时间隔
62
62
  const requestedDataOfUids = new Map(); // 存放已请求的 uid 映射
63
+ const printedList = new Set(); // 已打印的动态列表
63
64
  for (let chatType in biliPushData) {
64
65
  // 遍历 group 和 private
65
66
  if (!uidMap.has(chatType)) {
@@ -75,6 +76,10 @@ class BiliTask {
75
76
  let resp;
76
77
  // 检查是否已经请求过该 uid
77
78
  if (!requestedDataOfUids.has(uid)) {
79
+ if (!printedList.has(uid)) {
80
+ logger.info(`正在检测B站动态 [ ${name} : ${uid} ]`);
81
+ printedList.add(uid);
82
+ }
78
83
  resp = await this.hendleEventDynamicData(uid);
79
84
  if (resp) {
80
85
  if (resp.code === 0) {
@@ -110,6 +115,7 @@ class BiliTask {
110
115
  }
111
116
  }
112
117
  requestedDataOfUids.clear(); // 清空已请求的 uid 映射
118
+ printedList.clear(); // 清空已打印的动态列表
113
119
  }
114
120
  /**
115
121
  * 构建uid对应动态数据映射
@@ -120,17 +126,12 @@ class BiliTask {
120
126
  * @param biliConfigData Bilibili配置数据
121
127
  */
122
128
  async makeUidDynamicDataMap(uidMap, dynamicList, now, dynamicTimeRange, biliConfigData, messageMap) {
123
- const printedList = new Set(); // 已打印的动态列表
124
129
  for (let [chatType, chatTypeMap] of uidMap) {
125
130
  for (let [upUid, bot_idMap] of chatTypeMap) {
126
131
  const tempDynamicList = dynamicList[upUid] || [];
127
132
  const willPushDynamicList = [];
128
133
  for (let dynamicItem of tempDynamicList) {
129
134
  let author = dynamicItem?.modules?.module_author || {};
130
- if (!printedList.has(author?.mid)) {
131
- logger.info(`正在检测B站动态 [ ${author?.name} : ${author?.mid} ]`);
132
- printedList.add(author?.mid);
133
- }
134
135
  if (!author?.pub_ts)
135
136
  continue; // 如果动态没有发布时间,跳过当前循环
136
137
  if (Number(now - author.pub_ts) > dynamicTimeRange) {
@@ -157,7 +158,6 @@ class BiliTask {
157
158
  }
158
159
  }
159
160
  }
160
- printedList.clear(); // 清空已打印的动态列表
161
161
  }
162
162
  /**
163
163
  * 渲染构建待发送的动态消息数据的映射数组
@@ -72,7 +72,7 @@ class WeiboQuery {
72
72
  /**动态详情链接 */
73
73
  let detail_url = `https://weibo.com/${info?.user?.id}/${info?.bid}`;
74
74
  /* 构造动态渲染数据 *************************** */
75
- let pics = [];
75
+ let pics = [], video_pics_list;
76
76
  let formatData = { data: {} };
77
77
  /**头像 */
78
78
  formatData.data.face = face_url;
@@ -85,12 +85,17 @@ class WeiboQuery {
85
85
  formatData.data.type = type;
86
86
  switch (type) {
87
87
  case 'DYNAMIC_TYPE_AV':
88
+ video_pics_list = info?.pics ? info?.pics : info?.page_info?.page_pic?.url ? [{ large: { url: info.page_info.page_pic.url } }] : [];
89
+ pics =
90
+ video_pics_list.map((img) => {
91
+ return { url: img?.large?.url, width: Number(img?.large?.geo?.width), height: Number(img?.large?.geo?.height) };
92
+ }) || [];
88
93
  formatData.data.title = info?.page_info?.title || '';
89
94
  formatData.data.content = this.parseRichTextNodes(info?.text);
90
95
  formatData.data.url = detail_url;
91
96
  formatData.data.pubTs = moment(created_time).format('YYYY年MM月DD日 HH:mm:ss');
92
97
  formatData.data.category = '视频动态';
93
- formatData.data.pics = info?.page_info?.page_pic?.url ? [{ url: info.page_info.page_pic.url }] : [];
98
+ formatData.data.pics = pics;
94
99
  break;
95
100
  case 'DYNAMIC_TYPE_DRAW':
96
101
  let raw_pics_list = retweeted ? info?.retweeted_status?.pics || [] : info?.pics || [];
@@ -182,7 +187,7 @@ class WeiboQuery {
182
187
  /**图片高清资源链接*/
183
188
  pic_urls = [],
184
189
  /**图片*/
185
- pics = [];
190
+ pics = [], video_pics_list;
186
191
  let info = raw_post?.mblog || raw_post;
187
192
  let retweeted = info && info.retweeted_status ? true : false; //是否为转发动态
188
193
  let pic_num = retweeted ? info?.retweeted_status?.pic_num : info?.pic_num;
@@ -205,24 +210,39 @@ class WeiboQuery {
205
210
  /**动态发布时间 */
206
211
  let created_time = this.getDynamicCreatetDate(raw_post);
207
212
  let detail_url = `https://weibo.com/${info?.user?.id}/${info?.bid}`;
208
- let title = `微博【${upName}】动态推送:\n`;
213
+ let msg_meta = `微博【${upName}】动态推送:\n`;
209
214
  const dynamicPicCountLimit = setData.pushPicCountLimit || 3;
215
+ function formatNumber(num) {
216
+ if (num >= 10000) {
217
+ return `${(num / 10000).toFixed(1)}万`;
218
+ }
219
+ return num.toString();
220
+ }
210
221
  switch (dynamicType) {
211
222
  case 'DYNAMIC_TYPE_AV':
212
223
  if (!info)
213
224
  return;
214
- let cover_img_url = info?.page_info?.page_pic?.url;
215
- let cover_img = Segment.image(cover_img_url, false, 15000, { referer: 'https://weibo.com' });
216
- title = `微博【${upName}】视频动态推送:\n`;
225
+ video_pics_list = info?.pics ? info?.pics : info?.page_info?.page_pic?.url ? [{ large: { url: info.page_info.page_pic.url } }] : [];
226
+ pic_urls = video_pics_list.map(img => img?.large?.url);
227
+ for (const pic_url of pic_urls) {
228
+ const temp = Segment.image(pic_url, false, 15000, { referer: 'https://weibo.com' });
229
+ pics.push(temp);
230
+ }
231
+ msg_meta = `微博【${upName}】视频动态推送:\n`;
217
232
  msg = [
218
- title,
219
- `-----------------------------\n`,
220
- `标题:${info?.page_info?.title || ''}\n`,
221
- `${this.filterText(info?.text)}\n`,
222
- `链接:${detail_url}\n`,
223
- `时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
233
+ msg_meta,
234
+ `\n--------------------`,
235
+ `\n${info?.page_info?.title || ''}`, //标题
236
+ `\n--------------------`,
237
+ `\n正文:`,
238
+ `\n${this.filterText(info?.text)}`,
239
+ `\n--------------------`,
240
+ `\n投稿:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
241
+ `\n--------------------`,
242
+ `\n${formatNumber(info?.attitudes_count)}点赞 • ${formatNumber(info?.comments_count)}评论 • ${formatNumber(info?.reposts_count)}转发 `,
243
+ `\n--------------------`,
244
+ `\n链接:${detail_url}`
224
245
  ];
225
- pics = [cover_img];
226
246
  return { msg, pics, dynamicType };
227
247
  case 'DYNAMIC_TYPE_DRAW':
228
248
  raw_pics_list = retweeted ? info?.retweeted_status?.pics || [] : info?.pics || [];
@@ -235,13 +255,18 @@ class WeiboQuery {
235
255
  const temp = Segment.image(pic_url, false, 15000, { referer: 'https://weibo.com' });
236
256
  pics.push(temp);
237
257
  }
238
- title = `微博【${upName}】图文动态推送:\n`;
258
+ msg_meta = `微博【${upName}】图文动态推送:\n`;
239
259
  msg = [
240
- title,
241
- `-----------------------------\n`,
242
- `${this.dynamicContentLimit(this.filterText(info?.text), setData)}\n`,
243
- `链接:${detail_url}\n`,
244
- `时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
260
+ msg_meta,
261
+ `\n--------------------`,
262
+ `\n正文:`,
263
+ `\n${this.dynamicContentLimit(this.filterText(info?.text), setData)}`,
264
+ `\n--------------------`,
265
+ `\n投稿:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
266
+ `\n--------------------`,
267
+ `\n${formatNumber(info?.attitudes_count)}点赞 • ${formatNumber(info?.comments_count)}评论 • ${formatNumber(info?.reposts_count)}转发 `,
268
+ `\n--------------------`,
269
+ `\n链接:${detail_url}`
245
270
  ];
246
271
  return { msg, pics, dynamicType };
247
272
  case 'DYNAMIC_TYPE_ARTICLE':
@@ -255,13 +280,18 @@ class WeiboQuery {
255
280
  const temp = Segment.image(pic_url, false, 15000, { referer: 'https://weibo.com' });
256
281
  pics.push(temp);
257
282
  }
258
- title = `微博【${upName}】文章动态推送:\n`;
283
+ msg_meta = `微博【${upName}】文章动态推送:\n`;
259
284
  msg = [
260
- title,
261
- `-----------------------------\n`,
262
- `正文:${this.dynamicContentLimit(this.filterText(info?.text), setData)}\n`,
263
- `链接:${detail_url}\n`,
264
- `时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
285
+ msg_meta,
286
+ `\n--------------------`,
287
+ `\n正文:`,
288
+ `\n${this.dynamicContentLimit(this.filterText(info?.text), setData)}`,
289
+ `\n--------------------`,
290
+ `\n投稿:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
291
+ `\n--------------------`,
292
+ `\n${formatNumber(info?.attitudes_count)}点赞 • ${formatNumber(info?.comments_count)}评论 • ${formatNumber(info?.reposts_count)}转发 `,
293
+ `\n--------------------`,
294
+ `\n链接:${detail_url}`
265
295
  ];
266
296
  return { msg, pics, dynamicType };
267
297
  case 'DYNAMIC_TYPE_FORWARD':
@@ -280,14 +310,19 @@ class WeiboQuery {
280
310
  else {
281
311
  return 'continue';
282
312
  }
283
- title = `微博【${upName}】转发动态推送:\n`;
313
+ msg_meta = `微博【${upName}】转发动态推送:\n`;
284
314
  msg = [
285
- title,
286
- `-----------------------------\n`,
287
- `${this.dynamicContentLimit(this.filterText(info?.text), setData)}\n`,
288
- `链接:${detail_url}\n`,
289
- `时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}\n`,
290
- '\n---以下为转发内容---\n',
315
+ msg_meta,
316
+ `\n--------------------`,
317
+ `\n正文:`,
318
+ `\n${this.dynamicContentLimit(this.filterText(info?.text), setData)}`,
319
+ `\n--------------------`,
320
+ `\n投稿:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`,
321
+ `\n--------------------`,
322
+ `\n${formatNumber(info?.attitudes_count)}点赞 • ${formatNumber(info?.comments_count)}评论 • ${formatNumber(info?.reposts_count)}转发 `,
323
+ `\n--------------------`,
324
+ `\n链接:${detail_url}\n`,
325
+ '\n>>>>以下为转发内容<<<<\n',
291
326
  ...origContent
292
327
  ];
293
328
  return { msg, pics, dynamicType };
@@ -40,6 +40,7 @@ class WeiboTask {
40
40
  */
41
41
  async processWeiboData(weiboPushData, uidMap, dynamicList) {
42
42
  const requestedDataOfUids = new Map(); // 存放已请求的 uid 映射
43
+ const printedList = new Set(); // 已打印的动态列表
43
44
  for (let chatType in weiboPushData) {
44
45
  // 遍历 group 和 private
45
46
  if (!uidMap.has(chatType)) {
@@ -55,11 +56,15 @@ class WeiboTask {
55
56
  let resp;
56
57
  // 检查是否已经请求过该 uid
57
58
  if (!requestedDataOfUids.has(uid)) {
59
+ if (!printedList.has(uid)) {
60
+ logger.info(`正在检测微博动态 [ ${name} : ${uid} ]`);
61
+ printedList.add(uid);
62
+ }
58
63
  resp = await new WeiboWebDataFetcher().getBloggerDynamicList(uid); // 获取指定 uid 的动态列表
59
64
  if (resp) {
60
65
  requestedDataOfUids.set(uid, resp); // 将响应数据存储到映射中
61
66
  const dynamicData = resp || [];
62
- dynamicList[subInfoOfup.uid] = dynamicData;
67
+ dynamicList[uid] = dynamicData;
63
68
  }
64
69
  }
65
70
  if (!chatTypeMap.has(uid)) {
@@ -76,6 +81,7 @@ class WeiboTask {
76
81
  }
77
82
  }
78
83
  requestedDataOfUids.clear(); // 清空已请求的映射
84
+ printedList.clear(); // 清空已打印的动态列表
79
85
  }
80
86
  /**
81
87
  * 构建uid对应动态数据映射
@@ -86,7 +92,6 @@ class WeiboTask {
86
92
  * @param weiboConfigData 微博配置数据
87
93
  */
88
94
  async makeUidDynamicDataMap(uidMap, dynamicList, now, dynamicTimeRange, weiboConfigData, messageMap) {
89
- const printedList = new Set(); // 已打印的动态列表
90
95
  for (let [chatType, chatTypeMap] of uidMap) {
91
96
  for (let [upUid, bot_idMap] of chatTypeMap) {
92
97
  const tempDynamicList = dynamicList[upUid] || [];
@@ -94,10 +99,6 @@ class WeiboTask {
94
99
  for (let dynamicItem of tempDynamicList) {
95
100
  let raw_post = dynamicItem || {};
96
101
  let user = raw_post?.mblog?.user || {};
97
- if (!printedList.has(user?.id)) {
98
- logger.info(`正在检测微博动态 [ ${user?.screen_name} : ${user?.id} ]`);
99
- printedList.add(user?.id);
100
- }
101
102
  if (!raw_post?.mblog?.created_at)
102
103
  continue;
103
104
  if (Number(now - WeiboQuery.getDynamicCreatetDate(raw_post) / 1000) > dynamicTimeRange) {
@@ -124,7 +125,6 @@ class WeiboTask {
124
125
  }
125
126
  }
126
127
  }
127
- printedList.clear(); // 清空已打印的动态列表
128
128
  }
129
129
  /**
130
130
  * 渲染构建待发送的动态消息数据的映射数组
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yz-yuki-plugin",
3
- "version": "2.0.7-13",
3
+ "version": "2.0.7-15",
4
4
  "description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
5
5
  "author": "snowtafir",
6
6
  "type": "module",
@@ -31,12 +31,12 @@
31
31
  "debug": "^4.3.6",
32
32
  "jsdom": "^25.0.1",
33
33
  "json5": "^2.2.3",
34
- "jsxp": "^1.1.2",
34
+ "jsxp": "^1.2.1",
35
35
  "lodash": "^4.17.21",
36
36
  "md5": "^2.3.0",
37
37
  "moment": "^2.30.1",
38
38
  "node-fetch": "^3.3.2",
39
- "puppeteer": "^24.4.0",
39
+ "puppeteer": "^24.8.2",
40
40
  "qrcode": "^1.5.4",
41
41
  "react": "^18.3.1",
42
42
  "react-dom": "^18.3.1",
@@ -63,14 +63,14 @@
63
63
  "icqq": "^0.6.10",
64
64
  "jsdom": "^24.1.1",
65
65
  "json5": "^2.2.3",
66
- "jsxp": "^1.1.2",
66
+ "jsxp": "^1.2.1",
67
67
  "lodash": "^4.17.21",
68
- "lvyjs": "^0.2.14",
68
+ "lvyjs": "^0.2.19",
69
69
  "md5": "^2.3.0",
70
70
  "node-fetch": "^3.3.2",
71
71
  "postcss": "^8.4.47",
72
72
  "prettier": "^3.4.2",
73
- "puppeteer": "^24.4.0",
73
+ "puppeteer": "^24.8.2",
74
74
  "qrcode": "^1.5.4",
75
75
  "react": "^18.3.1",
76
76
  "react-dom": "^18.3.1",