yz-yuki-plugin 2.0.8-0 → 2.0.8-10
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 +3 -0
- package/README.md +8 -3
- package/defaultConfig/bilibili/config.yaml +3 -0
- package/defaultConfig/help/help.yaml +21 -0
- package/defaultConfig/weibo/config.yaml +3 -0
- package/lib/apps/bilibili.js +2 -2
- package/lib/apps/weibo.js +143 -24
- package/lib/components/loginQrcode/Page.js +3 -1
- package/lib/models/bilibili/bilibili.main.models.js +7 -7
- package/lib/models/bilibili/bilibili.main.query.js +7 -2
- package/lib/models/bilibili/bilibili.main.task.js +33 -35
- package/lib/models/weibo/weibo.main.api.js +115 -8
- package/lib/models/weibo/weibo.main.get.web.data.js +61 -4
- package/lib/models/weibo/weibo.main.models.js +347 -0
- package/lib/models/weibo/weibo.main.task.js +34 -36
- package/lib/models/weibo/weibo.risk.bd.rid.js +309 -0
- package/lib/utils/puppeteer.render.js +3 -1
- package/package.json +5 -4
- package/resources/css/dynamic/MainPage.css +1 -1
|
@@ -156,7 +156,9 @@ class WeiboTask {
|
|
|
156
156
|
if (getWhiteWords && Array.isArray(getWhiteWords) && getWhiteWords.length > 0) {
|
|
157
157
|
// 构建白名单关键字正则表达式,转义特殊字符
|
|
158
158
|
const whiteWords = new RegExp(getWhiteWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
159
|
-
|
|
159
|
+
const content = `${data?.title}${data?.content}`;
|
|
160
|
+
if (!whiteWords.test(content)) {
|
|
161
|
+
logger.info(`博主 "${upName}" 微博动态:白名单关键词已开启,但动态消息未匹配,已跳过推送`);
|
|
160
162
|
return; // 如果动态消息不在白名单中,则直接返回
|
|
161
163
|
}
|
|
162
164
|
}
|
|
@@ -166,8 +168,11 @@ class WeiboTask {
|
|
|
166
168
|
if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
|
|
167
169
|
// 构建屏蔽关键字正则表达式,转义特殊字符
|
|
168
170
|
const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
const content = `${data?.title}${data?.content}`;
|
|
172
|
+
const matched = content.match(banWords);
|
|
173
|
+
if (matched) {
|
|
174
|
+
logger.info(`博主 "${upName}" 微博动态:触发屏蔽关键词 "${matched.join(', ')}" ,已跳过推送`);
|
|
175
|
+
return; // 如果动态消息包含屏蔽关键字,则直接返回
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
178
|
else if (getBanWords && !Array.isArray(getBanWords)) {
|
|
@@ -177,6 +182,7 @@ class WeiboTask {
|
|
|
177
182
|
let isSplit = !!weiboConfigData.isSplit === false ? false : true; // 是否启用分片截图,默认为 true
|
|
178
183
|
let style = isSplit ? '' : `.unfold { max-height: ${weiboConfigData?.noSplitHeight ?? 7500}px; }`; // 不启用分片截图模式的样式
|
|
179
184
|
let splitHeight = weiboConfigData?.splitHeight ?? 8000; // 分片截图高度,默认 8000, 单位 px,启用分片截图时生效
|
|
185
|
+
let isPauseGif = !!weiboConfigData?.isPauseGif === true ? true : false; // 是否暂停 GIF 动图,默认为 false
|
|
180
186
|
const extentData = { ...data };
|
|
181
187
|
const urlQrcodeData = await QRCode.toDataURL(extentData?.url);
|
|
182
188
|
let renderData = this.buildRenderData(extentData, urlQrcodeData, boxGrid);
|
|
@@ -190,7 +196,8 @@ class WeiboTask {
|
|
|
190
196
|
quality: 98
|
|
191
197
|
},
|
|
192
198
|
saveHtmlfile: false,
|
|
193
|
-
pageSplitHeight: splitHeight
|
|
199
|
+
pageSplitHeight: splitHeight,
|
|
200
|
+
isPauseGif: isPauseGif
|
|
194
201
|
};
|
|
195
202
|
let imgs = await this.renderDynamicCard(uid, renderData, ScreenshotOptionsData);
|
|
196
203
|
if (!imgs)
|
|
@@ -208,6 +215,7 @@ class WeiboTask {
|
|
|
208
215
|
// 构建白名单关键字正则表达式,转义特殊字符
|
|
209
216
|
const whiteWords = new RegExp(getWhiteWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
210
217
|
if (!whiteWords.test(dynamicMsg.msg.join(''))) {
|
|
218
|
+
logger.info(`博主 "${upName}" 微博动态:白名单关键词已开启,但动态消息未匹配,已跳过推送`);
|
|
211
219
|
return; // 如果动态消息不在白名单中,则直接返回
|
|
212
220
|
}
|
|
213
221
|
}
|
|
@@ -217,8 +225,11 @@ class WeiboTask {
|
|
|
217
225
|
if (getBanWords && Array.isArray(getBanWords) && getBanWords.length > 0) {
|
|
218
226
|
// 构建屏蔽关键字正则表达式,转义特殊字符
|
|
219
227
|
const banWords = new RegExp(getBanWords.map(word => word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');
|
|
220
|
-
|
|
221
|
-
|
|
228
|
+
const content = dynamicMsg.msg.join('');
|
|
229
|
+
const matched = content.match(banWords);
|
|
230
|
+
if (matched) {
|
|
231
|
+
logger.info(`博主 "${upName}" 微博动态:触发屏蔽关键词 "${matched.join(', ')}" ,已跳过推送`);
|
|
232
|
+
return; // 如果动态消息包含屏蔽关键字,则直接返回
|
|
222
233
|
}
|
|
223
234
|
}
|
|
224
235
|
else if (getBanWords && !Array.isArray(getBanWords)) {
|
|
@@ -245,22 +256,25 @@ class WeiboTask {
|
|
|
245
256
|
* @returns 渲染数据
|
|
246
257
|
*/
|
|
247
258
|
buildRenderData(extentData, urlQrcodeData, boxGrid) {
|
|
259
|
+
const baseData = {
|
|
260
|
+
appName: 'weibo',
|
|
261
|
+
boxGrid: boxGrid,
|
|
262
|
+
type: extentData?.type,
|
|
263
|
+
face: extentData?.face,
|
|
264
|
+
pendant: extentData?.pendant,
|
|
265
|
+
name: extentData?.name,
|
|
266
|
+
pubTs: extentData?.pubTs,
|
|
267
|
+
title: extentData?.title,
|
|
268
|
+
content: extentData?.content,
|
|
269
|
+
urlImgData: urlQrcodeData,
|
|
270
|
+
created: extentData?.created,
|
|
271
|
+
pics: extentData?.pics,
|
|
272
|
+
category: extentData?.category
|
|
273
|
+
};
|
|
248
274
|
if (extentData.orig && extentData.orig.length !== 0) {
|
|
249
275
|
return {
|
|
250
276
|
data: {
|
|
251
|
-
|
|
252
|
-
boxGrid: boxGrid,
|
|
253
|
-
type: extentData?.type,
|
|
254
|
-
face: extentData?.face,
|
|
255
|
-
pendant: extentData?.pendant,
|
|
256
|
-
name: extentData?.name,
|
|
257
|
-
pubTs: extentData?.pubTs,
|
|
258
|
-
title: extentData?.title,
|
|
259
|
-
content: extentData?.content,
|
|
260
|
-
urlImgData: urlQrcodeData,
|
|
261
|
-
created: extentData?.created,
|
|
262
|
-
pics: extentData?.pics,
|
|
263
|
-
category: extentData?.category,
|
|
277
|
+
...baseData,
|
|
264
278
|
orig: {
|
|
265
279
|
data: {
|
|
266
280
|
type: extentData?.orig?.data?.type,
|
|
@@ -278,23 +292,7 @@ class WeiboTask {
|
|
|
278
292
|
};
|
|
279
293
|
}
|
|
280
294
|
else {
|
|
281
|
-
return {
|
|
282
|
-
data: {
|
|
283
|
-
appName: 'weibo',
|
|
284
|
-
boxGrid: boxGrid,
|
|
285
|
-
type: extentData?.type,
|
|
286
|
-
face: extentData?.face,
|
|
287
|
-
pendant: extentData?.pendant,
|
|
288
|
-
name: extentData?.name,
|
|
289
|
-
pubTs: extentData?.pubTs,
|
|
290
|
-
title: extentData?.title,
|
|
291
|
-
content: extentData?.content,
|
|
292
|
-
urlImgData: urlQrcodeData,
|
|
293
|
-
created: extentData?.created,
|
|
294
|
-
pics: extentData?.pics,
|
|
295
|
-
category: extentData?.category
|
|
296
|
-
}
|
|
297
|
-
};
|
|
295
|
+
return { data: baseData };
|
|
298
296
|
}
|
|
299
297
|
}
|
|
300
298
|
/**
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import WeiboApi from './weibo.main.api.js';
|
|
2
|
+
import lodash from 'lodash';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 生成更真实的鼠标轨迹数据(贝塞尔曲线模拟)
|
|
7
|
+
* @returns {Array<Array<number>>} 鼠标轨迹数据 [[x偏移, y偏移, 时间戳], ...]
|
|
8
|
+
*/
|
|
9
|
+
function generateRealisticMouseTrack() {
|
|
10
|
+
const tracks = [];
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
// 生成最近5分钟内的时间点
|
|
13
|
+
const startTime = now - 5 * 60 * 1000;
|
|
14
|
+
const endTime = now;
|
|
15
|
+
// 起始点和结束点
|
|
16
|
+
const startX = Math.floor(Math.random() * 200);
|
|
17
|
+
const startY = Math.floor(Math.random() * 200);
|
|
18
|
+
const endX = 200 + Math.floor(Math.random() * 800); // 页面常见宽度范围内
|
|
19
|
+
const endY = 100 + Math.floor(Math.random() * 600); // 页面常见高度范围内
|
|
20
|
+
// 控制点(用于贝塞尔曲线)
|
|
21
|
+
const controlX = startX + (endX - startX) * 0.3 + Math.random() * 200 - 100;
|
|
22
|
+
const controlY = startY + (endY - startY) * 0.7 + Math.random() * 200 - 100;
|
|
23
|
+
// 生成轨迹点数量
|
|
24
|
+
const pointCount = 30 + Math.floor(Math.random() * 40);
|
|
25
|
+
// 时间间隔
|
|
26
|
+
let currentTime = startTime;
|
|
27
|
+
const timeStep = (endTime - startTime) / pointCount;
|
|
28
|
+
for (let i = 0; i <= pointCount; i++) {
|
|
29
|
+
const t = i / pointCount;
|
|
30
|
+
// 二次贝塞尔曲线计算当前位置
|
|
31
|
+
const x = (1 - t) * (1 - t) * startX + 2 * (1 - t) * t * controlX + t * t * endX;
|
|
32
|
+
const y = (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * controlY + t * t * endY;
|
|
33
|
+
// 计算与上一个点的偏移量
|
|
34
|
+
if (i === 0) {
|
|
35
|
+
// 第一个点,偏移量为0
|
|
36
|
+
tracks.push([0, 0, Math.floor(currentTime)]);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const prevPoint = tracks[tracks.length - 1];
|
|
40
|
+
const deltaX = Math.round(x - (prevPoint[0] + prevPoint[0])); // 简化计算
|
|
41
|
+
const deltaY = Math.round(y - (prevPoint[1] + prevPoint[1]));
|
|
42
|
+
tracks.push([deltaX, deltaY, Math.floor(currentTime)]);
|
|
43
|
+
}
|
|
44
|
+
// 更新时间戳
|
|
45
|
+
currentTime += timeStep + (Math.random() * 200 - 100); // 添加随机波动
|
|
46
|
+
}
|
|
47
|
+
return tracks;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 生成浏览器指纹数据
|
|
51
|
+
* @returns {Object} 包含浏览器指纹信息的对象
|
|
52
|
+
*/
|
|
53
|
+
function generateFingerprint() {
|
|
54
|
+
const fingerprintData = { fp: {}, bh: {}, r: {} };
|
|
55
|
+
// 浏览器指纹信息
|
|
56
|
+
fingerprintData.fp = {
|
|
57
|
+
// 版本信息
|
|
58
|
+
0: '1.2.1',
|
|
59
|
+
// 是否支持某特性(布尔值)
|
|
60
|
+
1: {
|
|
61
|
+
s: 1,
|
|
62
|
+
v: false
|
|
63
|
+
},
|
|
64
|
+
// 语言信息
|
|
65
|
+
2: {
|
|
66
|
+
s: 1,
|
|
67
|
+
v: ['lang']
|
|
68
|
+
},
|
|
69
|
+
// User Agent 字符串
|
|
70
|
+
3: {
|
|
71
|
+
s: 1,
|
|
72
|
+
v: WeiboApi.USER_AGENT.replace(/Mozilla\//g, '')
|
|
73
|
+
},
|
|
74
|
+
// 错误信息
|
|
75
|
+
4: {
|
|
76
|
+
s: 1,
|
|
77
|
+
v: "TypeError: Cannot read properties of null (reading '0')\n at W (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:14066)\n at Object.NiOqR (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:3108)\n at https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:29253\n at Array.map (<anonymous>)\n at je (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:29193)\n at Object.Qhlex (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:5250)\n at Ie (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:25296)\n at Object.NsuAP (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:4977)\n at Pe (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:24928)\n at Module.Ve [as get] (https://passport.sinaimg.cn/js/fp/1.2.1.umd.js:1:24667)"
|
|
78
|
+
},
|
|
79
|
+
// 数值型数据
|
|
80
|
+
5: {
|
|
81
|
+
s: 1,
|
|
82
|
+
v: 33
|
|
83
|
+
},
|
|
84
|
+
// 函数字符串表示
|
|
85
|
+
6: {
|
|
86
|
+
s: 1,
|
|
87
|
+
v: 'function bind() { [native code] }'
|
|
88
|
+
},
|
|
89
|
+
// 语言环境
|
|
90
|
+
7: {
|
|
91
|
+
s: 1,
|
|
92
|
+
v: [['zh-CN']]
|
|
93
|
+
},
|
|
94
|
+
// 布尔值
|
|
95
|
+
8: {
|
|
96
|
+
s: 1,
|
|
97
|
+
v: true
|
|
98
|
+
},
|
|
99
|
+
// 布尔值
|
|
100
|
+
9: {
|
|
101
|
+
s: 1,
|
|
102
|
+
v: false
|
|
103
|
+
},
|
|
104
|
+
// 布尔值
|
|
105
|
+
10: {
|
|
106
|
+
s: 1,
|
|
107
|
+
v: true
|
|
108
|
+
},
|
|
109
|
+
// 数值
|
|
110
|
+
11: {
|
|
111
|
+
s: 1,
|
|
112
|
+
v: 5
|
|
113
|
+
},
|
|
114
|
+
// 错误信息
|
|
115
|
+
12: {
|
|
116
|
+
s: -1,
|
|
117
|
+
e: ''
|
|
118
|
+
},
|
|
119
|
+
// 日期字符串
|
|
120
|
+
13: {
|
|
121
|
+
s: 1,
|
|
122
|
+
v: '20030107'
|
|
123
|
+
},
|
|
124
|
+
// 数值
|
|
125
|
+
14: {
|
|
126
|
+
s: 1,
|
|
127
|
+
v: 50
|
|
128
|
+
},
|
|
129
|
+
// 完整的 User Agent
|
|
130
|
+
15: {
|
|
131
|
+
s: 1,
|
|
132
|
+
v: WeiboApi.USER_AGENT
|
|
133
|
+
},
|
|
134
|
+
// WebGL 信息
|
|
135
|
+
16: {
|
|
136
|
+
s: 1,
|
|
137
|
+
v: {
|
|
138
|
+
vendor: 'WebKit',
|
|
139
|
+
renderer: 'WebKit WebGL'
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
// 外部对象字符串表示
|
|
143
|
+
17: {
|
|
144
|
+
s: 1,
|
|
145
|
+
v: '[object External]'
|
|
146
|
+
},
|
|
147
|
+
// 屏幕尺寸信息
|
|
148
|
+
18: {
|
|
149
|
+
s: 1,
|
|
150
|
+
v: {
|
|
151
|
+
ow: 1920,
|
|
152
|
+
oh: 1152,
|
|
153
|
+
iw: 257,
|
|
154
|
+
ih: 1031
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
// 浏览器名称
|
|
158
|
+
19: {
|
|
159
|
+
s: 1,
|
|
160
|
+
v: 'chrome'
|
|
161
|
+
},
|
|
162
|
+
// 浏览器内核
|
|
163
|
+
20: {
|
|
164
|
+
s: 1,
|
|
165
|
+
v: 'chromium'
|
|
166
|
+
},
|
|
167
|
+
// 布尔值
|
|
168
|
+
21: {
|
|
169
|
+
s: 1,
|
|
170
|
+
v: false
|
|
171
|
+
},
|
|
172
|
+
// 布尔值
|
|
173
|
+
22: {
|
|
174
|
+
s: 1,
|
|
175
|
+
v: true
|
|
176
|
+
},
|
|
177
|
+
// 触摸支持信息
|
|
178
|
+
23: {
|
|
179
|
+
s: 1,
|
|
180
|
+
v: {
|
|
181
|
+
ots: false,
|
|
182
|
+
mtp: 0,
|
|
183
|
+
mmtp: -1
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
// 行为数据(鼠标轨迹和键盘操作)
|
|
188
|
+
fingerprintData.bh = {
|
|
189
|
+
// 鼠标移动轨迹 [x偏移, y偏移, 时间戳毫秒]
|
|
190
|
+
mt: generateRealisticMouseTrack(),
|
|
191
|
+
// 键盘操作统计
|
|
192
|
+
kt: {
|
|
193
|
+
down: 0,
|
|
194
|
+
up: 0
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
// 追踪设置
|
|
198
|
+
fingerprintData.r = {
|
|
199
|
+
isTraceKeyboard: true,
|
|
200
|
+
isTraceMouse: true
|
|
201
|
+
};
|
|
202
|
+
return fingerprintData;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 生成 AES 加密密钥和初始化向量
|
|
206
|
+
* @returns {Promise<Object>} 包含 key 和 iv 的对象
|
|
207
|
+
*/
|
|
208
|
+
function generateAESKey() {
|
|
209
|
+
// 生成 AES 密钥 (16位)
|
|
210
|
+
const key = crypto.randomBytes(16);
|
|
211
|
+
// 生成初始化向量
|
|
212
|
+
const iv = crypto.randomBytes(16);
|
|
213
|
+
return {
|
|
214
|
+
key: Buffer.from(key).toString('base64'),
|
|
215
|
+
iv: Buffer.from(iv).toString('base64')
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* RSA 加密函数 (模拟浏览器 Web Crypto API)
|
|
220
|
+
* @param {string} data - 待加密数据
|
|
221
|
+
* @param {Uint8Array} publicKeyDer - DER 格式的公钥
|
|
222
|
+
* @returns {Buffer} 加密后的数据
|
|
223
|
+
*/
|
|
224
|
+
function rsaEncrypt(data, publicKeyDer) {
|
|
225
|
+
// 将 DER 格式的公钥转换为 PEM 格式
|
|
226
|
+
const base64Key = Buffer.from(publicKeyDer).toString('base64');
|
|
227
|
+
const keyLines = base64Key.match(/.{1,64}/g) ?? [];
|
|
228
|
+
const publicKeyPem = `-----BEGIN PUBLIC KEY-----\n${keyLines.join('\n')}\n-----END PUBLIC KEY-----`;
|
|
229
|
+
// 使用 RSA-OAEP-SHA256 算法加密
|
|
230
|
+
return crypto.publicEncrypt({
|
|
231
|
+
key: publicKeyPem,
|
|
232
|
+
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
|
233
|
+
oaepHash: 'sha256'
|
|
234
|
+
}, Buffer.from(data, 'binary'));
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* AES CBC 加密函数 (模拟浏览器 Web Crypto API)
|
|
238
|
+
* @param {string} data - 待加密数据
|
|
239
|
+
* @param {string} keyBase64 - Base64 编码的密钥
|
|
240
|
+
* @param {string} ivBase64 - Base64 编码的初始化向量
|
|
241
|
+
* @returns {string} Base64 编码的加密结果
|
|
242
|
+
*/
|
|
243
|
+
function aesEncrypt(data, keyBase64, ivBase64) {
|
|
244
|
+
const key = Buffer.from(keyBase64, 'base64');
|
|
245
|
+
const iv = Buffer.from(ivBase64, 'base64');
|
|
246
|
+
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
|
|
247
|
+
cipher.setAutoPadding(true);
|
|
248
|
+
let encrypted = cipher.update(data, 'utf8', 'binary');
|
|
249
|
+
encrypted += cipher.final('binary');
|
|
250
|
+
return Buffer.from(encrypted, 'binary').toString('base64');
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* 数据加密主函数
|
|
254
|
+
* @param {string} jsonData - JSON 格式的指纹数据
|
|
255
|
+
* @returns {Promise<string>} 加密后的数据
|
|
256
|
+
*/
|
|
257
|
+
async function encryptData(jsonData) {
|
|
258
|
+
// 原始的 RSA 公钥(DER 格式),与源代码完全一致
|
|
259
|
+
const publicKeyDer = new Uint8Array([
|
|
260
|
+
48, 129, 159, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 129, 141, 0, 48, 129, 137, 2, 129, 129, 0, 180, 249, 101, 74, 227, 247, 222, 230,
|
|
261
|
+
24, 220, 10, 149, 183, 131, 164, 185, 20, 166, 164, 114, 158, 71, 46, 151, 77, 71, 226, 23, 78, 67, 177, 246, 197, 249, 213, 39, 243, 55, 38, 112, 17, 64,
|
|
262
|
+
135, 155, 109, 50, 185, 61, 21, 105, 106, 245, 148, 212, 127, 7, 18, 227, 255, 40, 199, 241, 65, 211, 167, 185, 232, 5, 186, 189, 245, 59, 161, 214, 48,
|
|
263
|
+
160, 251, 21, 92, 187, 172, 83, 152, 11, 85, 72, 37, 137, 87, 104, 63, 39, 86, 6, 150, 84, 6, 178, 229, 220, 144, 133, 131, 212, 47, 139, 232, 185, 192, 97,
|
|
264
|
+
89, 137, 170, 141, 39, 19, 85, 4, 153, 238, 75, 93, 243, 96, 206, 72, 135, 91, 2, 3, 1, 0, 1
|
|
265
|
+
]);
|
|
266
|
+
// 生成 AES 密钥和 IV
|
|
267
|
+
const { key, iv } = generateAESKey();
|
|
268
|
+
// RSA 加密 AES 密钥(将密钥重复两次后加密)
|
|
269
|
+
const keyBinary = Buffer.from(key, 'base64').toString('binary');
|
|
270
|
+
const encryptedKey = rsaEncrypt(keyBinary + keyBinary, publicKeyDer);
|
|
271
|
+
// AES 加密数据
|
|
272
|
+
const encryptedData = aesEncrypt(jsonData, key, iv);
|
|
273
|
+
// 组合加密数据(与原始代码保持一致的格式)
|
|
274
|
+
const result = '01' + Buffer.from('01' + encryptedKey.toString('binary') + '02' + Buffer.from(encryptedData, 'base64').toString('binary')).toString('base64');
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 生成请求载荷
|
|
279
|
+
* @returns {Promise<string>} 加密后的载荷数据
|
|
280
|
+
*/
|
|
281
|
+
async function genBdPayload() {
|
|
282
|
+
const fingerprint = generateFingerprint();
|
|
283
|
+
const jsonString = JSON.stringify(fingerprint);
|
|
284
|
+
const encryptedData = await encryptData(jsonString);
|
|
285
|
+
return encryptedData;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* 访问bd接口获取rid
|
|
289
|
+
* @param {string} X_CSRF_TOKEN - X_CSRF_TOKEN
|
|
290
|
+
* @returns {Promise<JSON>} 服务器响应结果
|
|
291
|
+
*/
|
|
292
|
+
async function getRidFromBd(X_CSRF_TOKEN) {
|
|
293
|
+
const params = new URLSearchParams();
|
|
294
|
+
const payload = await genBdPayload();
|
|
295
|
+
payload && params.append('data', payload);
|
|
296
|
+
params.append('from', 'weibo');
|
|
297
|
+
const ridData = (await fetch('https://passport.weibo.com/sso/bd', {
|
|
298
|
+
method: 'POST',
|
|
299
|
+
headers: lodash.merge(WeiboApi.WEIBO_GET_BD_TOKEN_HEADERS, {
|
|
300
|
+
Origin: 'https://passport.weibo.com',
|
|
301
|
+
Cookie: `X-CSRF-TOKEN=${X_CSRF_TOKEN}`
|
|
302
|
+
}),
|
|
303
|
+
body: params,
|
|
304
|
+
redirect: 'follow'
|
|
305
|
+
}).then(res => res.json()));
|
|
306
|
+
return ridData;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export { genBdPayload, getRidFromBd };
|
|
@@ -53,7 +53,9 @@ class YukiPuppeteerRender {
|
|
|
53
53
|
pageHeight = Math.round(boundingBox.height / num); //动态调整分片高度,防止过短影响观感。
|
|
54
54
|
await page.setViewport({ width: boundingBox.width + 50, height: pageHeight + 100 });
|
|
55
55
|
// 禁止 GIF 动图播放
|
|
56
|
-
|
|
56
|
+
if (Options?.isPauseGif === true) {
|
|
57
|
+
await page.addStyleTag({ content: `img[src$=".gif"] {animation-play-state: paused !important;}` });
|
|
58
|
+
}
|
|
57
59
|
// 是否保存 html 文件
|
|
58
60
|
if (Options?.saveHtmlfile === true) {
|
|
59
61
|
const htmlContent = await page.content();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yz-yuki-plugin",
|
|
3
|
-
"version": "2.0.8-
|
|
3
|
+
"version": "2.0.8-10",
|
|
4
4
|
"description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
|
|
5
5
|
"author": "snowtafir",
|
|
6
6
|
"type": "module",
|
|
@@ -28,10 +28,11 @@
|
|
|
28
28
|
"axios": "^1.7.9",
|
|
29
29
|
"chalk": "^5.3.0",
|
|
30
30
|
"chokidar": "4.0.1",
|
|
31
|
+
"cookie": "^1.0.2",
|
|
31
32
|
"debug": "^4.3.6",
|
|
32
33
|
"jsdom": "^25.0.1",
|
|
33
34
|
"json5": "^2.2.3",
|
|
34
|
-
"jsxp": "^1.2.
|
|
35
|
+
"jsxp": "^1.2.3",
|
|
35
36
|
"lodash": "^4.17.21",
|
|
36
37
|
"md5": "^2.3.0",
|
|
37
38
|
"moment": "^2.30.1",
|
|
@@ -63,9 +64,9 @@
|
|
|
63
64
|
"icqq": "^0.6.10",
|
|
64
65
|
"jsdom": "^24.1.1",
|
|
65
66
|
"json5": "^2.2.3",
|
|
66
|
-
"jsxp": "^1.2.
|
|
67
|
+
"jsxp": "^1.2.3",
|
|
67
68
|
"lodash": "^4.17.21",
|
|
68
|
-
"lvyjs": "^0.2.
|
|
69
|
+
"lvyjs": "^0.2.21",
|
|
69
70
|
"md5": "^2.3.0",
|
|
70
71
|
"node-fetch": "^3.3.2",
|
|
71
72
|
"postcss": "^8.4.47",
|
|
@@ -28,7 +28,7 @@ body::-webkit-scrollbar {
|
|
|
28
28
|
@import url('https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css');
|
|
29
29
|
|
|
30
30
|
body {
|
|
31
|
-
font-family: 'OPSans', 'HarmonyOS_Regular', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
|
|
31
|
+
font-family: 'OPSans', 'HarmonyOS_Regular', 'Noto Sans', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
|
|
32
32
|
background-color: #f9f9f9;
|
|
33
33
|
margin: 0;
|
|
34
34
|
padding: 0;
|