yz-yuki-plugin 2.0.6-9 → 2.0.7-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.
@@ -2,7 +2,7 @@ import QRCode from 'qrcode';
2
2
  import { Redis, Segment, Bot } from 'yunzaijs';
3
3
  import Config from '../../utils/config.js';
4
4
  import { renderPage } from '../../utils/image.js';
5
- import { WeiboGetWebData } from './weibo.main.get.web.data.js';
5
+ import { WeiboWebDataFetcher } from './weibo.main.get.web.data.js';
6
6
  import { WeiboQuery } from './weibo.main.query.js';
7
7
 
8
8
  class WeiboTask {
@@ -15,16 +15,22 @@ class WeiboTask {
15
15
  this.groupKey = 'Yz:yuki:weibo:upPush:group:';
16
16
  this.privateKey = 'Yz:yuki:weibo:upPush:private:';
17
17
  }
18
+ /**
19
+ * 执行动态推送任务
20
+ */
18
21
  async runTask() {
19
22
  let weiboConfigData = await Config.getUserConfig('weibo', 'config');
20
23
  let weiboPushData = await Config.getUserConfig('weibo', 'push');
21
- let interval = weiboConfigData.interval || 7200; // 推送间隔时间,单位为秒,默认2小时
24
+ let interval = weiboConfigData.interval || 7200; // 筛选何时发布的动态,单位为秒,默认2小时内发布的动态
22
25
  logger.debug(`当前微博功能配置:${JSON.stringify(weiboConfigData)}`);
23
26
  const uidMap = new Map(); // 存放group 和 private 对应所属 uid 与推送信息的映射
24
27
  const dynamicList = {}; // 存放获取的所有动态,键为 uid,值为动态数组
25
28
  await this.processWeiboData(weiboPushData, uidMap, dynamicList);
26
29
  let now = Date.now() / 1000; // 当前时间戳(秒)
27
- await this.pushDynamicMessages(uidMap, dynamicList, now, interval, weiboConfigData);
30
+ // 定义待推送动态消息映射
31
+ const messageMap = new Map();
32
+ await this.makeUidDynamicDataMap(uidMap, dynamicList, now, interval, weiboConfigData, messageMap);
33
+ await this.sendDynamicMessage(messageMap, weiboConfigData);
28
34
  }
29
35
  /**
30
36
  * 处理微博数据,获取动态列表并构建 uid 映射
@@ -53,7 +59,7 @@ class WeiboTask {
53
59
  dynamicList[subInfoOfup.uid] = dynamicData;
54
60
  }
55
61
  else {
56
- resp = await await new WeiboGetWebData().getBloggerDynamicList(subInfoOfup.uid); // 获取指定 uid 的动态列表
62
+ resp = await new WeiboWebDataFetcher().getBloggerDynamicList(subInfoOfup.uid); // 获取指定 uid 的动态列表
57
63
  if (resp) {
58
64
  requestedDataOfUids.set(subInfoOfup.uid, resp); // 将响应数据存储到映射中
59
65
  const dynamicData = resp || [];
@@ -71,14 +77,14 @@ class WeiboTask {
71
77
  requestedDataOfUids.clear(); // 清空已请求的映射
72
78
  }
73
79
  /**
74
- * 推送动态消息
80
+ * 构建uid对应动态数据映射
75
81
  * @param uidMap uid 映射
76
82
  * @param dynamicList 动态列表
77
83
  * @param now 当前时间戳
78
- * @param interval 推送间隔时间
84
+ * @param dynamicTimeRange 筛选何时发布的动态
79
85
  * @param weiboConfigData 微博配置数据
80
86
  */
81
- async pushDynamicMessages(uidMap, dynamicList, now, interval, weiboConfigData) {
87
+ async makeUidDynamicDataMap(uidMap, dynamicList, now, dynamicTimeRange, weiboConfigData, messageMap) {
82
88
  for (let [chatType, chatTypeMap] of uidMap) {
83
89
  for (let [key, value] of chatTypeMap) {
84
90
  const tempDynamicList = dynamicList[key] || [];
@@ -93,7 +99,7 @@ class WeiboTask {
93
99
  }
94
100
  if (!raw_post?.mblog?.created_at)
95
101
  continue;
96
- if (Number(now - WeiboQuery.getDynamicCreatetDate(raw_post) / 1000) > interval) {
102
+ if (Number(now - WeiboQuery.getDynamicCreatetDate(raw_post) / 1000) > dynamicTimeRange) {
97
103
  logger.debug(`超过间隔,跳过 [ ${user?.screen_name} : ${user?.id} ] ${raw_post?.mblog?.created_at} 的动态`);
98
104
  continue;
99
105
  } // 如果超过推送时间间隔,跳过当前循环
@@ -110,7 +116,7 @@ class WeiboTask {
110
116
  for (let chatId of chatIds) {
111
117
  if (type && type.length && !type.includes(pushDynamicData.type))
112
118
  continue; // 如果禁用了某类型的动态推送,跳过当前循环
113
- await this.sendDynamic(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType); // 发送动态消息
119
+ await this.makeDynamicMessageMap(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType, messageMap); // 发送动态消息
114
120
  await this.randomDelay(1000, 2000); // 随机延时1-2秒
115
121
  }
116
122
  }
@@ -119,15 +125,16 @@ class WeiboTask {
119
125
  }
120
126
  }
121
127
  /**
122
- * 发送动态消息
128
+ * 渲染构建待发送的动态消息数据的映射数组
123
129
  * @param chatId 聊天 ID
124
130
  * @param bot_id 机器人 ID
125
- * @param upName 用户名
131
+ * @param upName 博主用户名
126
132
  * @param pushDynamicData 推送动态数据
127
133
  * @param weiboConfigData 微博配置数据
128
134
  * @param chatType 聊天类型
135
+ * @param messageMap 待发送的动态消息映射
129
136
  */
130
- async sendDynamic(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType) {
137
+ async makeDynamicMessageMap(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType, messageMap) {
131
138
  const id_str = WeiboQuery.getDynamicId(pushDynamicData); // 获取动态 ID
132
139
  let sended = null, markKey = '';
133
140
  if (chatType === 'group') {
@@ -142,10 +149,16 @@ class WeiboTask {
142
149
  return; // 如果已经发送过,则直接返回
143
150
  if (!!weiboConfigData.pushMsgMode) {
144
151
  const { data, uid } = await WeiboQuery.formatDynamicData(pushDynamicData); // 处理动态数据
145
- const eval2 = eval;
146
- let banWords = eval2(`/${weiboConfigData.banWords.join('|')}/g`); // 构建屏蔽关键字正则表达式
147
- if (new RegExp(banWords).test(`${data?.title}${data?.content}`)) {
148
- return 'return'; // 如果动态包含屏蔽关键字,则直接返回
152
+ const getBanWords = weiboConfigData?.banWords;
153
+ if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
154
+ // 构建屏蔽关键字正则表达式,转义特殊字符
155
+ const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
156
+ if (banWords.test(`${data?.title}${data?.content}`)) {
157
+ return 'return'; // 如果动态消息包含屏蔽关键字,则直接返回
158
+ }
159
+ }
160
+ else if (getBanWords && !Array.isArray(getBanWords)) {
161
+ logger.error(`微博动态:Yaml配置文件中,banWords 字段格式不是数组格式,请检查!`);
149
162
  }
150
163
  let boxGrid = !!weiboConfigData.boxGrid === false ? false : true; // 是否启用九宫格样式,默认为 true
151
164
  let isSplit = !!weiboConfigData.isSplit === false ? false : true; // 是否启用分片截图,默认为 true
@@ -168,44 +181,35 @@ class WeiboTask {
168
181
  };
169
182
  let imgs = await this.renderDynamicCard(uid, renderData, ScreenshotOptionsData);
170
183
  if (!imgs)
171
- return;
172
- Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 * 72 }); // 设置已发送标记
173
- global?.logger?.mark('优纪插件:微博动态执行推送');
174
- for (let i = 0; i < imgs.length; i++) {
175
- const image = imgs[i];
176
- await this.sendMessage(chatId, bot_id, chatType, Segment.image(image));
177
- await this.randomDelay(1000, 2000); // 随机延时1-2秒
178
- }
179
- await new Promise(resolve => setTimeout(resolve, 1000));
184
+ return; // 如果渲染失败,则直接返回
185
+ await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'SINGLE', id_str, extentData?.type, imgs.map(img => Segment.image(img)));
180
186
  }
181
187
  else {
182
188
  const dynamicMsg = await WeiboQuery.formatTextDynamicData(upName, pushDynamicData, false, weiboConfigData); //构建文字动态消息
183
- Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 * 72 }); // 设置已发送标记
184
189
  if (dynamicMsg === undefined || dynamicMsg === 'continue') {
185
190
  return 'return'; // 如果动态消息构建失败或内部资源获取失败,则直接返回
186
191
  }
187
192
  const getBanWords = weiboConfigData?.banWords;
188
193
  if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
189
- const banWords = new RegExp(getBanWords.join('|'), 'g'); // 构建屏蔽关键字正则表达式
194
+ // 构建屏蔽关键字正则表达式,转义特殊字符
195
+ const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
190
196
  if (banWords.test(dynamicMsg.msg.join(''))) {
191
197
  return 'return'; // 如果动态消息包含屏蔽关键字,则直接返回
192
198
  }
193
199
  }
200
+ else if (getBanWords && !Array.isArray(getBanWords)) {
201
+ logger.error(`微博动态:Yaml配置文件中,banWords 字段格式不是数组格式,请检查!`);
202
+ }
194
203
  let mergeTextPic = !!weiboConfigData.mergeTextPic === false ? false : true; // 是否合并文字和图片,默认为 true
195
- if (mergeTextPic) {
204
+ //开启了合并文本和图片
205
+ if (mergeTextPic === true) {
196
206
  const mergeMsg = [...dynamicMsg.msg, ...dynamicMsg.pics];
197
- await this.sendMessage(chatId, bot_id, chatType, mergeMsg);
207
+ await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'MERGE', id_str, dynamicMsg.dynamicType, mergeMsg);
198
208
  }
199
209
  else {
200
- await this.sendMessage(chatId, bot_id, chatType, dynamicMsg.msg);
201
- const pics = dynamicMsg.pics;
202
- if (pics && pics.length > 0) {
203
- for (let i = 0; i < pics.length; i++) {
204
- await this.sendMessage(chatId, bot_id, chatType, pics[i]);
205
- await this.randomDelay(1000, 2000); // 随机延时1-2秒
206
- }
207
- }
208
- await new Promise(resolve => setTimeout(resolve, 1000));
210
+ //不合并文本和图片
211
+ await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'MERGE', id_str, dynamicMsg.dynamicType, dynamicMsg.msg);
212
+ await this.addMessageToMap(messageMap, chatType, bot_id, chatId, 'SINGLE', id_str, dynamicMsg.dynamicType, dynamicMsg.pics);
209
213
  }
210
214
  }
211
215
  }
@@ -286,13 +290,86 @@ class WeiboTask {
286
290
  }
287
291
  }
288
292
  /**
289
- * 发送消息
293
+ * 收集消息映射
294
+ * @param messageMap 消息映射
295
+ * @param chatType 聊天类型
296
+ * @param bot_id 机器人 ID
297
+ * @param chatId 聊天 ID
298
+ * @param sendMode 发送模式: SINGLE 逐条发送,MERGE 合并发送
299
+ * @param dynamicUUid_str 动态 UUID
300
+ * @param dynamicType 动态类型
301
+ * @param message 消息内容
302
+ */
303
+ async addMessageToMap(messageMap, chatType, bot_id, chatId, sendMode, dynamicUUid_str, dynamicType, messages) {
304
+ if (!messageMap.has(chatType)) {
305
+ messageMap.set(chatType, new Map());
306
+ }
307
+ const botMap = messageMap.get(chatType);
308
+ if (!botMap?.has(bot_id)) {
309
+ botMap?.set(bot_id, new Map());
310
+ }
311
+ const chatMap = botMap?.get(bot_id);
312
+ if (!chatMap?.has(chatId)) {
313
+ chatMap?.set(chatId, []);
314
+ }
315
+ chatMap?.get(chatId)?.push({ sendMode, dynamicUUid_str, dynamicType, messages });
316
+ }
317
+ /**
318
+ * 推送动态消息
319
+ * @param messageMap 消息映射
320
+ * @param biliConfigData 微博配置数据
321
+ */
322
+ async sendDynamicMessage(messageMap, weiboConfigData) {
323
+ const LogMark = new Set(); // 日志mark
324
+ for (const [chatType, botMap] of messageMap) {
325
+ for (const [bot_id, chatMap] of botMap) {
326
+ for (const [chatId, messageCombinationList] of chatMap) {
327
+ // 遍历组合消息
328
+ for (const messageCombination of messageCombinationList) {
329
+ const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
330
+ let sended = null;
331
+ let markKey = '';
332
+ if (chatType === 'group') {
333
+ markKey = this.groupKey;
334
+ sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
335
+ }
336
+ else if (chatType === 'private') {
337
+ markKey = this.privateKey;
338
+ sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
339
+ }
340
+ const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
341
+ if (sended) {
342
+ continue; // 如果已经发送过,则直接跳过
343
+ }
344
+ if (!LogMark.has('1')) {
345
+ global?.logger?.mark('优纪插件: B站动态执行推送');
346
+ LogMark.add('1');
347
+ }
348
+ if (sendMode === 'SINGLE') {
349
+ for (let i = 0; i < messages.length; i++) {
350
+ await this.sendMessageApi(chatId, bot_id, chatType, messages[i]);
351
+ }
352
+ await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
353
+ await this.randomDelay(1000, 2000); // 随机延时1-2秒
354
+ }
355
+ else if (sendMode === 'MERGE') {
356
+ await this.sendMessageApi(chatId, bot_id, chatType, messages);
357
+ await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
358
+ }
359
+ }
360
+ }
361
+ }
362
+ }
363
+ LogMark.clear(); // 清空日志mark
364
+ }
365
+ /**
366
+ * 发送消息api
290
367
  * @param chatId 聊天 ID
291
368
  * @param bot_id 机器人 ID
292
369
  * @param chatType 聊天类型
293
370
  * @param message 消息内容
294
371
  */
295
- async sendMessage(chatId, bot_id, chatType, message) {
372
+ async sendMessageApi(chatId, bot_id, chatType, message) {
296
373
  if (chatType === 'group') {
297
374
  await (Bot[bot_id] ?? Bot)
298
375
  ?.pickGroup(String(chatId))
@@ -15,11 +15,11 @@ class Image extends Picture {
15
15
  // 继承父类实例
16
16
  super();
17
17
  // 父类已经实例化组件渲染对象
18
- //this.Com;
18
+ //this.component;
19
19
  // 父类已经实例化启动 Puppeteer
20
- //this.Pup.start();
20
+ //this.puppeteer.start();
21
21
  // 初始化 YukiPuppeteerRender 实例
22
- this.yukiPuppeteerRender = new YukiPuppeteerRender(this.Pup);
22
+ this.yukiPuppeteerRender = new YukiPuppeteerRender(this.puppeteer);
23
23
  }
24
24
  /**
25
25
  * 实例方法,用于执行实际的渲染和截图操作
@@ -34,7 +34,7 @@ class Image extends Picture {
34
34
  // 根据组件名称获取对应的 React 组件
35
35
  const Page = index[page];
36
36
  // 调用 yukiPuppeteerRender 进行截图操作
37
- return this.yukiPuppeteerRender.yukiScreenshot(this.Com.compile({
37
+ return this.yukiPuppeteerRender.yukiScreenshot(this.component.compile({
38
38
  path: page,
39
39
  name: `${uid}.html`,
40
40
  component: React.createElement(Page, { ...props }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yz-yuki-plugin",
3
- "version": "2.0.6-9",
3
+ "version": "2.0.7-1",
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.0.8",
34
+ "jsxp": "^1.1.2",
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.1.0",
39
+ "puppeteer": "^24.4.0",
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.0.8",
66
+ "jsxp": "^1.1.2",
67
67
  "lodash": "^4.17.21",
68
68
  "lvyjs": "^0.2.14",
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.1.0",
73
+ "puppeteer": "^24.4.0",
74
74
  "qrcode": "^1.5.4",
75
75
  "react": "^18.3.1",
76
76
  "react-dom": "^18.3.1",