yz-yuki-plugin 2.0.1 → 2.0.2-1

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +8 -7
  3. package/lib/apps/bilibili.js +52 -20
  4. package/lib/apps/help.js +3 -0
  5. package/lib/apps/version.js +3 -0
  6. package/lib/apps/weibo.js +34 -16
  7. package/lib/components/dynamic/Account.js +2 -0
  8. package/lib/components/dynamic/Content.js +2 -0
  9. package/lib/components/dynamic/Footer.js +1 -0
  10. package/lib/components/dynamic/ForwardContent.js +2 -0
  11. package/lib/components/dynamic/LogoText.js +2 -0
  12. package/lib/components/dynamic/MainPage.js +1 -0
  13. package/lib/components/help/Help.js +1 -0
  14. package/lib/components/loginQrcode/Page.js +1 -0
  15. package/lib/index.js +53 -2
  16. package/lib/models/bilibili/bilibili.api.d.ts +3 -0
  17. package/lib/models/bilibili/bilibili.api.js +9 -0
  18. package/lib/models/bilibili/bilibili.get.web.data.d.ts +3 -0
  19. package/lib/models/bilibili/bilibili.get.web.data.js +4 -0
  20. package/lib/models/bilibili/bilibili.models.d.ts +37 -0
  21. package/lib/models/bilibili/bilibili.models.js +71 -19
  22. package/lib/models/bilibili/bilibili.query.d.ts +24 -0
  23. package/lib/models/bilibili/bilibili.query.js +69 -11
  24. package/lib/models/bilibili/bilibili.task.d.ts +41 -0
  25. package/lib/models/bilibili/bilibili.task.js +77 -34
  26. package/lib/models/bilibili/bilibili.wbi.d.ts +6 -0
  27. package/lib/models/bilibili/bilibili.wbi.js +16 -3
  28. package/lib/models/version/version.d.ts +10 -0
  29. package/lib/models/version/version.js +12 -1
  30. package/lib/models/weibo/weibo.api.d.ts +1 -0
  31. package/lib/models/weibo/weibo.api.js +2 -0
  32. package/lib/models/weibo/weibo.get.web.data.d.ts +3 -0
  33. package/lib/models/weibo/weibo.get.web.data.js +3 -0
  34. package/lib/models/weibo/weibo.query.d.ts +20 -0
  35. package/lib/models/weibo/weibo.query.js +63 -6
  36. package/lib/models/weibo/weibo.task.d.ts +49 -0
  37. package/lib/models/weibo/weibo.task.js +84 -34
  38. package/lib/utils/config.d.ts +50 -0
  39. package/lib/utils/config.js +55 -2
  40. package/lib/utils/image.d.ts +11 -0
  41. package/lib/utils/image.js +15 -0
  42. package/lib/utils/paths.js +7 -7
  43. package/lib/utils/puppeteer.render.d.ts +15 -0
  44. package/lib/utils/puppeteer.render.js +37 -15
  45. package/package.json +6 -5
@@ -6,16 +6,21 @@ import axios from 'axios';
6
6
  import lodash from 'lodash';
7
7
 
8
8
  class BiliQuery {
9
+ /**
10
+ * 序列化动态数据
11
+ * @param data - 动态数据对象
12
+ * @returns 序列化后的动态数据对象
13
+ */
9
14
  static async formatDynamicData(data) {
10
15
  const BiliDrawDynamicLinkUrl = "https://m.bilibili.com/dynamic/";
11
16
  let desc, pics = [], majorType;
12
17
  let formatData = { data: {} };
13
18
  const author = data?.modules?.module_author || {};
14
- formatData.data.face = author.face;
15
- formatData.data.name = author.name;
16
- formatData.data.pendant = author?.pendant?.image || data?.pendant?.image;
17
- formatData.data.created = moment().format("YYYY年MM月DD日 HH:mm:ss");
18
- formatData.data.type = data.type;
19
+ formatData.data.face = author.face; // 作者头像
20
+ formatData.data.name = author.name; // 作者名字
21
+ formatData.data.pendant = author?.pendant?.image || data?.pendant?.image; // 作者挂件
22
+ formatData.data.created = moment().format("YYYY年MM月DD日 HH:mm:ss"); // 创建时间
23
+ formatData.data.type = data.type; // 动态类型
19
24
  switch (data.type) {
20
25
  case "DYNAMIC_TYPE_AV":
21
26
  desc = data?.modules?.module_dynamic?.major?.archive || {};
@@ -85,10 +90,13 @@ class BiliQuery {
85
90
  const readInfo = await this.getFullArticleContent(this.formatUrl(desc?.jump_url));
86
91
  formatData.data.content = this.praseFullArticleContent(readInfo?.content);
87
92
  formatData.data.pics = [];
88
- if ((formatData.data.content) === null) {
93
+ if (!(formatData.data.content)) {
89
94
  formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text) || "";
90
95
  formatData.data.pics = pics;
91
96
  }
97
+ else {
98
+ formatData.data.pics = [];
99
+ }
92
100
  }
93
101
  else {
94
102
  formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text) || "";
@@ -146,12 +154,18 @@ class BiliQuery {
146
154
  }
147
155
  return {
148
156
  ...formatData,
149
- uid: data?.id_str,
157
+ uid: data?.id_str, // 用户ID
150
158
  };
151
159
  }
152
160
  ;
161
+ /**
162
+ * 动态内容富文本节点解析
163
+ * @param nodes - 动态内容富文本节点
164
+ * @returns 解析后的动态内容富文本
165
+ */
153
166
  static parseRichTextNodes = (nodes) => {
154
167
  if (typeof nodes === 'string') {
168
+ // 将\t 替换为&nbsp;实现空格,\n 替换为 <br> 以实现换行
155
169
  nodes = nodes.replace(/\t/g, '&nbsp;');
156
170
  return nodes.replace(/\n/g, '<br>');
157
171
  }
@@ -159,23 +173,30 @@ class BiliQuery {
159
173
  return nodes.map((node) => {
160
174
  switch (node.type) {
161
175
  case 'RICH_TEXT_NODE_TYPE_TOPIC':
176
+ // 确保链接以 https:// 开头
162
177
  let jumpUrl = node?.jump_url;
163
178
  if (jumpUrl && !jumpUrl.startsWith('http://') && !jumpUrl.startsWith('https://')) {
164
179
  jumpUrl = `https://${jumpUrl}`;
165
180
  }
166
181
  return `<span class="bili-rich-text-module topic" href="${jumpUrl}">${node?.text}</span>`;
167
182
  case 'RICH_TEXT_NODE_TYPE_TEXT':
183
+ // 正文将 \n 替换为 <br> 以实现换行
168
184
  return node.text.replace(/\n/g, '<br>');
169
185
  case 'RICH_TEXT_NODE_TYPE_AT':
186
+ // 处理 @ 类型,使用官方的HTML标签写法
170
187
  return `<span data-module="desc" data-type="at" data-oid="${node?.rid}" class="bili-rich-text-module at">${node?.text}</span>`;
171
188
  case 'RICH_TEXT_NODE_TYPE_LOTTERY':
189
+ // 处理互动抽奖类型,使用官方的HTML标签写法
172
190
  return `<span data-module="desc" data-type="lottery" data-oid="${node?.rid}" class="bili-rich-text-module lottery">${node?.text}</span>`;
173
191
  case 'RICH_TEXT_NODE_TYPE_WEB':
192
+ // 处理 RICH_TEXT_NODE_TYPE_WEB 类型,直接拼接 text 属性
174
193
  return node.text;
175
194
  case 'RICH_TEXT_NODE_TYPE_EMOJI':
195
+ // 处理表情类型,使用 img 标签显示表情
176
196
  const emoji = node.emoji;
177
197
  return `<img src="${emoji?.icon_url}" alt="${emoji?.text}" title="${emoji?.text}" style="vertical-align: middle; width: ${emoji?.size}em; height: ${emoji?.size}em;">`;
178
198
  case 'RICH_TEXT_NODE_TYPE_GOODS':
199
+ // 处理商品推广类型,使用官方的HTML标签写法
179
200
  const goods_url = node?.jump_url;
180
201
  return `<span data-module="desc" data-type="goods" data-url="${goods_url}" data-oid="${node?.rid}" class="bili-rich-text-module goods ${node?.icon_name}">&ZeroWidthSpace;${node?.text}</span>`;
181
202
  default:
@@ -184,9 +205,14 @@ class BiliQuery {
184
205
  }).join('');
185
206
  }
186
207
  else {
208
+ // 未知类型,直接返回
187
209
  return nodes;
188
210
  }
189
211
  };
212
+ /**获取完整B站文章内容
213
+ * @param postId - 文章ID
214
+ * @returns {Json}完整的B站文章内容json数据
215
+ */
190
216
  static async getFullArticleContent(postUrl) {
191
217
  const Cookie = await readSyncCookie();
192
218
  try {
@@ -207,24 +233,37 @@ class BiliQuery {
207
233
  return null;
208
234
  }
209
235
  }
236
+ /**解析完整文章内容 */
210
237
  static praseFullArticleContent(content) {
211
238
  content = String(content).replace(/\n/g, '<br>');
239
+ // 使用正则表达式匹配 <img> 标签的 data-src 属性
212
240
  const imgTagRegex = /<img[^>]*data-src="([^"]*)"[^>]*>/g;
241
+ // 替换 data-src 为 src,并将 // 开头的链接改为 https:// 开头
213
242
  content = content.replace(imgTagRegex, (match, p1) => {
214
243
  const newSrc = this.formatUrl(p1);
215
244
  return match.replace('data-src', 'src').replace(p1, newSrc);
216
245
  });
217
246
  return content;
218
247
  }
248
+ // 处理斜杠开头的链接
219
249
  static formatUrl(url) {
220
250
  return 0 == url.indexOf('//') ? `https:${url}` : url;
221
251
  }
252
+ /**
253
+ * 生成动态消息文字内容
254
+ * @param upName - UP主名称
255
+ * @param formatData - 动态数据
256
+ * @param isForward - 是否为转发动态
257
+ * @param setData - 设置数据
258
+ * @returns 生成的动态消息文字内容
259
+ */
222
260
  static async formatTextDynamicData(upName, data, isForward, setData) {
223
261
  const BiliDrawDynamicLinkUrl = "https://m.bilibili.com/dynamic/";
224
262
  let desc, msg, pics, author, majorType, content, dynamicTitle;
225
263
  let title = `B站【${upName}】动态推送:\n`;
226
264
  switch (data.type) {
227
265
  case "DYNAMIC_TYPE_AV":
266
+ // 处理视频动态
228
267
  desc = data?.modules?.module_dynamic?.major?.archive;
229
268
  author = data?.modules?.module_author;
230
269
  if (!desc && !author)
@@ -241,6 +280,7 @@ class BiliQuery {
241
280
  ];
242
281
  return msg;
243
282
  case "DYNAMIC_TYPE_WORD":
283
+ // 处理文字动态
244
284
  author = data?.modules?.module_author;
245
285
  majorType = data?.modules?.module_dynamic?.major?.type;
246
286
  if (majorType === "MAJOR_TYPE_OPUS") {
@@ -267,6 +307,7 @@ class BiliQuery {
267
307
  ];
268
308
  return msg;
269
309
  case "DYNAMIC_TYPE_DRAW":
310
+ // 处理图文动态
270
311
  author = data?.modules?.module_author;
271
312
  majorType = data?.modules?.module_dynamic?.major?.type;
272
313
  if (majorType === "MAJOR_TYPE_OPUS") {
@@ -309,6 +350,7 @@ class BiliQuery {
309
350
  ];
310
351
  return msg;
311
352
  case "DYNAMIC_TYPE_ARTICLE":
353
+ // 处理文章动态
312
354
  author = data?.modules?.module_author;
313
355
  majorType = data?.modules?.module_dynamic?.major?.type;
314
356
  if (majorType === "MAJOR_TYPE_OPUS") {
@@ -349,6 +391,7 @@ class BiliQuery {
349
391
  ];
350
392
  return msg;
351
393
  case "DYNAMIC_TYPE_FORWARD":
394
+ // 处理转发动态
352
395
  author = data?.modules?.module_author;
353
396
  desc = data?.modules?.module_dynamic?.desc || {};
354
397
  content = desc?.text;
@@ -376,6 +419,7 @@ class BiliQuery {
376
419
  ];
377
420
  return msg;
378
421
  case "DYNAMIC_TYPE_LIVE_RCMD":
422
+ // 处理直播动态
379
423
  desc = data?.modules?.module_dynamic?.major?.live_rcmd?.content;
380
424
  if (!desc)
381
425
  return;
@@ -393,14 +437,17 @@ class BiliQuery {
393
437
  ];
394
438
  return msg;
395
439
  default:
440
+ // 处理未定义的动态类型
396
441
  (Bot.logger ?? logger)?.mark(`未处理的B站推送【${upName}】:${data.type}`);
397
442
  return "continue";
398
443
  }
399
444
  }
445
+ // 限制文字模式下动态内容的字数和行数
400
446
  static dynamicContentLimit(content, setData) {
401
447
  const lines = content.split("\n");
402
448
  const lengthLimit = setData.pushContentLenLimit || 100;
403
449
  const lineLimit = setData.pushContentLineLimit || 5;
450
+ // 限制行数
404
451
  if (lines.length > lineLimit) {
405
452
  lines.length = lineLimit;
406
453
  }
@@ -421,7 +468,9 @@ class BiliQuery {
421
468
  }
422
469
  return lines.join("\n");
423
470
  }
471
+ /**根据关键字更新 up 的动态类型 */
424
472
  static typeHandle(up, msg, type) {
473
+ // 定义一个对象映射,将关键字映射到对应的类型
425
474
  const typeMap = {
426
475
  "直播": "DYNAMIC_TYPE_LIVE_RCMD",
427
476
  "转发": "DYNAMIC_TYPE_FORWARD",
@@ -429,33 +478,42 @@ class BiliQuery {
429
478
  "图文": ["DYNAMIC_TYPE_DRAW", "DYNAMIC_TYPE_WORD"],
430
479
  "视频": "DYNAMIC_TYPE_AV"
431
480
  };
481
+ // 初始化新的类型集合,如果 up.type 存在则使用它,否则使用空数组
432
482
  let newType = new Set(up.type || []);
483
+ // 定义一个处理类型的函数,根据传入的 action 参数决定是添加还是删除类型
433
484
  const handleType = (action) => {
434
- let isHandled = false;
485
+ let isHandled = false; // 标记是否有类型被处理
486
+ // 遍历 typeMap 对象,根据 msg 中的关键字进行类型操作
435
487
  for (const [key, value] of Object.entries(typeMap)) {
436
488
  if (msg.indexOf(key) !== -1) {
437
489
  if (Array.isArray(value)) {
490
+ // 如果 value 是数组,则对数组中的每个元素进行操作
438
491
  value.forEach(v => action === "add" ? newType.add(v) : newType.delete(v));
439
492
  }
440
493
  else {
494
+ // 否则直接对单个值进行操作
441
495
  action === "add" ? newType.add(value) : newType.delete(value);
442
496
  }
443
- isHandled = true;
497
+ isHandled = true; // 标记有类型被处理
444
498
  }
445
499
  }
446
- return isHandled;
500
+ return isHandled; // 返回是否有类型被处理
447
501
  };
502
+ // 根据 type 参数决定是添加还是删除类型
448
503
  if (type === "add") {
449
- handleType("add");
504
+ handleType("add"); // 调用 handleType 函数进行类型添加
450
505
  }
451
506
  else if (type === "del") {
452
507
  if (!newType.size) {
508
+ // 如果 newType 为空,则初始化它为所有可能的类型
453
509
  newType = new Set(Object.values(typeMap).flat());
454
510
  }
511
+ // 调用 handleType 函数进行类型删除,如果没有类型被删除则清空 newType
455
512
  if (!handleType("delete")) {
456
513
  newType.clear();
457
514
  }
458
515
  }
516
+ // 将 newType 转换为数组并返回
459
517
  return Array.from(newType);
460
518
  }
461
519
  }
@@ -9,11 +9,52 @@ export declare class BiliTask {
9
9
  constructor(e?: EventType);
10
10
  hendleEventDynamicData(uid: string | number, count?: number): Promise<any>;
11
11
  runTask(): Promise<void>;
12
+ /**
13
+ * 处理Bilibili数据,获取动态列表并构建 uid 映射
14
+ * @param biliPushData Bilibili推送数据
15
+ * @param uidMap uid 映射
16
+ * @param dynamicList 动态列表
17
+ * @param lastLiveStatus 最后直播状态
18
+ */
12
19
  processBiliData(biliPushData: any, uidMap: Map<any, Map<string, any>>, dynamicList: any, lastLiveStatus: any): Promise<void>;
20
+ /**
21
+ * 推送动态消息
22
+ * @param uidMap uid 映射
23
+ * @param dynamicList 动态列表
24
+ * @param now 当前时间戳
25
+ * @param interval 推送间隔时间
26
+ * @param biliConfigData Bilibili配置数据
27
+ */
13
28
  pushDynamicMessages(uidMap: Map<any, Map<string, any>>, dynamicList: any, now: number, interval: number, biliConfigData: any): Promise<void>;
14
29
  sendDynamic(chatId: string | number, bot_id: string | number, upName: string, pushDynamicData: any, biliConfigData: any, chatType: string): Promise<string>;
30
+ /**
31
+ * 构建渲染数据
32
+ * @param extentData 扩展数据
33
+ * @param urlQrcodeData URL 二维码数据
34
+ * @param boxGrid 是否启用九宫格样式
35
+ * @returns 渲染数据
36
+ */
15
37
  buildRenderData(extentData: any, urlQrcodeData: string, boxGrid: boolean): MainProps;
38
+ /**
39
+ * 渲染动态卡片
40
+ * @param uid 用户 ID
41
+ * @param renderData 渲染数据
42
+ * @param ScreenshotOptionsData 截图选项数据
43
+ * @returns 图片数据
44
+ */
16
45
  renderDynamicCard(uid: string, renderData: MainProps, ScreenshotOptionsData: ScreenshotOptions): Promise<Buffer[] | null>;
46
+ /**
47
+ * 发送消息
48
+ * @param chatId 聊天 ID
49
+ * @param bot_id 机器人 ID
50
+ * @param chatType 聊天类型
51
+ * @param message 消息内容
52
+ */
17
53
  sendMessage(chatId: string | number, bot_id: string | number, chatType: string, message: any): Promise<void>;
54
+ /**
55
+ * 随机延时
56
+ * @param min 最小延时时间
57
+ * @param max 最大延时时间
58
+ */
18
59
  randomDelay(min: number, max: number): Promise<void>;
19
60
  }
@@ -26,7 +26,7 @@ class BiliTask {
26
26
  else if (resjson.code === -352) {
27
27
  await postGateway(cookie);
28
28
  if (count < 3) {
29
- await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (10500 - 2000 + 1) + 2000)));
29
+ await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (10500 - 2000 + 1) + 2000))); // 随机延时2-10.5秒
30
30
  await this.hendleEventDynamicData(uid, count + 1);
31
31
  logger.error(`获取 ${uid} 动态,Gateway count:${String(count)}`);
32
32
  }
@@ -44,18 +44,25 @@ class BiliTask {
44
44
  let biliPushData = await Config.getUserConfig("bilibili", "push");
45
45
  let interval = biliConfigData.interval || 7200;
46
46
  let lastLiveStatus = JSON.parse(await Redis.get("yuki:bililive:lastlivestatus")) || {};
47
- const uidMap = new Map();
48
- const dynamicList = {};
47
+ const uidMap = new Map(); // 存放group 和 private 对应所属 uid 与推送信息的映射
48
+ const dynamicList = {}; // 存放获取的所有动态,键为 uid,值为动态数组
49
49
  await this.processBiliData(biliPushData, uidMap, dynamicList, lastLiveStatus);
50
- let now = Date.now() / 1000;
50
+ let now = Date.now() / 1000; // 时间戳(秒)
51
51
  await this.pushDynamicMessages(uidMap, dynamicList, now, interval, biliConfigData);
52
52
  }
53
+ /**
54
+ * 处理Bilibili数据,获取动态列表并构建 uid 映射
55
+ * @param biliPushData Bilibili推送数据
56
+ * @param uidMap uid 映射
57
+ * @param dynamicList 动态列表
58
+ * @param lastLiveStatus 最后直播状态
59
+ */
53
60
  async processBiliData(biliPushData, uidMap, dynamicList, lastLiveStatus) {
54
- for (let chatType in biliPushData) {
61
+ for (let chatType in biliPushData) { // 遍历 group 和 private
55
62
  if (!uidMap.has(chatType)) {
56
63
  uidMap.set(chatType, new Map());
57
64
  }
58
- const chatTypeMap = uidMap.get(chatType);
65
+ const chatTypeMap = uidMap.get(chatType); // 建立当前 chatType (group 或 private) 的 uid 映射
59
66
  for (let chatId in biliPushData[chatType]) {
60
67
  const subUpsOfChat = biliPushData[chatType][chatId] || [];
61
68
  for (let subInfoOfup of subUpsOfChat) {
@@ -85,17 +92,25 @@ class BiliTask {
85
92
  const bot_id = subInfoOfup.bot_id || [];
86
93
  const { name, type } = subInfoOfup;
87
94
  chatTypeMap.set(subInfoOfup.uid, { chatIds, bot_id, upName: name, type });
88
- await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (8000 - 2000 + 1) + 2000)));
95
+ await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (8000 - 2000 + 1) + 2000))); // 随机延时2-8秒
89
96
  }
90
97
  }
91
98
  }
92
99
  }
100
+ /**
101
+ * 推送动态消息
102
+ * @param uidMap uid 映射
103
+ * @param dynamicList 动态列表
104
+ * @param now 当前时间戳
105
+ * @param interval 推送间隔时间
106
+ * @param biliConfigData Bilibili配置数据
107
+ */
93
108
  async pushDynamicMessages(uidMap, dynamicList, now, interval, biliConfigData) {
94
109
  for (let [chatType, chatTypeMap] of uidMap) {
95
110
  for (let [key, value] of chatTypeMap) {
96
111
  const tempDynamicList = dynamicList[key] || [];
97
112
  const willPushDynamicList = [];
98
- const printedList = new Set();
113
+ const printedList = new Set(); // 已打印的动态列表
99
114
  for (let dynamicItem of tempDynamicList) {
100
115
  let author = dynamicItem?.modules?.module_author || {};
101
116
  if (!printedList.has(author?.mid)) {
@@ -103,31 +118,33 @@ class BiliTask {
103
118
  printedList.add(author?.mid);
104
119
  }
105
120
  if (!author?.pub_ts)
106
- continue;
121
+ continue; // 如果动态没有发布时间,跳过当前循环
107
122
  if (Number(now - author.pub_ts) > interval) {
108
123
  logger.debug(`超过间隔,跳过 [ ${author?.name} : ${author?.mid} ] ${author?.pub_time} 的动态`);
109
124
  continue;
110
- }
125
+ } // 如果超过推送时间间隔,跳过当前循环
111
126
  if (dynamicItem.type === "DYNAMIC_TYPE_FORWARD" && !biliConfigData.pushTransmit)
112
- continue;
127
+ continue; // 如果关闭了转发动态的推送,跳过当前循环
113
128
  willPushDynamicList.push(dynamicItem);
114
129
  }
115
130
  printedList.clear();
116
- const pushMapInfo = value || {};
131
+ const pushMapInfo = value || {}; // 获取当前 uid 对应的推送信息
117
132
  const { chatIds, bot_id, upName, type } = pushMapInfo;
133
+ // 遍历待推送的动态数组,发送动态消息
118
134
  for (let pushDynamicData of willPushDynamicList) {
119
135
  if (chatIds && chatIds.length) {
120
136
  for (let chatId of chatIds) {
121
137
  if (type && type.length && !type.includes(pushDynamicData.type))
122
- continue;
123
- await this.sendDynamic(chatId, bot_id, upName, pushDynamicData, biliConfigData, chatType);
124
- await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (6500 - 2000 + 1) + 2000)));
138
+ continue; // 如果禁用了某类型的动态推送,跳过当前循环
139
+ await this.sendDynamic(chatId, bot_id, upName, pushDynamicData, biliConfigData, chatType); // 发送动态消息
140
+ await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (6500 - 2000 + 1) + 2000))); // 随机延时2-6.5秒
125
141
  }
126
142
  }
127
143
  }
128
144
  }
129
145
  }
130
146
  }
147
+ /*发送动态*/
131
148
  async sendDynamic(chatId, bot_id, upName, pushDynamicData, biliConfigData, chatType) {
132
149
  const id_str = pushDynamicData.id_str;
133
150
  let sended, markKey;
@@ -140,18 +157,18 @@ class BiliTask {
140
157
  sended = await Redis.get(`${markKey}${chatId}:${id_str}`);
141
158
  }
142
159
  if (sended)
143
- return;
160
+ return; // 如果已经发送过,则直接返回
144
161
  if (!!biliConfigData.pushMsgMode) {
145
- const { data, uid } = await BiliQuery.formatDynamicData(pushDynamicData);
162
+ const { data, uid } = await BiliQuery.formatDynamicData(pushDynamicData); // 处理动态数据
146
163
  const extentData = { ...data };
147
164
  const eval2 = eval;
148
- let banWords = eval2(`/${biliConfigData.banWords.join("|")}/g`);
165
+ let banWords = eval2(`/${biliConfigData.banWords.join("|")}/g`); // 构建屏蔽关键字正则表达式
149
166
  if (new RegExp(banWords).test(`${extentData?.title}${extentData?.content}`)) {
150
- return "return";
167
+ return "return"; // 如果动态包含屏蔽关键字,则直接返回
151
168
  }
152
- let boxGrid = !!biliConfigData.boxGrid === false ? false : true;
153
- let isSplit = !!biliConfigData.isSplit === false ? false : true;
154
- let style = isSplit ? '' : '.unfold { height: 7500px; }';
169
+ let boxGrid = !!biliConfigData.boxGrid === false ? false : true; // 是否启用九宫格样式,默认为 true
170
+ let isSplit = !!biliConfigData.isSplit === false ? false : true; // 是否启用分片截图,默认为 true
171
+ let style = isSplit ? '' : '.unfold { height: 7500px; }'; // 不启用分片截图模式的样式
155
172
  const urlQrcodeData = await QRCode.toDataURL(extentData?.url);
156
173
  let renderData = this.buildRenderData(extentData, urlQrcodeData, boxGrid);
157
174
  const ScreenshotOptionsData = {
@@ -168,31 +185,38 @@ class BiliTask {
168
185
  let imgs = await this.renderDynamicCard(uid, renderData, ScreenshotOptionsData);
169
186
  if (!imgs)
170
187
  return;
171
- Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 });
188
+ Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 }); // 设置已发送标记
172
189
  (logger ?? Bot.logger)?.mark("优纪插件:B站动态执行推送");
173
190
  for (let i = 0; i < imgs.length; i++) {
174
191
  const image = imgs[i];
175
192
  await this.sendMessage(chatId, bot_id, chatType, Segment.image(image));
176
- await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (6500 - 2000 + 1) + 2000)));
193
+ await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (6500 - 2000 + 1) + 2000))); // 随机延时2-6.5秒
177
194
  }
178
- await new Promise((resolve) => setTimeout(resolve, 1000));
195
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // 休眠1秒
179
196
  }
180
197
  else {
181
- const dynamicMsg = await BiliQuery.formatTextDynamicData(upName, pushDynamicData, false, biliConfigData);
182
- Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 });
198
+ const dynamicMsg = await BiliQuery.formatTextDynamicData(upName, pushDynamicData, false, biliConfigData); // 构建图文动态消息
199
+ Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 }); // 设置已发送标记
183
200
  if (dynamicMsg == "continue") {
184
- return "return";
201
+ return "return"; // 如果动态消息构建失败,则直接返回
185
202
  }
186
203
  if (biliConfigData.banWords.length > 0) {
187
- const banWords = new RegExp(biliConfigData.banWords.join("|"), "g");
204
+ const banWords = new RegExp(biliConfigData.banWords.join("|"), "g"); // 构建屏蔽关键字正则表达式
188
205
  if (banWords.test(dynamicMsg.join(""))) {
189
- return "return";
206
+ return "return"; // 如果动态消息包含屏蔽关键字,则直接返回
190
207
  }
191
208
  }
192
209
  await this.sendMessage(chatId, bot_id, chatType, dynamicMsg);
193
210
  await new Promise((resolve) => setTimeout(resolve, 1000));
194
211
  }
195
212
  }
213
+ /**
214
+ * 构建渲染数据
215
+ * @param extentData 扩展数据
216
+ * @param urlQrcodeData URL 二维码数据
217
+ * @param boxGrid 是否启用九宫格样式
218
+ * @returns 渲染数据
219
+ */
196
220
  buildRenderData(extentData, urlQrcodeData, boxGrid) {
197
221
  if (extentData.orig && (extentData.orig).length !== 0) {
198
222
  return {
@@ -246,18 +270,32 @@ class BiliTask {
246
270
  };
247
271
  }
248
272
  }
273
+ /**
274
+ * 渲染动态卡片
275
+ * @param uid 用户 ID
276
+ * @param renderData 渲染数据
277
+ * @param ScreenshotOptionsData 截图选项数据
278
+ * @returns 图片数据
279
+ */
249
280
  async renderDynamicCard(uid, renderData, ScreenshotOptionsData) {
250
- const dynamicMsg = await Image.renderPage(uid, "MainPage", renderData, ScreenshotOptionsData);
281
+ const dynamicMsg = await Image.renderPage(uid, "MainPage", renderData, ScreenshotOptionsData); // 渲染动态卡片
251
282
  if (dynamicMsg !== false) {
252
- return dynamicMsg.img;
283
+ return dynamicMsg.img; // 缓存图片数据
253
284
  }
254
285
  else {
255
286
  return null;
256
287
  }
257
288
  }
289
+ /**
290
+ * 发送消息
291
+ * @param chatId 聊天 ID
292
+ * @param bot_id 机器人 ID
293
+ * @param chatType 聊天类型
294
+ * @param message 消息内容
295
+ */
258
296
  async sendMessage(chatId, bot_id, chatType, message) {
259
297
  if (chatType === "group") {
260
- await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message)
298
+ await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message) // 发送群聊
261
299
  .catch((error) => {
262
300
  (logger ?? Bot.logger)?.error(`群组[${chatId}]推送失败:${JSON.stringify(error)}`);
263
301
  });
@@ -266,9 +304,14 @@ class BiliTask {
266
304
  await (Bot[bot_id] ?? Bot)?.pickFriend(String(chatId)).sendMsg(message)
267
305
  .catch((error) => {
268
306
  (logger ?? Bot.logger)?.error(`用户[${chatId}]推送失败:${JSON.stringify(error)}`);
269
- });
307
+ }); // 发送好友私聊
270
308
  }
271
309
  }
310
+ /**
311
+ * 随机延时
312
+ * @param min 最小延时时间
313
+ * @param max 最大延时时间
314
+ */
272
315
  async randomDelay(min, max) {
273
316
  await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (max - min + 1) + min)));
274
317
  }
@@ -1,3 +1,9 @@
1
+ /**
2
+ * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/misc/sign/wbi.md#javascript
3
+ * 对实际请求参数进行 wbi 签名, 生成 wbi 签名
4
+ * @param {object} params 除了 wbi 签名外的全部请求参数,例如 api get请求的查询参数 { uid: 12345678, jsonp: jsonp}
5
+ * @param {object} headers 必需要 referer 和 UA 两个请求头
6
+ */
1
7
  export declare function getWbiSign(params: any, headers: any, cookie: string): Promise<{
2
8
  query: string;
3
9
  w_rid: string;
@@ -6,33 +6,40 @@ const mixinKeyEncTab = [
6
6
  61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
7
7
  36, 20, 34, 44, 52
8
8
  ];
9
+ // 对 imgKey 和 subKey 进行字符顺序打乱编码
9
10
  const getMixinKey = (orig) => mixinKeyEncTab
10
11
  .map((n) => orig[n])
11
12
  .join("")
12
13
  .slice(0, 32);
14
+ // 为请求参数进行 wbi 签名
13
15
  function encWbi(params, img_key, sub_key) {
14
16
  const mixin_key = getMixinKey(img_key + sub_key), curr_time = Math.round(Date.now() / 1000), chr_filter = /[!'()*]/g;
15
- Object.assign(params, { wts: curr_time });
17
+ Object.assign(params, { wts: curr_time }); // 添加 wts 字段
18
+ // 按照 key 重排参数
16
19
  const query = Object.keys(params)
17
20
  .sort()
18
21
  .map((key) => {
22
+ // 过滤 value 中的 "!'()*" 字符
19
23
  const value = params[key].toString().replace(chr_filter, "");
20
24
  return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
21
25
  })
22
26
  .join("&");
23
- const wbi_sign = md5(query + mixin_key);
27
+ const wbi_sign = md5(query + mixin_key); // 计算 w_rid
28
+ //return query + "&w_rid=" + wbi_sign;
24
29
  return {
25
30
  query: query,
26
31
  w_rid: wbi_sign,
27
32
  time_stamp: curr_time
28
33
  };
29
34
  }
35
+ // 获取最新的 img_key 和 sub_key
30
36
  async function getWbiKeys(headers, cookie) {
31
37
  const res = await fetch('https://api.bilibili.com/x/web-interface/nav', {
32
38
  headers: {
39
+ // SESSDATA 字段
33
40
  Cookie: cookie,
34
41
  'User-Agent': headers['User-Agent'],
35
- Referer: 'https://www.bilibili.com/'
42
+ Referer: 'https://www.bilibili.com/' //对于直接浏览器调用可能不适用
36
43
  }
37
44
  });
38
45
  const { data: { wbi_img: { img_url, sub_url }, }, } = (await res.json());
@@ -41,6 +48,12 @@ async function getWbiKeys(headers, cookie) {
41
48
  sub_key: sub_url.slice(sub_url.lastIndexOf('/') + 1, sub_url.lastIndexOf('.'))
42
49
  };
43
50
  }
51
+ /**
52
+ * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/misc/sign/wbi.md#javascript
53
+ * 对实际请求参数进行 wbi 签名, 生成 wbi 签名
54
+ * @param {object} params 除了 wbi 签名外的全部请求参数,例如 api get请求的查询参数 { uid: 12345678, jsonp: jsonp}
55
+ * @param {object} headers 必需要 referer 和 UA 两个请求头
56
+ */
44
57
  async function getWbiSign(params, headers, cookie) {
45
58
  const { img_key, sub_key } = await getWbiKeys(headers, cookie);
46
59
  return encWbi(params, img_key, sub_key);
@@ -3,5 +3,15 @@ export default class VersionData {
3
3
  cache: any;
4
4
  versionPath: string;
5
5
  constructor();
6
+ /**
7
+ * CHANGELOG.md内容支持示例:
8
+ * # 1.0.0
9
+ * * 新增功能3
10
+ * * 新增功能4
11
+ *
12
+ * # 0.1.0
13
+ * * 新增功能1
14
+ * * 新增功能2
15
+ */
6
16
  getChangelogContent(): Promise<any>;
7
17
  }
@@ -11,6 +11,16 @@ class VersionData {
11
11
  this.cache = {};
12
12
  this.versionPath = path.resolve(_paths.pluginPath, 'CHANGELOG.md');
13
13
  }
14
+ /**
15
+ * CHANGELOG.md内容支持示例:
16
+ * # 1.0.0
17
+ * * 新增功能3
18
+ * * 新增功能4
19
+ *
20
+ * # 0.1.0
21
+ * * 新增功能1
22
+ * * 新增功能2
23
+ */
14
24
  async getChangelogContent() {
15
25
  let key = this.model;
16
26
  if (this.cache[key])
@@ -42,6 +52,7 @@ class VersionData {
42
52
  data: currentData
43
53
  });
44
54
  }
55
+ // 对版本进行排序并截取最新的10个版本
45
56
  result.sort((a, b) => {
46
57
  let aParts = a.version.split('.').map(Number);
47
58
  let bParts = b.version.split('.').map(Number);
@@ -54,7 +65,7 @@ class VersionData {
54
65
  }
55
66
  return 0;
56
67
  });
57
- this.cache[key] = result.slice(0, 5);
68
+ this.cache[key] = result.slice(0, 10);
58
69
  return this.cache[key];
59
70
  }
60
71
  }