yz-yuki-plugin 2.0.1 → 2.0.2-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +8 -7
- package/lib/apps/bilibili.js +52 -20
- package/lib/apps/help.js +3 -0
- package/lib/apps/version.js +3 -0
- package/lib/apps/weibo.js +34 -16
- package/lib/components/dynamic/Account.js +2 -0
- package/lib/components/dynamic/Content.js +2 -0
- package/lib/components/dynamic/Footer.js +1 -0
- package/lib/components/dynamic/ForwardContent.js +2 -0
- package/lib/components/dynamic/LogoText.js +2 -0
- package/lib/components/dynamic/MainPage.js +1 -0
- package/lib/components/help/Help.js +1 -0
- package/lib/components/loginQrcode/Page.js +1 -0
- package/lib/index.js +53 -2
- package/lib/models/bilibili/bilibili.api.d.ts +3 -0
- package/lib/models/bilibili/bilibili.api.js +9 -0
- package/lib/models/bilibili/bilibili.get.web.data.d.ts +3 -0
- package/lib/models/bilibili/bilibili.get.web.data.js +4 -0
- package/lib/models/bilibili/bilibili.models.d.ts +37 -0
- package/lib/models/bilibili/bilibili.models.js +71 -19
- package/lib/models/bilibili/bilibili.query.d.ts +24 -0
- package/lib/models/bilibili/bilibili.query.js +69 -11
- package/lib/models/bilibili/bilibili.task.d.ts +41 -0
- package/lib/models/bilibili/bilibili.task.js +77 -34
- package/lib/models/bilibili/bilibili.wbi.d.ts +6 -0
- package/lib/models/bilibili/bilibili.wbi.js +16 -3
- package/lib/models/version/version.d.ts +10 -0
- package/lib/models/version/version.js +12 -1
- package/lib/models/weibo/weibo.api.d.ts +1 -0
- package/lib/models/weibo/weibo.api.js +2 -0
- package/lib/models/weibo/weibo.get.web.data.d.ts +3 -0
- package/lib/models/weibo/weibo.get.web.data.js +3 -0
- package/lib/models/weibo/weibo.query.d.ts +20 -0
- package/lib/models/weibo/weibo.query.js +63 -6
- package/lib/models/weibo/weibo.task.d.ts +49 -0
- package/lib/models/weibo/weibo.task.js +84 -34
- package/lib/utils/config.d.ts +50 -0
- package/lib/utils/config.js +55 -2
- package/lib/utils/image.d.ts +11 -0
- package/lib/utils/image.js +15 -0
- package/lib/utils/paths.js +7 -7
- package/lib/utils/puppeteer.render.d.ts +15 -0
- package/lib/utils/puppeteer.render.js +37 -15
- package/package.json +8 -7
|
@@ -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)
|
|
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 替换为 实现空格,\n 替换为 <br> 以实现换行
|
|
155
169
|
nodes = nodes.replace(/\t/g, ' ');
|
|
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}">​${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);
|
|
@@ -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,
|
|
68
|
+
this.cache[key] = result.slice(0, 10);
|
|
58
69
|
return this.cache[key];
|
|
59
70
|
}
|
|
60
71
|
}
|