yz-yuki-plugin 2.0.7-1 → 2.0.7-11
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 +2 -0
- package/README.md +3 -1
- package/defaultConfig/bilibili/config.yaml +9 -0
- package/defaultConfig/weibo/config.yaml +9 -0
- package/lib/apps/bilibili.js +3 -2
- package/lib/models/bilibili/bilibili.main.get.web.data.js +1 -5
- package/lib/models/bilibili/bilibili.main.query.js +2 -2
- package/lib/models/bilibili/bilibili.main.task.js +77 -49
- package/lib/models/bilibili/bilibili.risk.dm.img.js +4 -4
- package/lib/models/weibo/weibo.main.task.js +72 -44
- package/package.json +1 -1
- package/resources/css/dynamic/MainPage.css +3 -1
- package/lib/models/bilibili/bilibili.risk.w_webid.js +0 -46
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# YUKI-PLUGIN
|
|
4
4
|
|
|
5
|
-
- 一个适用于 `Yunzai 系列机器人框架` 的B
|
|
5
|
+
- 一个适用于 `Yunzai 系列机器人框架` 的B站动态、B站视频链接解析和微博动态订阅推送的插件
|
|
6
6
|
|
|
7
7
|
- 支持 群聊/私聊 订阅B站动态和微博动态,支持定时推送,支持手动触发推送,支持简单查询B站/微博用户信息。
|
|
8
8
|
|
|
@@ -160,6 +160,7 @@ https://m.weibo.cn/u/7643376782 # 7643376782 为崩坏星穹铁道博主uid
|
|
|
160
160
|
请使用 `#优纪帮助`或 `/yuki帮助` 获取完整帮助
|
|
161
161
|
|
|
162
162
|
- [x] B站动态
|
|
163
|
+
- [x] B站视频链接解析
|
|
163
164
|
- [x] 微博动态
|
|
164
165
|
|
|
165
166
|
|
|
@@ -190,6 +191,7 @@ https://m.weibo.cn/u/7643376782 # 7643376782 为崩坏星穹铁道博主uid
|
|
|
190
191
|
| 删除B站ck | 删除手动获取的B站cookie,权限:Master | `#删除B站本地ck` |
|
|
191
192
|
| 查看B站ck | 查看当前启用的B站ck,仅限私聊 | `#我的B站ck` |
|
|
192
193
|
| 刷新B站临时ck | 重新获取并刷新redis缓存的未绑定自己的B站ck而自动获取的 临时B站cookie | `#刷新B站临时ck` |
|
|
194
|
+
| B站视频链接解析 | 解析B站视频链接,支持av号、BV号、app分享链接,官方短链 | `链接xxxx` |
|
|
193
195
|
||||
|
|
194
196
|
| **微博功能** | ------------------------- | ---------- |
|
|
195
197
|
| 添加微博推送 | 检测博主的微博动态进行推送,权限:Master。可选分类:视频、图文、文章、转发,不加分类则默认全部 | `#订阅微博推送uid` `#订阅微博推送 图文 uid` |
|
|
@@ -43,6 +43,15 @@ pushContentLineLimit: 5
|
|
|
43
43
|
# 是否展示定时任务的日志,0 不显示 1 显示
|
|
44
44
|
pushTaskLog: 1
|
|
45
45
|
|
|
46
|
+
# 白名单关键词,命中即推送,不在白名单则不推送。
|
|
47
|
+
# 白名单与黑名单共同起作用,即命中白名单但不命中黑名单即推送,不在白名单或命中黑名单则不推送。
|
|
48
|
+
# 白名单为空则不启用白名单功能。
|
|
49
|
+
# 配置示例:
|
|
50
|
+
# whiteWordslist:
|
|
51
|
+
# - 白名单关键词1
|
|
52
|
+
# - 白名单关键词2
|
|
53
|
+
whiteWordslist:
|
|
54
|
+
|
|
46
55
|
# 包含关键词不推送
|
|
47
56
|
banWords:
|
|
48
57
|
- 关键词1
|
|
@@ -38,6 +38,15 @@ pushContentLineLimit: 5
|
|
|
38
38
|
# 是否展示定时任务的日志,0 不显示 1 显示
|
|
39
39
|
pushTaskLog: 1
|
|
40
40
|
|
|
41
|
+
# 白名单关键词,命中即推送,不在白名单则不推送。
|
|
42
|
+
# 白名单与黑名单共同起作用,即命中白名单但不命中黑名单即推送,不在白名单或命中黑名单则不推送。
|
|
43
|
+
# 白名单为空则不启用白名单功能。
|
|
44
|
+
# 配置示例:
|
|
45
|
+
# whiteWordslist:
|
|
46
|
+
# - 白名单关键词1
|
|
47
|
+
# - 白名单关键词2
|
|
48
|
+
whiteWordslist:
|
|
49
|
+
|
|
41
50
|
# 包含关键词不推送
|
|
42
51
|
banWords:
|
|
43
52
|
- 关键词1
|
package/lib/apps/bilibili.js
CHANGED
|
@@ -499,9 +499,10 @@ message.use(async (e) => {
|
|
|
499
499
|
}, [/^(#|\/)(yuki|优纪)?搜索(b站|B站|bili|bilibili|哔哩|哔哩哔哩)(up|UP)主.*$/]);
|
|
500
500
|
/** 根据名称搜索up的uid*/
|
|
501
501
|
message.use(async (e) => {
|
|
502
|
-
|
|
502
|
+
let parseVideoLink = !!biliConfigData?.parseVideoLink === false ? false : true;
|
|
503
|
+
if (parseVideoLink === false) {
|
|
503
504
|
logger?.info(`优纪B站视频链接解析配置文件已设置关闭,解析终止。`);
|
|
504
|
-
return;
|
|
505
|
+
return false;
|
|
505
506
|
}
|
|
506
507
|
const videoIDMatch = e.msg.match(/(b23\.tv\/([a-zA-Z0-9]+))|(www\.bilibili\.com\/video\/)?(av\d+|BV[a-zA-Z0-9]+)/);
|
|
507
508
|
let videoID;
|
|
@@ -4,7 +4,6 @@ import BiliApi from './bilibili.main.api.js';
|
|
|
4
4
|
import { readSyncCookie, cookieWithBiliTicket, readSavedCookieItems, readSavedCookieOtherItems } from './bilibili.main.models.js';
|
|
5
5
|
import { getWbiSign } from './bilibili.risk.wbi.js';
|
|
6
6
|
import { getDmImg } from './bilibili.risk.dm.img.js';
|
|
7
|
-
import { getWebId } from './bilibili.risk.w_webid.js';
|
|
8
7
|
|
|
9
8
|
class BilibiliWebDataFetcher {
|
|
10
9
|
e;
|
|
@@ -52,20 +51,17 @@ class BilibiliWebDataFetcher {
|
|
|
52
51
|
let { cookie } = await readSyncCookie();
|
|
53
52
|
cookie = await cookieWithBiliTicket(cookie);
|
|
54
53
|
const dmImg = await getDmImg();
|
|
55
|
-
const w_webid = await getWebId(uid);
|
|
56
54
|
const data = {
|
|
57
55
|
mid: uid,
|
|
58
56
|
token: '',
|
|
59
57
|
platform: 'web',
|
|
60
58
|
web_location: 1550101,
|
|
61
|
-
...dmImg
|
|
62
|
-
w_webid: w_webid
|
|
59
|
+
...dmImg
|
|
63
60
|
};
|
|
64
61
|
let signCookie = (await readSavedCookieItems(cookie, ['SESSDATA'], false)) || (await readSavedCookieOtherItems(cookie, ['SESSDATA']));
|
|
65
62
|
const { w_rid, time_stamp } = await getWbiSign(data, BiliApi.BILIBILI_HEADERS, signCookie);
|
|
66
63
|
const params = {
|
|
67
64
|
...data,
|
|
68
|
-
w_webid: w_webid,
|
|
69
65
|
w_rid: w_rid,
|
|
70
66
|
wts: time_stamp
|
|
71
67
|
};
|
|
@@ -227,7 +227,7 @@ class BiliQuery {
|
|
|
227
227
|
case 'RICH_TEXT_NODE_TYPE_EMOJI':
|
|
228
228
|
// 处理表情类型,使用 img 标签显示表情
|
|
229
229
|
const emoji = node.emoji;
|
|
230
|
-
return `<img src="${emoji?.icon_url}" alt="${emoji?.text}" title="${emoji?.text}" style="vertical-align: middle; width: ${emoji?.size ? Number(emoji?.size) *
|
|
230
|
+
return `<img src="${emoji?.icon_url}" alt="${emoji?.text}" title="${emoji?.text}" style="vertical-align: middle; width: ${emoji?.size ? Number(emoji?.size) * 1.5 : 1.5}em; height: ${emoji?.size ? Number(emoji?.size) * 1.5 : 1.5}em;">`;
|
|
231
231
|
case 'RICH_TEXT_NODE_TYPE_GOODS':
|
|
232
232
|
// 处理商品推广类型,使用官方的HTML标签写法
|
|
233
233
|
const goods_url = node?.jump_url;
|
|
@@ -346,7 +346,7 @@ class BiliQuery {
|
|
|
346
346
|
case 'RICH_TEXT_NODE_TYPE_EMOJI':
|
|
347
347
|
// 处理表情类型,使用 img 标签显示表情
|
|
348
348
|
const emoji = node?.rich?.emoji;
|
|
349
|
-
return `<img src="${emoji?.icon_url}" alt="${emoji?.text}" title="${emoji?.text}" style="vertical-align: middle; width: ${emoji?.size ? Number(emoji?.size) *
|
|
349
|
+
return `<img src="${emoji?.icon_url}" alt="${emoji?.text}" title="${emoji?.text}" style="vertical-align: middle; width: ${emoji?.size ? Number(emoji?.size) * 1.5 : 1.5}em; height: ${emoji?.size ? Number(emoji?.size) * 1.5 : 1.5}em;">`;
|
|
350
350
|
case 'RICH_TEXT_NODE_TYPE_GOODS':
|
|
351
351
|
// 处理商品推广类型,使用官方的HTML标签写法
|
|
352
352
|
const goods_url = node?.rich?.jump_url;
|
|
@@ -71,39 +71,40 @@ class BiliTask {
|
|
|
71
71
|
for (let chatId in biliPushData[chatType]) {
|
|
72
72
|
const subUpsOfChat = Array.prototype.slice.call(biliPushData[chatType][chatId] || []);
|
|
73
73
|
for (let subInfoOfup of subUpsOfChat) {
|
|
74
|
+
const { uid, bot_id, name, type } = subInfoOfup;
|
|
74
75
|
let resp;
|
|
75
76
|
// 检查是否已经请求过该 uid
|
|
76
|
-
if (requestedDataOfUids.has(
|
|
77
|
-
resp =
|
|
78
|
-
const dynamicData = resp.data?.items || [];
|
|
79
|
-
dynamicList[subInfoOfup.uid] = dynamicData;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
resp = await this.hendleEventDynamicData(subInfoOfup.uid);
|
|
77
|
+
if (!requestedDataOfUids.has(uid)) {
|
|
78
|
+
resp = await this.hendleEventDynamicData(uid);
|
|
83
79
|
if (resp) {
|
|
84
80
|
if (resp.code === 0) {
|
|
85
|
-
requestedDataOfUids.set(
|
|
81
|
+
requestedDataOfUids.set(uid, resp); // 将响应数据存储到映射中
|
|
86
82
|
const dynamicData = resp.data?.items || [];
|
|
87
|
-
dynamicList[
|
|
83
|
+
dynamicList[uid] = dynamicData;
|
|
88
84
|
}
|
|
89
85
|
else if (resp.code === -352) {
|
|
90
|
-
logger.error(`获取 ${
|
|
86
|
+
logger.error(`获取 ${uid} 动态失败,resCode:-352,请待下次任务自动重试`);
|
|
91
87
|
return;
|
|
92
88
|
}
|
|
93
89
|
else if (resp.code !== 0) {
|
|
94
|
-
logger.error(`获取 ${
|
|
90
|
+
logger.error(`获取 ${uid} 动态失败,resCode:${resp.code},请待下次任务自动重试`);
|
|
95
91
|
return;
|
|
96
92
|
}
|
|
97
93
|
}
|
|
98
94
|
else {
|
|
99
|
-
logger.error(`获取 ${
|
|
95
|
+
logger.error(`获取 ${uid} 动态失败,无响应数据,请待下次任务自动重试`);
|
|
100
96
|
return;
|
|
101
97
|
}
|
|
102
98
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
chatTypeMap.
|
|
99
|
+
if (!chatTypeMap.has(uid)) {
|
|
100
|
+
chatTypeMap.set(uid, new Map());
|
|
101
|
+
}
|
|
102
|
+
const botChatMap = chatTypeMap.get(uid);
|
|
103
|
+
if (!botChatMap?.has(bot_id)) {
|
|
104
|
+
botChatMap?.set(bot_id, new Map());
|
|
105
|
+
}
|
|
106
|
+
const chatMap = botChatMap?.get(bot_id);
|
|
107
|
+
chatMap?.set(chatId, { upName: name, types: type });
|
|
107
108
|
await this.randomDelay(2000, getDataRandomDelay); // 随机延时
|
|
108
109
|
}
|
|
109
110
|
}
|
|
@@ -119,11 +120,11 @@ class BiliTask {
|
|
|
119
120
|
* @param biliConfigData Bilibili配置数据
|
|
120
121
|
*/
|
|
121
122
|
async makeUidDynamicDataMap(uidMap, dynamicList, now, dynamicTimeRange, biliConfigData, messageMap) {
|
|
123
|
+
const printedList = new Set(); // 已打印的动态列表
|
|
122
124
|
for (let [chatType, chatTypeMap] of uidMap) {
|
|
123
|
-
for (let [
|
|
124
|
-
const tempDynamicList = dynamicList[
|
|
125
|
+
for (let [upUid, bot_idMap] of chatTypeMap) {
|
|
126
|
+
const tempDynamicList = dynamicList[upUid] || [];
|
|
125
127
|
const willPushDynamicList = [];
|
|
126
|
-
const printedList = new Set(); // 已打印的动态列表
|
|
127
128
|
for (let dynamicItem of tempDynamicList) {
|
|
128
129
|
let author = dynamicItem?.modules?.module_author || {};
|
|
129
130
|
if (!printedList.has(author?.mid)) {
|
|
@@ -136,19 +137,19 @@ class BiliTask {
|
|
|
136
137
|
logger.debug(`超过间隔,跳过 [ ${author?.name} : ${author?.mid} ] ${author?.pub_time} 的动态`);
|
|
137
138
|
continue;
|
|
138
139
|
} // 如果超过推送时间间隔,跳过当前循环
|
|
139
|
-
if (dynamicItem
|
|
140
|
-
continue;
|
|
140
|
+
if (dynamicItem?.type === 'DYNAMIC_TYPE_FORWARD' && !biliConfigData.pushTransmit) {
|
|
141
|
+
continue;
|
|
142
|
+
} // 如果关闭了转发动态的推送,跳过当前循环
|
|
141
143
|
willPushDynamicList.push(dynamicItem);
|
|
142
144
|
}
|
|
143
|
-
printedList.clear();
|
|
144
|
-
const pushMapInfo = value || {}; // 获取当前 uid 对应的推送信息
|
|
145
|
-
const { chatIds, bot_id, upName, type } = pushMapInfo;
|
|
146
145
|
// 遍历待推送的动态数组,发送动态消息
|
|
147
|
-
for (let
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
for (let [bot_id, chatIdMap] of bot_idMap) {
|
|
147
|
+
for (let [chatId, subUpInfo] of chatIdMap) {
|
|
148
|
+
const { upName, types } = subUpInfo;
|
|
149
|
+
for (let pushDynamicData of willPushDynamicList) {
|
|
150
|
+
if (types && types.length > 0 && !types.includes(pushDynamicData.type)) {
|
|
151
|
+
continue;
|
|
152
|
+
} // 如果禁用了某类型的动态推送,跳过当前循环
|
|
152
153
|
await this.makeDynamicMessageMap(chatId, bot_id, upName, pushDynamicData, biliConfigData, chatType, messageMap); // 发送动态消息
|
|
153
154
|
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
154
155
|
}
|
|
@@ -156,6 +157,7 @@ class BiliTask {
|
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
}
|
|
160
|
+
printedList.clear(); // 清空已打印的动态列表
|
|
159
161
|
}
|
|
160
162
|
/**
|
|
161
163
|
* 渲染构建待发送的动态消息数据的映射数组
|
|
@@ -184,12 +186,23 @@ class BiliTask {
|
|
|
184
186
|
if (pushMsgMode === 'PIC') {
|
|
185
187
|
const { data, uid } = await BiliQuery.formatDynamicData(pushDynamicData); // 处理动态数据
|
|
186
188
|
const extentData = { ...data };
|
|
189
|
+
const getWhiteWords = biliConfigData?.whiteWordslist;
|
|
187
190
|
const getBanWords = biliConfigData?.banWords;
|
|
191
|
+
if (getWhiteWords && Array.isArray(getWhiteWords) && getWhiteWords.length > 0) {
|
|
192
|
+
// 构建白名单关键字正则表达式,转义特殊字符
|
|
193
|
+
const whiteWords = new RegExp(getWhiteWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
194
|
+
if (!whiteWords.test(`${extentData?.title}${extentData?.content}`)) {
|
|
195
|
+
return; // 如果动态消息不在白名单中,则直接返回
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (getWhiteWords && !Array.isArray(getWhiteWords)) {
|
|
199
|
+
logger.error(`B站动态:Yaml配置文件中,whiteWordslist 字段格式不是数组格式,请检查!`);
|
|
200
|
+
}
|
|
188
201
|
if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
|
|
189
202
|
// 构建屏蔽关键字正则表达式,转义特殊字符
|
|
190
203
|
const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
191
204
|
if (banWords.test(`${extentData?.title}${extentData?.content}`)) {
|
|
192
|
-
return
|
|
205
|
+
return; // 如果动态消息包含屏蔽关键字,则直接返回
|
|
193
206
|
}
|
|
194
207
|
}
|
|
195
208
|
else if (getBanWords && !Array.isArray(getBanWords)) {
|
|
@@ -223,7 +236,18 @@ class BiliTask {
|
|
|
223
236
|
if (dynamicMsg === undefined || dynamicMsg === 'continue') {
|
|
224
237
|
return 'return'; // 如果动态消息构建失败,则直接返回
|
|
225
238
|
}
|
|
239
|
+
const getWhiteWords = biliConfigData?.whiteWordslist;
|
|
226
240
|
const getBanWords = biliConfigData?.banWords;
|
|
241
|
+
if (getWhiteWords && Array.isArray(getWhiteWords) && getWhiteWords.length > 0) {
|
|
242
|
+
// 构建白名单关键字正则表达式,转义特殊字符
|
|
243
|
+
const whiteWords = new RegExp(getWhiteWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
244
|
+
if (!whiteWords.test(dynamicMsg.msg.join(''))) {
|
|
245
|
+
return; // 如果动态消息不在白名单中,则直接返回
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else if (getWhiteWords && !Array.isArray(getWhiteWords)) {
|
|
249
|
+
logger.error(`B站动态:Yaml配置文件中,whiteWordslist 字段格式不是数组格式,请检查!`);
|
|
250
|
+
}
|
|
227
251
|
if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
|
|
228
252
|
// 构建屏蔽关键字正则表达式,转义特殊字符
|
|
229
253
|
const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
@@ -399,15 +423,22 @@ class BiliTask {
|
|
|
399
423
|
}
|
|
400
424
|
}
|
|
401
425
|
if (sendMode === 'SINGLE') {
|
|
426
|
+
let allSent = true;
|
|
402
427
|
for (let i = 0; i < messages.length; i++) {
|
|
403
|
-
await this.sendMessageApi(chatId, bot_id, chatType, messages[i])
|
|
428
|
+
if (!(await this.sendMessageApi(chatId, bot_id, chatType, messages[i]))) {
|
|
429
|
+
allSent = false;
|
|
430
|
+
break; // 如果有任何一条消息发送失败,停止发送后续消息
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (allSent) {
|
|
434
|
+
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
|
|
435
|
+
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
404
436
|
}
|
|
405
|
-
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
|
|
406
|
-
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
407
437
|
}
|
|
408
438
|
else if (sendMode === 'MERGE') {
|
|
409
|
-
await this.sendMessageApi(chatId, bot_id, chatType, messages)
|
|
410
|
-
|
|
439
|
+
if (await this.sendMessageApi(chatId, bot_id, chatType, messages)) {
|
|
440
|
+
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
|
|
441
|
+
}
|
|
411
442
|
}
|
|
412
443
|
}
|
|
413
444
|
}
|
|
@@ -423,21 +454,18 @@ class BiliTask {
|
|
|
423
454
|
* @param message 消息内容
|
|
424
455
|
*/
|
|
425
456
|
async sendMessageApi(chatId, bot_id, chatType, message) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
?.pickGroup(String(chatId))
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
457
|
+
try {
|
|
458
|
+
if (chatType === 'group') {
|
|
459
|
+
await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message); // 发送群聊
|
|
460
|
+
}
|
|
461
|
+
else if (chatType === 'private') {
|
|
462
|
+
await (Bot[bot_id] ?? Bot)?.pickFriend(String(chatId)).sendMsg(message); // 发送好友私聊
|
|
463
|
+
}
|
|
464
|
+
return true; // 发送成功
|
|
433
465
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
.sendMsg(message)
|
|
438
|
-
.catch((error) => {
|
|
439
|
-
global?.logger?.error(`用户[${chatId}]推送失败:${JSON.stringify(error)}`);
|
|
440
|
-
}); // 发送好友私聊
|
|
466
|
+
catch (error) {
|
|
467
|
+
global?.logger?.error(`${chatType === 'group' ? '群聊' : '私聊'} ${chatId} 消息发送失败:${JSON.stringify(error)}`);
|
|
468
|
+
return false; // 发送失败
|
|
441
469
|
}
|
|
442
470
|
}
|
|
443
471
|
/**
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**获取dm参数 */
|
|
2
2
|
async function getDmImg() {
|
|
3
|
-
const dm_img_list = []
|
|
3
|
+
const dm_img_list = `[]`;
|
|
4
4
|
//Buffer.from("WebGL 1", 'utf-8').toString("base64") //webgl version的值 WebGL 1 的base64 编码
|
|
5
5
|
const dm_img_str = 'V2ViR0wgMS';
|
|
6
6
|
//webgl unmasked renderer的值拼接webgl unmasked vendor的值的base64编码
|
|
7
7
|
const dm_cover_img_str = 'QU5HTEUgKEludGVsLCBJbnRlbChSKSBIRCBHcmFwaGljcyBEaXJlY3QzRDExIHZzXzVfMCBwc181XzApLCBvciBzaW1pbGFyR29vZ2xlIEluYy4gKEludGVsKQ';
|
|
8
|
-
const dm_img_inter = {
|
|
8
|
+
const dm_img_inter = `{ds:[],wh:[0,0,0],of:[0,0,0]}`;
|
|
9
9
|
return {
|
|
10
|
-
dm_img_list:
|
|
10
|
+
dm_img_list: dm_img_list,
|
|
11
11
|
dm_img_str: dm_img_str,
|
|
12
12
|
dm_cover_img_str: dm_cover_img_str,
|
|
13
|
-
dm_img_inter:
|
|
13
|
+
dm_img_inter: dm_img_inter
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -51,25 +51,26 @@ class WeiboTask {
|
|
|
51
51
|
for (let chatId in weiboPushData[chatType]) {
|
|
52
52
|
const subUpsOfChat = Array.prototype.slice.call(weiboPushData[chatType][chatId] || []);
|
|
53
53
|
for (let subInfoOfup of subUpsOfChat) {
|
|
54
|
+
const { uid, bot_id, name, type } = subInfoOfup;
|
|
54
55
|
let resp;
|
|
55
56
|
// 检查是否已经请求过该 uid
|
|
56
|
-
if (requestedDataOfUids.has(
|
|
57
|
-
resp =
|
|
58
|
-
const dynamicData = resp || [];
|
|
59
|
-
dynamicList[subInfoOfup.uid] = dynamicData;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
resp = await new WeiboWebDataFetcher().getBloggerDynamicList(subInfoOfup.uid); // 获取指定 uid 的动态列表
|
|
57
|
+
if (!requestedDataOfUids.has(uid)) {
|
|
58
|
+
resp = await new WeiboWebDataFetcher().getBloggerDynamicList(uid); // 获取指定 uid 的动态列表
|
|
63
59
|
if (resp) {
|
|
64
|
-
requestedDataOfUids.set(
|
|
60
|
+
requestedDataOfUids.set(uid, resp); // 将响应数据存储到映射中
|
|
65
61
|
const dynamicData = resp || [];
|
|
66
62
|
dynamicList[subInfoOfup.uid] = dynamicData;
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
chatTypeMap.
|
|
65
|
+
if (!chatTypeMap.has(uid)) {
|
|
66
|
+
chatTypeMap.set(uid, new Map());
|
|
67
|
+
}
|
|
68
|
+
const botChatMap = chatTypeMap.get(uid);
|
|
69
|
+
if (!botChatMap?.has(bot_id)) {
|
|
70
|
+
botChatMap?.set(bot_id, new Map());
|
|
71
|
+
}
|
|
72
|
+
const chatMap = botChatMap?.get(bot_id);
|
|
73
|
+
chatMap?.set(chatId, { upName: name, types: type });
|
|
73
74
|
await this.randomDelay(1000, 4000); // 随机延时1-4秒
|
|
74
75
|
}
|
|
75
76
|
}
|
|
@@ -85,11 +86,11 @@ class WeiboTask {
|
|
|
85
86
|
* @param weiboConfigData 微博配置数据
|
|
86
87
|
*/
|
|
87
88
|
async makeUidDynamicDataMap(uidMap, dynamicList, now, dynamicTimeRange, weiboConfigData, messageMap) {
|
|
89
|
+
const printedList = new Set(); // 已打印的动态列表
|
|
88
90
|
for (let [chatType, chatTypeMap] of uidMap) {
|
|
89
|
-
for (let [
|
|
90
|
-
const tempDynamicList = dynamicList[
|
|
91
|
+
for (let [upUid, bot_idMap] of chatTypeMap) {
|
|
92
|
+
const tempDynamicList = dynamicList[upUid] || [];
|
|
91
93
|
const willPushDynamicList = [];
|
|
92
|
-
const printedList = new Set(); // 已打印的动态列表
|
|
93
94
|
for (let dynamicItem of tempDynamicList) {
|
|
94
95
|
let raw_post = dynamicItem || {};
|
|
95
96
|
let user = raw_post?.mblog?.user || {};
|
|
@@ -103,19 +104,19 @@ class WeiboTask {
|
|
|
103
104
|
logger.debug(`超过间隔,跳过 [ ${user?.screen_name} : ${user?.id} ] ${raw_post?.mblog?.created_at} 的动态`);
|
|
104
105
|
continue;
|
|
105
106
|
} // 如果超过推送时间间隔,跳过当前循环
|
|
106
|
-
if (dynamicItem
|
|
107
|
-
continue;
|
|
107
|
+
if (dynamicItem?.type === 'DYNAMIC_TYPE_FORWARD' && !weiboConfigData.pushTransmit) {
|
|
108
|
+
continue;
|
|
109
|
+
} // 如果关闭了转发动态的推送,跳过当前循环
|
|
108
110
|
willPushDynamicList.push(dynamicItem);
|
|
109
111
|
}
|
|
110
|
-
printedList.clear();
|
|
111
|
-
const pushMapInfo = value || {}; // 获取当前 uid 对应的推送信息
|
|
112
|
-
const { chatIds, bot_id, upName, type } = pushMapInfo;
|
|
113
112
|
// 遍历待推送的动态数组,发送动态消息
|
|
114
|
-
for (let
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
for (let [bot_id, chatIdMap] of bot_idMap) {
|
|
114
|
+
for (let [chatId, subUpInfo] of chatIdMap) {
|
|
115
|
+
const { upName, types } = subUpInfo;
|
|
116
|
+
for (let pushDynamicData of willPushDynamicList) {
|
|
117
|
+
if (types && types.length > 0 && !types.includes(pushDynamicData.type)) {
|
|
118
|
+
continue;
|
|
119
|
+
} // 如果禁用了某类型的动态推送,跳过当前循环
|
|
119
120
|
await this.makeDynamicMessageMap(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType, messageMap); // 发送动态消息
|
|
120
121
|
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
121
122
|
}
|
|
@@ -123,6 +124,7 @@ class WeiboTask {
|
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
}
|
|
127
|
+
printedList.clear(); // 清空已打印的动态列表
|
|
126
128
|
}
|
|
127
129
|
/**
|
|
128
130
|
* 渲染构建待发送的动态消息数据的映射数组
|
|
@@ -149,7 +151,18 @@ class WeiboTask {
|
|
|
149
151
|
return; // 如果已经发送过,则直接返回
|
|
150
152
|
if (!!weiboConfigData.pushMsgMode) {
|
|
151
153
|
const { data, uid } = await WeiboQuery.formatDynamicData(pushDynamicData); // 处理动态数据
|
|
154
|
+
const getWhiteWords = weiboConfigData?.whiteWordslist;
|
|
152
155
|
const getBanWords = weiboConfigData?.banWords;
|
|
156
|
+
if (getWhiteWords && Array.isArray(getWhiteWords) && getWhiteWords.length > 0) {
|
|
157
|
+
// 构建白名单关键字正则表达式,转义特殊字符
|
|
158
|
+
const whiteWords = new RegExp(getWhiteWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
159
|
+
if (!whiteWords.test(`${data?.title}${data?.content}`)) {
|
|
160
|
+
return; // 如果动态消息不在白名单中,则直接返回
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else if (getWhiteWords && !Array.isArray(getWhiteWords)) {
|
|
164
|
+
logger.error(`微博动态:Yaml配置文件中,whiteWordslist 字段格式不是数组格式,请检查!`);
|
|
165
|
+
}
|
|
153
166
|
if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
|
|
154
167
|
// 构建屏蔽关键字正则表达式,转义特殊字符
|
|
155
168
|
const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
@@ -189,7 +202,18 @@ class WeiboTask {
|
|
|
189
202
|
if (dynamicMsg === undefined || dynamicMsg === 'continue') {
|
|
190
203
|
return 'return'; // 如果动态消息构建失败或内部资源获取失败,则直接返回
|
|
191
204
|
}
|
|
205
|
+
const getWhiteWords = weiboConfigData?.whiteWordslist;
|
|
192
206
|
const getBanWords = weiboConfigData?.banWords;
|
|
207
|
+
if (getWhiteWords && Array.isArray(getWhiteWords) && getWhiteWords.length > 0) {
|
|
208
|
+
// 构建白名单关键字正则表达式,转义特殊字符
|
|
209
|
+
const whiteWords = new RegExp(getWhiteWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
210
|
+
if (!whiteWords.test(dynamicMsg.msg.join(''))) {
|
|
211
|
+
return; // 如果动态消息不在白名单中,则直接返回
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (getWhiteWords && !Array.isArray(getWhiteWords)) {
|
|
215
|
+
logger.error(`微博动态:Yaml配置文件中,whiteWordslist 字段格式不是数组格式,请检查!`);
|
|
216
|
+
}
|
|
193
217
|
if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
|
|
194
218
|
// 构建屏蔽关键字正则表达式,转义特殊字符
|
|
195
219
|
const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
@@ -346,15 +370,22 @@ class WeiboTask {
|
|
|
346
370
|
LogMark.add('1');
|
|
347
371
|
}
|
|
348
372
|
if (sendMode === 'SINGLE') {
|
|
373
|
+
let allSent = true;
|
|
349
374
|
for (let i = 0; i < messages.length; i++) {
|
|
350
|
-
await this.sendMessageApi(chatId, bot_id, chatType, messages[i])
|
|
375
|
+
if (!(await this.sendMessageApi(chatId, bot_id, chatType, messages[i]))) {
|
|
376
|
+
allSent = false;
|
|
377
|
+
break; // 如果有任何一条消息发送失败,停止发送后续消息
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (allSent) {
|
|
381
|
+
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
|
|
382
|
+
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
351
383
|
}
|
|
352
|
-
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
|
|
353
|
-
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
354
384
|
}
|
|
355
385
|
else if (sendMode === 'MERGE') {
|
|
356
|
-
await this.sendMessageApi(chatId, bot_id, chatType, messages)
|
|
357
|
-
|
|
386
|
+
if (await this.sendMessageApi(chatId, bot_id, chatType, messages)) {
|
|
387
|
+
await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
|
|
388
|
+
}
|
|
358
389
|
}
|
|
359
390
|
}
|
|
360
391
|
}
|
|
@@ -370,21 +401,18 @@ class WeiboTask {
|
|
|
370
401
|
* @param message 消息内容
|
|
371
402
|
*/
|
|
372
403
|
async sendMessageApi(chatId, bot_id, chatType, message) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
?.pickGroup(String(chatId))
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
404
|
+
try {
|
|
405
|
+
if (chatType === 'group') {
|
|
406
|
+
await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message); // 发送群聊
|
|
407
|
+
}
|
|
408
|
+
else if (chatType === 'private') {
|
|
409
|
+
await (Bot[bot_id] ?? Bot)?.pickFriend(String(chatId)).sendMsg(message); // 发送好友私聊
|
|
410
|
+
}
|
|
411
|
+
return true; // 发送成功
|
|
380
412
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
.sendMsg(message)
|
|
385
|
-
.catch(error => {
|
|
386
|
-
global?.logger?.error(`用户[${chatId}]推送失败:${JSON.stringify(error)}`);
|
|
387
|
-
}); // 发送好友私聊
|
|
413
|
+
catch (error) {
|
|
414
|
+
global?.logger?.error(`${chatType === 'group' ? '群聊' : '私聊'} ${chatId} 消息发送失败:${JSON.stringify(error)}`);
|
|
415
|
+
return false; // 发送失败
|
|
388
416
|
}
|
|
389
417
|
}
|
|
390
418
|
/**
|
package/package.json
CHANGED
|
@@ -25,8 +25,10 @@ body::-webkit-scrollbar {
|
|
|
25
25
|
font-style: normal;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
@import url('https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css');
|
|
29
|
+
|
|
28
30
|
body {
|
|
29
|
-
font-family: 'OPSans', Arial, sans-serif;
|
|
31
|
+
font-family: 'OPSans', 'HarmonyOS_Regular', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
|
|
30
32
|
background-color: #f9f9f9;
|
|
31
33
|
margin: 0;
|
|
32
34
|
padding: 0;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import lodash from 'lodash';
|
|
3
|
-
import { Redis } from 'yunzaijs';
|
|
4
|
-
import BiliApi from './bilibili.main.api.js';
|
|
5
|
-
import { readSyncCookie, cookieWithBiliTicket } from './bilibili.main.models.js';
|
|
6
|
-
|
|
7
|
-
async function getWebId(uid) {
|
|
8
|
-
const w_webid_key = 'Yz:yuki:bili:w_webid';
|
|
9
|
-
const w_webid = await Redis.get(w_webid_key);
|
|
10
|
-
const keyTTL = await Redis.ttl(w_webid_key);
|
|
11
|
-
if (w_webid && keyTTL < 259200) {
|
|
12
|
-
return String(w_webid);
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
const url = `https://space.bilibili.com/${uid ? uid : 401742377}/dynamic`;
|
|
16
|
-
let { cookie } = await readSyncCookie();
|
|
17
|
-
cookie = await cookieWithBiliTicket(cookie);
|
|
18
|
-
const res = await axios.get(url, {
|
|
19
|
-
timeout: 8000,
|
|
20
|
-
headers: lodash.merge(BiliApi.BILIBILI_DYNAMIC_SPACE_HEADERS, {
|
|
21
|
-
Cookie: `${cookie}`,
|
|
22
|
-
Host: `space.bilibili.com`
|
|
23
|
-
})
|
|
24
|
-
});
|
|
25
|
-
const htmlContent = await res.data;
|
|
26
|
-
const htmlContentRegex = /="__RENDER_DATA__"\s*type="application\/json">(.*?)<\/script>/;
|
|
27
|
-
const __RENDER_DATA__ = htmlContent.match(htmlContentRegex);
|
|
28
|
-
if (__RENDER_DATA__ && __RENDER_DATA__[1]) {
|
|
29
|
-
const decoded__RENDER_DATA__JsonString = decodeURIComponent(__RENDER_DATA__[1]);
|
|
30
|
-
const accessIdRegex = /"access_id":"(.*?)"/;
|
|
31
|
-
const access_id = decoded__RENDER_DATA__JsonString.match(accessIdRegex);
|
|
32
|
-
const ExpirationTimeRegex = /document.getElementById\("__RENDER_DATA__"\).*?setTimeout\(function\(\)\s*{window.location.reload\(true\);},\s*(\d+)\s*\*\s*(\d+)\);<\/script>/;
|
|
33
|
-
const ExpirationTime = htmlContent.match(ExpirationTimeRegex);
|
|
34
|
-
if (access_id && access_id[1] && ExpirationTime && ExpirationTime[1]) {
|
|
35
|
-
await Redis.set(w_webid_key, access_id[1], { EX: Number(ExpirationTime[1]) });
|
|
36
|
-
return String(access_id[1]);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
console.error('Failed to get access_id from __RENDER_DATA__');
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export { getWebId };
|