yz-yuki-plugin 2.0.2 → 2.0.3-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.
- package/CHANGELOG.md +5 -1
- package/README.md +6 -5
- package/lib/apps/bilibili.js +50 -6
- package/lib/apps/help.js +3 -0
- package/lib/apps/version.js +3 -0
- package/lib/apps/weibo.js +31 -1
- 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 +20 -1
- 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 +11 -0
- 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 +51 -1
- package/lib/utils/config.js +61 -8
- 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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
class WeiboApi {
|
|
2
2
|
static WEIBO_API = {
|
|
3
3
|
weiboGetIndex: 'https://m.weibo.cn/api/container/getIndex',
|
|
4
|
+
//通过关键词${upKeyword}搜索博主 parama = { q: 'Keyword'},
|
|
4
5
|
weiboAjaxSearch: 'https://weibo.com/ajax/side/search',
|
|
5
6
|
};
|
|
7
|
+
/**统一设置header */
|
|
6
8
|
static WEIBO_HEADERS = {
|
|
7
9
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
|
|
8
10
|
'Accept-language': 'zh-CN,zh;q=0.9',
|
|
@@ -2,7 +2,10 @@ import { EventType } from 'yunzai';
|
|
|
2
2
|
export declare class WeiboGetWebData {
|
|
3
3
|
e?: EventType;
|
|
4
4
|
constructor(e?: EventType);
|
|
5
|
+
/**通过uid获取博主信息 */
|
|
5
6
|
getBloggerInfo(target: any): Promise<import("axios").AxiosResponse<any, any>>;
|
|
7
|
+
/**通过关键词搜索微博大v */
|
|
6
8
|
searchBloggerInfo(keyword: string): Promise<import("axios").AxiosResponse<any, any>>;
|
|
9
|
+
/**获取主页动态资源相关数组 */
|
|
7
10
|
getBloggerDynamicList(target: any): Promise<any>;
|
|
8
11
|
}
|
|
@@ -7,6 +7,7 @@ class WeiboGetWebData {
|
|
|
7
7
|
e;
|
|
8
8
|
constructor(e) {
|
|
9
9
|
}
|
|
10
|
+
/**通过uid获取博主信息 */
|
|
10
11
|
async getBloggerInfo(target) {
|
|
11
12
|
const param = { containerid: '100505' + target };
|
|
12
13
|
const url = new URL(WeiboApi.WEIBO_API.weiboGetIndex);
|
|
@@ -17,6 +18,7 @@ class WeiboGetWebData {
|
|
|
17
18
|
});
|
|
18
19
|
return resp;
|
|
19
20
|
}
|
|
21
|
+
/**通过关键词搜索微博大v */
|
|
20
22
|
async searchBloggerInfo(keyword) {
|
|
21
23
|
const url = WeiboApi.WEIBO_API.weiboAjaxSearch;
|
|
22
24
|
const params = {
|
|
@@ -29,6 +31,7 @@ class WeiboGetWebData {
|
|
|
29
31
|
});
|
|
30
32
|
return resp;
|
|
31
33
|
}
|
|
34
|
+
/**获取主页动态资源相关数组 */
|
|
32
35
|
async getBloggerDynamicList(target) {
|
|
33
36
|
const params = { containerid: '107603' + target };
|
|
34
37
|
const url = new URL(WeiboApi.WEIBO_API.weiboGetIndex);
|
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
export declare class WeiboQuery {
|
|
2
|
+
/**获取文章id */
|
|
2
3
|
static getDynamicId(post: any): any;
|
|
4
|
+
/**获取指定动态类型的原始数据 */
|
|
3
5
|
static filterCardTypeCustom(raw_post: any): boolean;
|
|
6
|
+
/**转换微博动态创建时间:(created_at)转换为 UNIX 时间戳(以毫秒为单位) */
|
|
4
7
|
static getDynamicCreatetDate(raw_post: any): number;
|
|
8
|
+
/**分类动态,返回标识 */
|
|
5
9
|
static MakeCategory(raw_post: any): "DYNAMIC_TYPE_AV" | "DYNAMIC_TYPE_DRAW" | "DYNAMIC_TYPE_ARTICLE" | "DYNAMIC_TYPE_FORWARD";
|
|
10
|
+
/**筛选正文 */
|
|
6
11
|
static filterText(raw_text: string): string;
|
|
12
|
+
/** 获取并生成微博动态渲染数据 */
|
|
7
13
|
static formatDynamicData(raw_post: any): Promise<{
|
|
8
14
|
uid: any;
|
|
9
15
|
data: {
|
|
10
16
|
[key: string]: any;
|
|
11
17
|
};
|
|
12
18
|
}>;
|
|
19
|
+
/**
|
|
20
|
+
* 动态内容富文本节点解析
|
|
21
|
+
* @param nodes - 动态内容富文本节点
|
|
22
|
+
* @returns 解析后的动态内容富文本
|
|
23
|
+
*/
|
|
13
24
|
static parseRichTextNodes: (nodes: any[] | string | any) => any;
|
|
25
|
+
/**
|
|
26
|
+
* 生成动态消息文字内容
|
|
27
|
+
* @param upName - UP主名称
|
|
28
|
+
* @param formatData - 动态数据
|
|
29
|
+
* @param isForward - 是否为转发动态
|
|
30
|
+
* @param setData - 设置数据
|
|
31
|
+
* @returns 生成的动态消息文字内容
|
|
32
|
+
*/
|
|
14
33
|
static formatTextDynamicData(upName: string, raw_post: any, isForward?: boolean, setData?: any): Promise<false | any[] | "continue">;
|
|
15
34
|
static dynamicContentLimit(content: string, setData: any): string;
|
|
16
35
|
static formatUrl(url: string): string;
|
|
36
|
+
/**推送类型设置 */
|
|
17
37
|
static typeHandle(up: any, msg: string, type: string): unknown[];
|
|
18
38
|
}
|
|
@@ -5,16 +5,20 @@ import { Bot, Segment } from 'yunzai';
|
|
|
5
5
|
import { JSDOM } from 'jsdom';
|
|
6
6
|
|
|
7
7
|
class WeiboQuery {
|
|
8
|
+
/**获取文章id */
|
|
8
9
|
static getDynamicId(post) {
|
|
9
10
|
return post?.mblog?.mid || post?.mblog?.id;
|
|
10
11
|
}
|
|
12
|
+
/**获取指定动态类型的原始数据 */
|
|
11
13
|
static filterCardTypeCustom(raw_post) {
|
|
12
14
|
return raw_post.card_type === 9;
|
|
13
15
|
}
|
|
16
|
+
/**转换微博动态创建时间:(created_at)转换为 UNIX 时间戳(以毫秒为单位) */
|
|
14
17
|
static getDynamicCreatetDate(raw_post) {
|
|
15
18
|
const created_time = Date.parse(raw_post?.mblog?.created_at || raw_post?.created_at);
|
|
16
19
|
return created_time;
|
|
17
20
|
}
|
|
21
|
+
/**分类动态,返回标识 */
|
|
18
22
|
static MakeCategory(raw_post) {
|
|
19
23
|
if (raw_post?.mblog?.retweeted_status) {
|
|
20
24
|
return "DYNAMIC_TYPE_FORWARD";
|
|
@@ -29,12 +33,15 @@ class WeiboQuery {
|
|
|
29
33
|
return "DYNAMIC_TYPE_ARTICLE";
|
|
30
34
|
}
|
|
31
35
|
}
|
|
36
|
+
/**筛选正文 */
|
|
32
37
|
static filterText(raw_text) {
|
|
33
38
|
const text = raw_text.replace(/<br \/>/g, '\n');
|
|
34
39
|
const dom = new JSDOM(text);
|
|
35
40
|
return dom.window.document.body.textContent || '';
|
|
36
41
|
}
|
|
42
|
+
/** 获取并生成微博动态渲染数据 */
|
|
37
43
|
static async formatDynamicData(raw_post) {
|
|
44
|
+
/* 初始数据进一步处理 **************** */
|
|
38
45
|
let info = raw_post?.mblog || raw_post;
|
|
39
46
|
let retweeted = info && info?.retweeted_status ? true : false;
|
|
40
47
|
let pic_num = retweeted ? info?.retweeted_status?.pic_num : info?.pic_num;
|
|
@@ -53,14 +60,22 @@ class WeiboQuery {
|
|
|
53
60
|
logger?.error(`优纪插件:微博 detail message error(https://m.weibo.cn/detail/${info?.mid})`);
|
|
54
61
|
}
|
|
55
62
|
}
|
|
63
|
+
/**头像链接 */
|
|
56
64
|
const face_url = info?.user?.profile_image_url;
|
|
65
|
+
/**昵称 */
|
|
57
66
|
const nick_name = info?.user?.screen_name;
|
|
67
|
+
/**动态发布时间 */
|
|
58
68
|
let created_time = this.getDynamicCreatetDate(raw_post);
|
|
69
|
+
/**动态详情链接 */
|
|
59
70
|
let detail_url = `https://weibo.com/${info?.user?.id}/${info?.bid}`;
|
|
71
|
+
/* 构造动态渲染数据 *************************** */
|
|
60
72
|
let pics = [];
|
|
61
73
|
let formatData = { data: {} };
|
|
74
|
+
/**头像 */
|
|
62
75
|
formatData.data.face = face_url;
|
|
76
|
+
/**昵称 */
|
|
63
77
|
formatData.data.name = nick_name;
|
|
78
|
+
/**头像框 */
|
|
64
79
|
formatData.data.pendant = '';
|
|
65
80
|
formatData.data.created = moment().format("YYYY年MM月DD日 HH:mm:ss");
|
|
66
81
|
formatData.data.type = type;
|
|
@@ -110,33 +125,60 @@ class WeiboQuery {
|
|
|
110
125
|
};
|
|
111
126
|
}
|
|
112
127
|
;
|
|
128
|
+
/**
|
|
129
|
+
* 动态内容富文本节点解析
|
|
130
|
+
* @param nodes - 动态内容富文本节点
|
|
131
|
+
* @returns 解析后的动态内容富文本
|
|
132
|
+
*/
|
|
113
133
|
static parseRichTextNodes = (nodes) => {
|
|
114
134
|
if (typeof nodes === 'string') {
|
|
135
|
+
// 将 \n 替换为 <br> 以实现换行
|
|
115
136
|
let parsedContent = nodes.replace(/\n/g, '<br>');
|
|
137
|
+
// 使用正则表达式查找所有的 <a> 标签
|
|
116
138
|
parsedContent = parsedContent.replace(/<a/g, () => {
|
|
139
|
+
// 生成一个随机的 key 值
|
|
117
140
|
const randomKey = Math.random().toString(36).substring(7);
|
|
118
141
|
return `<a key="${randomKey}"`;
|
|
119
142
|
});
|
|
120
143
|
parsedContent = parsedContent.replace(/class="url-icon"/g, () => {
|
|
144
|
+
// 生成一个随机的 key 值
|
|
121
145
|
const randomKey = Math.random().toString(36).substring(7);
|
|
122
146
|
return `class="url-icon ${randomKey}"`;
|
|
123
147
|
});
|
|
148
|
+
// 使用正则表达式查找所有的 <img> 标签
|
|
124
149
|
parsedContent = parsedContent.replace(/<img/g, () => {
|
|
150
|
+
// 生成一个随机的 key 值
|
|
125
151
|
const randomKey = Math.random().toString(36).substring(7);
|
|
126
152
|
return `<img key="${randomKey}"`;
|
|
127
153
|
});
|
|
128
154
|
return parsedContent;
|
|
129
155
|
}
|
|
130
156
|
else {
|
|
157
|
+
// 未知类型,直接返回
|
|
131
158
|
return nodes;
|
|
132
159
|
}
|
|
133
160
|
};
|
|
161
|
+
/**
|
|
162
|
+
* 生成动态消息文字内容
|
|
163
|
+
* @param upName - UP主名称
|
|
164
|
+
* @param formatData - 动态数据
|
|
165
|
+
* @param isForward - 是否为转发动态
|
|
166
|
+
* @param setData - 设置数据
|
|
167
|
+
* @returns 生成的动态消息文字内容
|
|
168
|
+
*/
|
|
134
169
|
static async formatTextDynamicData(upName, raw_post, isForward, setData) {
|
|
135
|
-
let msg = [],
|
|
170
|
+
let msg = [],
|
|
171
|
+
/**全部图片资源链接*/
|
|
172
|
+
raw_pics_list,
|
|
173
|
+
/**图片高清资源链接*/
|
|
174
|
+
pic_urls,
|
|
175
|
+
/**图片*/
|
|
176
|
+
pics;
|
|
136
177
|
let info = raw_post?.mblog || raw_post;
|
|
137
|
-
let retweeted = info && info.retweeted_status ? true : false;
|
|
178
|
+
let retweeted = info && info.retweeted_status ? true : false; //是否为转发动态
|
|
138
179
|
let pic_num = retweeted ? info?.retweeted_status?.pic_num : info?.pic_num;
|
|
139
180
|
let type = this.MakeCategory(raw_post);
|
|
181
|
+
/**获取动态全文 */
|
|
140
182
|
if (info?.isLongText || pic_num > 9) {
|
|
141
183
|
const res = await fetch(`https://m.weibo.cn/detail/${info.mid}`, { headers: WeiboApi.WEIBO_HEADERS });
|
|
142
184
|
try {
|
|
@@ -151,6 +193,7 @@ class WeiboQuery {
|
|
|
151
193
|
(logger ?? Bot.logger)?.mark(`优纪插件:获取微博动态全文出错:https://m.weibo.cn/detail/${info?.mid}`);
|
|
152
194
|
}
|
|
153
195
|
}
|
|
196
|
+
/**动态发布时间 */
|
|
154
197
|
let created_time = this.getDynamicCreatetDate(raw_post);
|
|
155
198
|
let detail_url = `https://weibo.com/${info?.user?.id}/${info?.bid}`;
|
|
156
199
|
let title = `微博【${upName}】动态推送:\n`;
|
|
@@ -254,10 +297,12 @@ class WeiboQuery {
|
|
|
254
297
|
return "continue";
|
|
255
298
|
}
|
|
256
299
|
}
|
|
300
|
+
// 限制文字模式下动态内容的字数和行数
|
|
257
301
|
static dynamicContentLimit(content, setData) {
|
|
258
302
|
const lines = content.split("\n");
|
|
259
303
|
const lengthLimit = setData.pushContentLenLimit || 100;
|
|
260
304
|
const lineLimit = setData.pushContentLineLimit || 5;
|
|
305
|
+
// 限制行数
|
|
261
306
|
if (lines.length > lineLimit) {
|
|
262
307
|
lines.length = lineLimit;
|
|
263
308
|
}
|
|
@@ -278,10 +323,13 @@ class WeiboQuery {
|
|
|
278
323
|
}
|
|
279
324
|
return lines.join("\n");
|
|
280
325
|
}
|
|
326
|
+
// 处理斜杠开头的url
|
|
281
327
|
static formatUrl(url) {
|
|
282
328
|
return 0 == url.indexOf('//') ? `https:${url}` : url;
|
|
283
329
|
}
|
|
330
|
+
/**推送类型设置 */
|
|
284
331
|
static typeHandle(up, msg, type) {
|
|
332
|
+
// 定义一个对象映射,将关键字映射到对应的类型
|
|
285
333
|
const typeMap = {
|
|
286
334
|
"直播": "DYNAMIC_TYPE_LIVE_RCMD",
|
|
287
335
|
"转发": "DYNAMIC_TYPE_FORWARD",
|
|
@@ -289,33 +337,42 @@ class WeiboQuery {
|
|
|
289
337
|
"图文": ["DYNAMIC_TYPE_DRAW", "DYNAMIC_TYPE_WORD"],
|
|
290
338
|
"视频": "DYNAMIC_TYPE_AV"
|
|
291
339
|
};
|
|
340
|
+
// 初始化新的类型集合,如果 up.type 存在则使用它,否则使用空数组
|
|
292
341
|
let newType = new Set(up.type || []);
|
|
342
|
+
// 定义一个处理类型的函数,根据传入的 action 参数决定是添加还是删除类型
|
|
293
343
|
const handleType = (action) => {
|
|
294
|
-
let isHandled = false;
|
|
344
|
+
let isHandled = false; // 标记是否有类型被处理
|
|
345
|
+
// 遍历 typeMap 对象,根据 msg 中的关键字进行类型操作
|
|
295
346
|
for (const [key, value] of Object.entries(typeMap)) {
|
|
296
347
|
if (msg.indexOf(key) !== -1) {
|
|
297
348
|
if (Array.isArray(value)) {
|
|
349
|
+
// 如果 value 是数组,则对数组中的每个元素进行操作
|
|
298
350
|
value.forEach(v => action === "add" ? newType.add(v) : newType.delete(v));
|
|
299
351
|
}
|
|
300
352
|
else {
|
|
353
|
+
// 否则直接对单个值进行操作
|
|
301
354
|
action === "add" ? newType.add(value) : newType.delete(value);
|
|
302
355
|
}
|
|
303
|
-
isHandled = true;
|
|
356
|
+
isHandled = true; // 标记有类型被处理
|
|
304
357
|
}
|
|
305
358
|
}
|
|
306
|
-
return isHandled;
|
|
359
|
+
return isHandled; // 返回是否有类型被处理
|
|
307
360
|
};
|
|
361
|
+
// 根据 type 参数决定是添加还是删除类型
|
|
308
362
|
if (type === "add") {
|
|
309
|
-
handleType("add");
|
|
363
|
+
handleType("add"); // 调用 handleType 函数进行类型添加
|
|
310
364
|
}
|
|
311
365
|
else if (type === "del") {
|
|
312
366
|
if (!newType.size) {
|
|
367
|
+
// 如果 newType 为空,则初始化它为所有可能的类型
|
|
313
368
|
newType = new Set(Object.values(typeMap).flat());
|
|
314
369
|
}
|
|
370
|
+
// 调用 handleType 函数进行类型删除,如果没有类型被删除则清空 newType
|
|
315
371
|
if (!handleType("delete")) {
|
|
316
372
|
newType.clear();
|
|
317
373
|
}
|
|
318
374
|
}
|
|
375
|
+
// 将 newType 转换为数组并返回
|
|
319
376
|
return Array.from(newType);
|
|
320
377
|
}
|
|
321
378
|
}
|
|
@@ -8,11 +8,60 @@ export declare class WeiboTask {
|
|
|
8
8
|
e?: EventType;
|
|
9
9
|
constructor(e?: any);
|
|
10
10
|
runTask(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* 处理微博数据,获取动态列表并构建 uid 映射
|
|
13
|
+
* @param weiboPushData 微博推送数据
|
|
14
|
+
* @param uidMap uid 映射
|
|
15
|
+
* @param dynamicList 动态列表
|
|
16
|
+
*/
|
|
11
17
|
processWeiboData(weiboPushData: any, uidMap: Map<any, Map<string, any>>, dynamicList: any): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* 推送动态消息
|
|
20
|
+
* @param uidMap uid 映射
|
|
21
|
+
* @param dynamicList 动态列表
|
|
22
|
+
* @param now 当前时间戳
|
|
23
|
+
* @param interval 推送间隔时间
|
|
24
|
+
* @param weiboConfigData 微博配置数据
|
|
25
|
+
*/
|
|
12
26
|
pushDynamicMessages(uidMap: Map<any, Map<string, any>>, dynamicList: any, now: number, interval: number, weiboConfigData: any): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* 发送动态消息
|
|
29
|
+
* @param chatId 聊天 ID
|
|
30
|
+
* @param bot_id 机器人 ID
|
|
31
|
+
* @param upName 用户名
|
|
32
|
+
* @param pushDynamicData 推送动态数据
|
|
33
|
+
* @param weiboConfigData 微博配置数据
|
|
34
|
+
* @param chatType 聊天类型
|
|
35
|
+
*/
|
|
13
36
|
sendDynamic(chatId: string | number, bot_id: string | number, upName: string, pushDynamicData: any, weiboConfigData: any, chatType: string): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* 构建渲染数据
|
|
39
|
+
* @param extentData 扩展数据
|
|
40
|
+
* @param urlQrcodeData URL 二维码数据
|
|
41
|
+
* @param boxGrid 是否启用九宫格样式
|
|
42
|
+
* @returns 渲染数据
|
|
43
|
+
*/
|
|
14
44
|
buildRenderData(extentData: any, urlQrcodeData: string, boxGrid: boolean): MainProps;
|
|
45
|
+
/**
|
|
46
|
+
* 渲染动态卡片
|
|
47
|
+
* @param uid 用户 ID
|
|
48
|
+
* @param renderData 渲染数据
|
|
49
|
+
* @param ScreenshotOptionsData 截图选项数据
|
|
50
|
+
* @returns 图片数据
|
|
51
|
+
*/
|
|
15
52
|
renderDynamicCard(uid: string | number, renderData: MainProps, ScreenshotOptionsData: ScreenshotOptions): Promise<Buffer[] | null>;
|
|
53
|
+
/**
|
|
54
|
+
* 发送消息
|
|
55
|
+
* @param chatId 聊天 ID
|
|
56
|
+
* @param bot_id 机器人 ID
|
|
57
|
+
* @param chatType 聊天类型
|
|
58
|
+
* @param message 消息内容
|
|
59
|
+
*/
|
|
16
60
|
sendMessage(chatId: string | number, bot_id: string | number, chatType: string, message: any): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* 随机延时
|
|
63
|
+
* @param min 最小延时时间
|
|
64
|
+
* @param max 最大延时时间
|
|
65
|
+
*/
|
|
17
66
|
randomDelay(min: number, max: number): Promise<void>;
|
|
18
67
|
}
|
|
@@ -18,23 +18,29 @@ class WeiboTask {
|
|
|
18
18
|
async runTask() {
|
|
19
19
|
let weiboConfigData = await Config.getUserConfig("weibo", "config");
|
|
20
20
|
let weiboPushData = await Config.getUserConfig("weibo", "push");
|
|
21
|
-
let interval = weiboConfigData.interval || 7200;
|
|
22
|
-
const uidMap = new Map();
|
|
23
|
-
const dynamicList = {};
|
|
21
|
+
let interval = weiboConfigData.interval || 7200; // 推送间隔时间,单位为秒,默认2小时
|
|
22
|
+
const uidMap = new Map(); // 存放group 和 private 对应所属 uid 与推送信息的映射
|
|
23
|
+
const dynamicList = {}; // 存放获取的所有动态,键为 uid,值为动态数组
|
|
24
24
|
await this.processWeiboData(weiboPushData, uidMap, dynamicList);
|
|
25
|
-
let now = Date.now() / 1000;
|
|
25
|
+
let now = Date.now() / 1000; // 当前时间戳(秒)
|
|
26
26
|
await this.pushDynamicMessages(uidMap, dynamicList, now, interval, weiboConfigData);
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* 处理微博数据,获取动态列表并构建 uid 映射
|
|
30
|
+
* @param weiboPushData 微博推送数据
|
|
31
|
+
* @param uidMap uid 映射
|
|
32
|
+
* @param dynamicList 动态列表
|
|
33
|
+
*/
|
|
28
34
|
async processWeiboData(weiboPushData, uidMap, dynamicList) {
|
|
29
|
-
for (let chatType in weiboPushData) {
|
|
35
|
+
for (let chatType in weiboPushData) { // 遍历 group 和 private
|
|
30
36
|
if (!uidMap.has(chatType)) {
|
|
31
37
|
uidMap.set(chatType, new Map());
|
|
32
38
|
}
|
|
33
|
-
const chatTypeMap = uidMap.get(chatType);
|
|
39
|
+
const chatTypeMap = uidMap.get(chatType); // 建立当前 chatType (group 或 private) 的 uid 映射
|
|
34
40
|
for (let chatId in weiboPushData[chatType]) {
|
|
35
41
|
const subUpsOfChat = weiboPushData[chatType][chatId] || [];
|
|
36
42
|
for (let subInfoOfup of subUpsOfChat) {
|
|
37
|
-
const resp = await new WeiboGetWebData().getBloggerDynamicList(subInfoOfup.uid);
|
|
43
|
+
const resp = await new WeiboGetWebData().getBloggerDynamicList(subInfoOfup.uid); // 获取指定 uid 的动态列表
|
|
38
44
|
if (resp) {
|
|
39
45
|
const dynamicData = resp || [];
|
|
40
46
|
dynamicList[subInfoOfup.uid] = dynamicData;
|
|
@@ -43,17 +49,25 @@ class WeiboTask {
|
|
|
43
49
|
const bot_id = subInfoOfup.bot_id || [];
|
|
44
50
|
const { name, type } = subInfoOfup;
|
|
45
51
|
chatTypeMap.set(subInfoOfup.uid, { chatIds, bot_id, upName: name, type });
|
|
46
|
-
await this.randomDelay(1000, 4000);
|
|
52
|
+
await this.randomDelay(1000, 4000); // 随机延时1-4秒
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* 推送动态消息
|
|
59
|
+
* @param uidMap uid 映射
|
|
60
|
+
* @param dynamicList 动态列表
|
|
61
|
+
* @param now 当前时间戳
|
|
62
|
+
* @param interval 推送间隔时间
|
|
63
|
+
* @param weiboConfigData 微博配置数据
|
|
64
|
+
*/
|
|
51
65
|
async pushDynamicMessages(uidMap, dynamicList, now, interval, weiboConfigData) {
|
|
52
66
|
for (let [chatType, chatTypeMap] of uidMap) {
|
|
53
67
|
for (let [key, value] of chatTypeMap) {
|
|
54
68
|
const tempDynamicList = dynamicList[key] || [];
|
|
55
69
|
const willPushDynamicList = [];
|
|
56
|
-
const printedList = new Set();
|
|
70
|
+
const printedList = new Set(); // 已打印的动态列表
|
|
57
71
|
for (let dynamicItem of tempDynamicList) {
|
|
58
72
|
let raw_post = dynamicItem || {};
|
|
59
73
|
let user = raw_post?.mblog?.user || {};
|
|
@@ -66,29 +80,39 @@ class WeiboTask {
|
|
|
66
80
|
if (Number(now - (WeiboQuery.getDynamicCreatetDate(raw_post) / 1000)) > interval) {
|
|
67
81
|
logger.debug(`超过间隔,跳过 [ ${user?.screen_name} : ${user?.id} ] ${raw_post?.mblog?.created_at} 的动态`);
|
|
68
82
|
continue;
|
|
69
|
-
}
|
|
83
|
+
} // 如果超过推送时间间隔,跳过当前循环
|
|
70
84
|
if (dynamicItem.type === "DYNAMIC_TYPE_FORWARD" && !weiboConfigData.pushTransmit)
|
|
71
|
-
continue;
|
|
85
|
+
continue; // 如果关闭了转发动态的推送,跳过当前循环
|
|
72
86
|
willPushDynamicList.push(dynamicItem);
|
|
73
87
|
}
|
|
74
88
|
printedList.clear();
|
|
75
|
-
const pushMapInfo = value || {};
|
|
89
|
+
const pushMapInfo = value || {}; // 获取当前 uid 对应的推送信息
|
|
76
90
|
const { chatIds, bot_id, upName, type } = pushMapInfo;
|
|
91
|
+
// 遍历待推送的动态数组,发送动态消息
|
|
77
92
|
for (let pushDynamicData of willPushDynamicList) {
|
|
78
93
|
if (chatIds && chatIds.length) {
|
|
79
94
|
for (let chatId of chatIds) {
|
|
80
95
|
if (type && type.length && !type.includes(pushDynamicData.type))
|
|
81
|
-
continue;
|
|
82
|
-
await this.sendDynamic(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType);
|
|
83
|
-
await this.randomDelay(2000, 10500);
|
|
96
|
+
continue; // 如果禁用了某类型的动态推送,跳过当前循环
|
|
97
|
+
await this.sendDynamic(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType); // 发送动态消息
|
|
98
|
+
await this.randomDelay(2000, 10500); // 随机延时2-10.5秒
|
|
84
99
|
}
|
|
85
100
|
}
|
|
86
101
|
}
|
|
87
102
|
}
|
|
88
103
|
}
|
|
89
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* 发送动态消息
|
|
107
|
+
* @param chatId 聊天 ID
|
|
108
|
+
* @param bot_id 机器人 ID
|
|
109
|
+
* @param upName 用户名
|
|
110
|
+
* @param pushDynamicData 推送动态数据
|
|
111
|
+
* @param weiboConfigData 微博配置数据
|
|
112
|
+
* @param chatType 聊天类型
|
|
113
|
+
*/
|
|
90
114
|
async sendDynamic(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType) {
|
|
91
|
-
const id_str = WeiboQuery.getDynamicId(pushDynamicData);
|
|
115
|
+
const id_str = WeiboQuery.getDynamicId(pushDynamicData); // 获取动态 ID
|
|
92
116
|
let sended, markKey;
|
|
93
117
|
if (chatType === "group") {
|
|
94
118
|
markKey = this.groupKey;
|
|
@@ -99,17 +123,17 @@ class WeiboTask {
|
|
|
99
123
|
sended = await Redis.get(`${markKey}${chatId}:${id_str}`);
|
|
100
124
|
}
|
|
101
125
|
if (sended)
|
|
102
|
-
return;
|
|
126
|
+
return; // 如果已经发送过,则直接返回
|
|
103
127
|
if (!!weiboConfigData.pushMsgMode) {
|
|
104
|
-
const { data, uid } = await WeiboQuery.formatDynamicData(pushDynamicData);
|
|
128
|
+
const { data, uid } = await WeiboQuery.formatDynamicData(pushDynamicData); // 处理动态数据
|
|
105
129
|
const eval2 = eval;
|
|
106
|
-
let banWords = eval2(`/${weiboConfigData.banWords.join("|")}/g`);
|
|
130
|
+
let banWords = eval2(`/${weiboConfigData.banWords.join("|")}/g`); // 构建屏蔽关键字正则表达式
|
|
107
131
|
if (new RegExp(banWords).test(`${data?.title}${data?.content}`)) {
|
|
108
|
-
return "return";
|
|
132
|
+
return "return"; // 如果动态包含屏蔽关键字,则直接返回
|
|
109
133
|
}
|
|
110
|
-
let boxGrid = !!weiboConfigData.boxGrid === false ? false : true;
|
|
111
|
-
let isSplit = !!weiboConfigData.isSplit === false ? false : true;
|
|
112
|
-
let style = isSplit ? '' : '.unfold { height: 7500px; }';
|
|
134
|
+
let boxGrid = !!weiboConfigData.boxGrid === false ? false : true; // 是否启用九宫格样式,默认为 true
|
|
135
|
+
let isSplit = !!weiboConfigData.isSplit === false ? false : true; // 是否启用分片截图,默认为 true
|
|
136
|
+
let style = isSplit ? '' : '.unfold { height: 7500px; }'; // 不启用分片截图模式的样式
|
|
113
137
|
const extentData = { ...data };
|
|
114
138
|
const urlQrcodeData = await QRCode.toDataURL(extentData?.url);
|
|
115
139
|
let renderData = this.buildRenderData(extentData, urlQrcodeData, boxGrid);
|
|
@@ -127,31 +151,38 @@ class WeiboTask {
|
|
|
127
151
|
let imgs = await this.renderDynamicCard(uid, renderData, ScreenshotOptionsData);
|
|
128
152
|
if (!imgs)
|
|
129
153
|
return;
|
|
130
|
-
Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 });
|
|
154
|
+
Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 }); // 设置已发送标记
|
|
131
155
|
(logger ?? Bot.logger)?.mark("优纪插件:B站动态执行推送");
|
|
132
156
|
for (let i = 0; i < imgs.length; i++) {
|
|
133
157
|
const image = imgs[i];
|
|
134
158
|
await this.sendMessage(chatId, bot_id, chatType, Segment.image(image));
|
|
135
|
-
await this.randomDelay(2000, 6500);
|
|
159
|
+
await this.randomDelay(2000, 6500); // 随机延时2-6.5秒
|
|
136
160
|
}
|
|
137
161
|
await this.randomDelay(1000, 2000);
|
|
138
162
|
}
|
|
139
163
|
else {
|
|
140
|
-
const dynamicMsg = await WeiboQuery.formatTextDynamicData(upName, pushDynamicData, false, weiboConfigData);
|
|
141
|
-
Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 });
|
|
164
|
+
const dynamicMsg = await WeiboQuery.formatTextDynamicData(upName, pushDynamicData, false, weiboConfigData); //构建文字动态消息
|
|
165
|
+
Redis.set(`${markKey}${chatId}:${id_str}`, "1", { EX: 3600 * 10 }); // 设置已发送标记
|
|
142
166
|
if (dynamicMsg == "continue" || dynamicMsg == false) {
|
|
143
|
-
return "return";
|
|
167
|
+
return "return"; // 如果动态消息构建失败或内部资源获取失败,则直接返回
|
|
144
168
|
}
|
|
145
169
|
if (weiboConfigData.banWords.length > 0) {
|
|
146
|
-
const banWords = new RegExp(weiboConfigData.banWords.join("|"), "g");
|
|
170
|
+
const banWords = new RegExp(weiboConfigData.banWords.join("|"), "g"); // 构建屏蔽关键字正则表达式
|
|
147
171
|
if (banWords.test(dynamicMsg.join(""))) {
|
|
148
|
-
return "return";
|
|
172
|
+
return "return"; // 如果动态消息包含屏蔽关键字,则直接返回
|
|
149
173
|
}
|
|
150
174
|
}
|
|
151
175
|
await this.sendMessage(chatId, bot_id, chatType, dynamicMsg);
|
|
152
176
|
await this.randomDelay(1000, 2000);
|
|
153
177
|
}
|
|
154
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* 构建渲染数据
|
|
181
|
+
* @param extentData 扩展数据
|
|
182
|
+
* @param urlQrcodeData URL 二维码数据
|
|
183
|
+
* @param boxGrid 是否启用九宫格样式
|
|
184
|
+
* @returns 渲染数据
|
|
185
|
+
*/
|
|
155
186
|
buildRenderData(extentData, urlQrcodeData, boxGrid) {
|
|
156
187
|
if (extentData.orig && (extentData.orig).length !== 0) {
|
|
157
188
|
return {
|
|
@@ -205,18 +236,32 @@ class WeiboTask {
|
|
|
205
236
|
};
|
|
206
237
|
}
|
|
207
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* 渲染动态卡片
|
|
241
|
+
* @param uid 用户 ID
|
|
242
|
+
* @param renderData 渲染数据
|
|
243
|
+
* @param ScreenshotOptionsData 截图选项数据
|
|
244
|
+
* @returns 图片数据
|
|
245
|
+
*/
|
|
208
246
|
async renderDynamicCard(uid, renderData, ScreenshotOptionsData) {
|
|
209
|
-
const dynamicMsg = await Image.renderPage(uid, "MainPage", renderData, ScreenshotOptionsData);
|
|
247
|
+
const dynamicMsg = await Image.renderPage(uid, "MainPage", renderData, ScreenshotOptionsData); // 渲染动态卡片
|
|
210
248
|
if (dynamicMsg !== false) {
|
|
211
|
-
return dynamicMsg.img;
|
|
249
|
+
return dynamicMsg.img; // 缓存图片数据
|
|
212
250
|
}
|
|
213
251
|
else {
|
|
214
252
|
return null;
|
|
215
253
|
}
|
|
216
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* 发送消息
|
|
257
|
+
* @param chatId 聊天 ID
|
|
258
|
+
* @param bot_id 机器人 ID
|
|
259
|
+
* @param chatType 聊天类型
|
|
260
|
+
* @param message 消息内容
|
|
261
|
+
*/
|
|
217
262
|
async sendMessage(chatId, bot_id, chatType, message) {
|
|
218
263
|
if (chatType === "group") {
|
|
219
|
-
await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message)
|
|
264
|
+
await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message) // 发送群聊
|
|
220
265
|
.catch((error) => {
|
|
221
266
|
(logger ?? Bot.logger)?.error(`群组[${chatId}]推送失败:${JSON.stringify(error)}`);
|
|
222
267
|
});
|
|
@@ -225,9 +270,14 @@ class WeiboTask {
|
|
|
225
270
|
await (Bot[bot_id] ?? Bot)?.pickFriend(String(chatId)).sendMsg(message)
|
|
226
271
|
.catch((error) => {
|
|
227
272
|
(logger ?? Bot.logger)?.error(`用户[${chatId}]推送失败:${JSON.stringify(error)}`);
|
|
228
|
-
});
|
|
273
|
+
}); // 发送好友私聊
|
|
229
274
|
}
|
|
230
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* 随机延时
|
|
278
|
+
* @param min 最小延时时间
|
|
279
|
+
* @param max 最大延时时间
|
|
280
|
+
*/
|
|
231
281
|
async randomDelay(min, max) {
|
|
232
282
|
await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (max - min + 1) + min)));
|
|
233
283
|
}
|
package/lib/utils/config.d.ts
CHANGED
|
@@ -1,20 +1,70 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
|
+
/**
|
|
3
|
+
* Config 类用于管理配置文件的读取和监听
|
|
4
|
+
*/
|
|
2
5
|
declare class Config {
|
|
3
|
-
readonly
|
|
6
|
+
readonly packageJsonPath: string;
|
|
4
7
|
readonly defaultConfigPath: string;
|
|
5
8
|
readonly userConfigPath: string;
|
|
6
9
|
defaultConfig: Record<string, any>;
|
|
7
10
|
userConfig: Record<string, any>;
|
|
8
11
|
watcher: Record<string, chokidar.FSWatcher>;
|
|
9
12
|
constructor();
|
|
13
|
+
/** 操作并创建配置文件到指定目录 */
|
|
10
14
|
initConfigFiles(): void;
|
|
15
|
+
/**
|
|
16
|
+
* 通用获取配置文件数据方法
|
|
17
|
+
* @param typeDir 配置文件目录类型对应路径 defaultConfig: defaultConfig 或 config: yunzai/data/yuki-plugin/config
|
|
18
|
+
* @param appDir 配置app目录
|
|
19
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
20
|
+
* @returns {object} 配置数据
|
|
21
|
+
*/
|
|
11
22
|
getConfigData(typeDir: string, appDir: string, functionName: string): any;
|
|
23
|
+
/**
|
|
24
|
+
* 获取配置文件路径
|
|
25
|
+
* @param typeDir 配置文件目录类型对应路径 defaultConfig: defaultConfig 或 config: yunzai/data/yuki-plugin/config
|
|
26
|
+
* @param appDir 配置app目录
|
|
27
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
28
|
+
* @returns {string} 配置文件路径
|
|
29
|
+
*/
|
|
12
30
|
getConfigFilePath(typeDir: string, appDir: string, functionName: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* 监听配置文件的变化
|
|
33
|
+
* @param configFilePath 文件路径
|
|
34
|
+
* @param typeDir 配置文件目录类型对应路径 defaultConfig: defaultConfig 或 config: yunzai/data/yuki-plugin/config
|
|
35
|
+
* @param appDir 配置app目录
|
|
36
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
37
|
+
*/
|
|
13
38
|
watch(configFilePath: string, typeDir: string, appDir: string, functionName: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* 获取默认配置
|
|
41
|
+
* @param appDir 配置app目录
|
|
42
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
43
|
+
*/
|
|
14
44
|
getDefaultConfig(appDir: string, functionName: string): any;
|
|
45
|
+
/**
|
|
46
|
+
* 获取用户配置
|
|
47
|
+
* @param appDir 配置app目录
|
|
48
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
49
|
+
*/
|
|
15
50
|
getUserConfig(appDir: string, functionName: string): any;
|
|
51
|
+
/**
|
|
52
|
+
* 保存配置文件
|
|
53
|
+
* @param typeDir 插件为起始的配置文件目录
|
|
54
|
+
* @param appDir 配置app目录
|
|
55
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
56
|
+
* @param data 配置数据
|
|
57
|
+
*/
|
|
16
58
|
saveConfig(typeDir: string, appDir: string, functionName: string, data: any): void;
|
|
59
|
+
/**
|
|
60
|
+
* 更新并保存配置项
|
|
61
|
+
* @param appDir 配置app目录
|
|
62
|
+
* @param functionName 配置文件名称,不包含.yaml后缀
|
|
63
|
+
* @param key 配置项的键
|
|
64
|
+
* @param value 配置项的值
|
|
65
|
+
*/
|
|
17
66
|
updateConfigItem(appDir: string, functionName: string, key: string, value: any): void;
|
|
67
|
+
/** 读取package.json文件,获取最新版本号*/
|
|
18
68
|
getLatestVersion(): string | null;
|
|
19
69
|
}
|
|
20
70
|
declare const _default: Config;
|