yz-yuki-plugin 2.0.8-1 → 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/help/help.yaml +21 -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 +13 -4
- 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 +14 -5
- package/lib/models/weibo/weibo.risk.bd.rid.js +309 -0
- package/package.json +5 -4
- package/resources/css/dynamic/MainPage.css +1 -1
|
@@ -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 };
|
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;
|