yz-yuki-plugin 2.0.5-9 → 2.0.6-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 +6 -0
- package/defaultConfig/bilibili/config.yaml +9 -0
- package/defaultConfig/weibo/config.yaml +3 -0
- package/lib/apps/bilibili.js +44 -37
- package/lib/index.js +1 -1
- package/lib/models/bilibili/bilibili.main.api.js +402 -0
- package/lib/models/bilibili/{bilibili.get.web.data.js → bilibili.main.get.web.data.js} +8 -7
- package/lib/models/bilibili/{bilibili.models.js → bilibili.main.models.js} +81 -250
- package/lib/models/bilibili/{bilibili.query.js → bilibili.main.query.js} +114 -92
- package/lib/models/bilibili/{bilibili.task.js → bilibili.main.task.js} +39 -15
- package/lib/models/bilibili/bilibili.risk.buid.fp.js +282 -0
- package/lib/models/bilibili/{bilibili.dm.img.js → bilibili.risk.dm.img.js} +2 -2
- package/lib/models/bilibili/{bilibili.ticket.js → bilibili.risk.ticket.js} +1 -1
- package/lib/models/bilibili/{bilibili.w_webid.js → bilibili.risk.w_webid.js} +4 -4
- package/lib/models/bilibili/bilibili.risk.wbi.js +102 -0
- package/lib/models/weibo/weibo.get.web.data.js +1 -2
- package/lib/models/weibo/weibo.query.js +19 -21
- package/lib/models/weibo/weibo.task.js +27 -11
- package/lib/utils/config.js +1 -1
- package/lib/utils/image.js +1 -1
- package/lib/utils/puppeteer.render.js +2 -0
- package/package.json +13 -11
- package/lib/models/bilibili/bilibili.api.js +0 -93
- package/lib/models/bilibili/bilibili.buid.fp.js +0 -77
- package/lib/models/bilibili/bilibili.wbi.js +0 -61
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Contains code from open-source projects:
|
|
3
|
+
* MurmurHash3 by Karan Lyons (https://github.com/karanlyons/murmurHash3.js)
|
|
4
|
+
*/
|
|
5
|
+
class MurmurHash3 {
|
|
6
|
+
// x64Add 函数:将两个64位整数相加
|
|
7
|
+
//
|
|
8
|
+
// Given two 64bit ints (as an array of two 32bit ints) returns the two
|
|
9
|
+
// added together as a 64bit int (as an array of two 32bit ints).
|
|
10
|
+
//
|
|
11
|
+
static x64Add = function (m, n) {
|
|
12
|
+
m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff];
|
|
13
|
+
n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff];
|
|
14
|
+
var o = [0, 0, 0, 0];
|
|
15
|
+
o[3] += m[3] + n[3];
|
|
16
|
+
o[2] += o[3] >>> 16;
|
|
17
|
+
o[3] &= 0xffff;
|
|
18
|
+
o[2] += m[2] + n[2];
|
|
19
|
+
o[1] += o[2] >>> 16;
|
|
20
|
+
o[2] &= 0xffff;
|
|
21
|
+
o[1] += m[1] + n[1];
|
|
22
|
+
o[0] += o[1] >>> 16;
|
|
23
|
+
o[1] &= 0xffff;
|
|
24
|
+
o[0] += m[0] + n[0];
|
|
25
|
+
o[0] &= 0xffff;
|
|
26
|
+
return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]];
|
|
27
|
+
};
|
|
28
|
+
// x64Multiply 函数:将两个64位整数相乘
|
|
29
|
+
//
|
|
30
|
+
// Given two 64bit ints (as an array of two 32bit ints) returns the two
|
|
31
|
+
// multiplied together as a 64bit int (as an array of two 32bit ints).
|
|
32
|
+
//
|
|
33
|
+
static x64Multiply = function (m, n) {
|
|
34
|
+
m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff];
|
|
35
|
+
n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff];
|
|
36
|
+
var o = [0, 0, 0, 0];
|
|
37
|
+
o[3] += m[3] * n[3];
|
|
38
|
+
o[2] += o[3] >>> 16;
|
|
39
|
+
o[3] &= 0xffff;
|
|
40
|
+
o[2] += m[2] * n[3];
|
|
41
|
+
o[1] += o[2] >>> 16;
|
|
42
|
+
o[2] &= 0xffff;
|
|
43
|
+
o[2] += m[3] * n[2];
|
|
44
|
+
o[1] += o[2] >>> 16;
|
|
45
|
+
o[2] &= 0xffff;
|
|
46
|
+
o[1] += m[1] * n[3];
|
|
47
|
+
o[0] += o[1] >>> 16;
|
|
48
|
+
o[1] &= 0xffff;
|
|
49
|
+
o[1] += m[2] * n[2];
|
|
50
|
+
o[0] += o[1] >>> 16;
|
|
51
|
+
o[1] &= 0xffff;
|
|
52
|
+
o[1] += m[3] * n[1];
|
|
53
|
+
o[0] += o[1] >>> 16;
|
|
54
|
+
o[1] &= 0xffff;
|
|
55
|
+
o[0] += m[0] * n[3] + m[1] * n[2] + m[2] * n[1] + m[3] * n[0];
|
|
56
|
+
o[0] &= 0xffff;
|
|
57
|
+
return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]];
|
|
58
|
+
};
|
|
59
|
+
// x64Rotl 函数:将给定64位整数左移
|
|
60
|
+
//
|
|
61
|
+
// Given a 64bit int (as an array of two 32bit ints) and an int
|
|
62
|
+
// representing a number of bit positions, returns the 64bit int (as an
|
|
63
|
+
// array of two 32bit ints) rotated left by that number of positions.
|
|
64
|
+
//
|
|
65
|
+
static x64Rotl = function (m, n) {
|
|
66
|
+
n %= 64;
|
|
67
|
+
if (n === 32) {
|
|
68
|
+
return [m[1], m[0]];
|
|
69
|
+
}
|
|
70
|
+
else if (n < 32) {
|
|
71
|
+
return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))];
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
n -= 32;
|
|
75
|
+
return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))];
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
// x64LeftShift 函数:将64位整数左移
|
|
79
|
+
//
|
|
80
|
+
// Given a 64bit int (as an array of two 32bit ints) and an int
|
|
81
|
+
// representing a number of bit positions, returns the 64bit int (as an
|
|
82
|
+
// array of two 32bit ints) shifted left by that number of positions.
|
|
83
|
+
//
|
|
84
|
+
static x64LeftShift = function (m, n) {
|
|
85
|
+
n %= 64;
|
|
86
|
+
if (n === 0) {
|
|
87
|
+
return m;
|
|
88
|
+
}
|
|
89
|
+
else if (n < 32) {
|
|
90
|
+
return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n];
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return [m[1] << (n - 32), 0];
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
// x64Xor 函数:返回两个64位整数的异或结果
|
|
97
|
+
//
|
|
98
|
+
// Given two 64bit ints (as an array of two 32bit ints) returns the two
|
|
99
|
+
// xored together as a 64bit int (as an array of two 32bit ints).
|
|
100
|
+
//
|
|
101
|
+
static x64Xor = function (m, n) {
|
|
102
|
+
return [m[0] ^ n[0], m[1] ^ n[1]];
|
|
103
|
+
};
|
|
104
|
+
// x64Fmix 函数:MurmurHash3的最终混合步骤
|
|
105
|
+
//
|
|
106
|
+
// Given a block, returns murmurHash3's final x64 mix of that block.
|
|
107
|
+
// (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the
|
|
108
|
+
// only place where we need to right shift 64bit ints.)
|
|
109
|
+
//
|
|
110
|
+
static x64Fmix = function (h) {
|
|
111
|
+
h = MurmurHash3.x64Xor(h, [0, h[0] >>> 1]);
|
|
112
|
+
h = MurmurHash3.x64Multiply(h, [0xff51afd7, 0xed558ccd]);
|
|
113
|
+
h = MurmurHash3.x64Xor(h, [0, h[0] >>> 1]);
|
|
114
|
+
h = MurmurHash3.x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]);
|
|
115
|
+
h = MurmurHash3.x64Xor(h, [0, h[0] >>> 1]);
|
|
116
|
+
return h;
|
|
117
|
+
};
|
|
118
|
+
// x64hash128 函数:生成128位哈希
|
|
119
|
+
//
|
|
120
|
+
// Given a string and an optional seed as an int, returns a 128 bit
|
|
121
|
+
// hash using the x64 flavor of MurmurHash3, as an unsigned hex.
|
|
122
|
+
//
|
|
123
|
+
static x64hash128 = function (key, seed) {
|
|
124
|
+
key = key || '';
|
|
125
|
+
seed = seed || 0;
|
|
126
|
+
var remainder = key.length % 16;
|
|
127
|
+
var bytes = key.length - remainder;
|
|
128
|
+
var h1 = [0, seed];
|
|
129
|
+
var h2 = [0, seed];
|
|
130
|
+
var k1 = [0, 0];
|
|
131
|
+
var k2 = [0, 0];
|
|
132
|
+
var c1 = [0x87c37b91, 0x114253d5];
|
|
133
|
+
var c2 = [0x4cf5ad43, 0x2745937f];
|
|
134
|
+
for (var i = 0; i < bytes; i += 16) {
|
|
135
|
+
k1 = [
|
|
136
|
+
(key.charCodeAt(i + 4) & 0xff) |
|
|
137
|
+
((key.charCodeAt(i + 5) & 0xff) << 8) |
|
|
138
|
+
((key.charCodeAt(i + 6) & 0xff) << 16) |
|
|
139
|
+
((key.charCodeAt(i + 7) & 0xff) << 24),
|
|
140
|
+
(key.charCodeAt(i) & 0xff) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24)
|
|
141
|
+
];
|
|
142
|
+
k2 = [
|
|
143
|
+
(key.charCodeAt(i + 12) & 0xff) |
|
|
144
|
+
((key.charCodeAt(i + 13) & 0xff) << 8) |
|
|
145
|
+
((key.charCodeAt(i + 14) & 0xff) << 16) |
|
|
146
|
+
((key.charCodeAt(i + 15) & 0xff) << 24),
|
|
147
|
+
(key.charCodeAt(i + 8) & 0xff) |
|
|
148
|
+
((key.charCodeAt(i + 9) & 0xff) << 8) |
|
|
149
|
+
((key.charCodeAt(i + 10) & 0xff) << 16) |
|
|
150
|
+
((key.charCodeAt(i + 11) & 0xff) << 24)
|
|
151
|
+
];
|
|
152
|
+
// 处理 k1 和 k2
|
|
153
|
+
k1 = MurmurHash3.x64Multiply(k1, c1);
|
|
154
|
+
k1 = MurmurHash3.x64Rotl(k1, 31);
|
|
155
|
+
k1 = MurmurHash3.x64Multiply(k1, c2);
|
|
156
|
+
h1 = MurmurHash3.x64Xor(h1, k1);
|
|
157
|
+
h1 = MurmurHash3.x64Rotl(h1, 27);
|
|
158
|
+
h1 = MurmurHash3.x64Add(h1, h2);
|
|
159
|
+
h1 = MurmurHash3.x64Add(MurmurHash3.x64Multiply(h1, [0, 5]), [0, 0x52dce729]);
|
|
160
|
+
k2 = MurmurHash3.x64Multiply(k2, c2);
|
|
161
|
+
k2 = MurmurHash3.x64Rotl(k2, 33);
|
|
162
|
+
k2 = MurmurHash3.x64Multiply(k2, c1);
|
|
163
|
+
h2 = MurmurHash3.x64Xor(h2, k2);
|
|
164
|
+
h2 = MurmurHash3.x64Rotl(h2, 31);
|
|
165
|
+
h2 = MurmurHash3.x64Add(h2, h1);
|
|
166
|
+
h2 = MurmurHash3.x64Add(MurmurHash3.x64Multiply(h2, [0, 5]), [0, 0x38495ab5]);
|
|
167
|
+
}
|
|
168
|
+
k1 = [0, 0];
|
|
169
|
+
k2 = [0, 0];
|
|
170
|
+
switch (remainder) {
|
|
171
|
+
case 15:
|
|
172
|
+
k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, String(key).charCodeAt(i + 14)], 48));
|
|
173
|
+
// fallthrough
|
|
174
|
+
case 14:
|
|
175
|
+
k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 13)], 40));
|
|
176
|
+
// fallthrough
|
|
177
|
+
case 13:
|
|
178
|
+
k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 12)], 32));
|
|
179
|
+
// fallthrough
|
|
180
|
+
case 12:
|
|
181
|
+
k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 11)], 24));
|
|
182
|
+
// fallthrough
|
|
183
|
+
case 11:
|
|
184
|
+
k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 10)], 16));
|
|
185
|
+
// fallthrough
|
|
186
|
+
case 10:
|
|
187
|
+
k2 = MurmurHash3.x64Xor(k2, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 9)], 8));
|
|
188
|
+
// fallthrough
|
|
189
|
+
case 9:
|
|
190
|
+
k2 = MurmurHash3.x64Xor(k2, [0, key.charCodeAt(i + 8)]);
|
|
191
|
+
k2 = MurmurHash3.x64Multiply(k2, c2);
|
|
192
|
+
k2 = MurmurHash3.x64Rotl(k2, 33);
|
|
193
|
+
k2 = MurmurHash3.x64Multiply(k2, c1);
|
|
194
|
+
h2 = MurmurHash3.x64Xor(h2, k2);
|
|
195
|
+
// fallthrough
|
|
196
|
+
case 8:
|
|
197
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 7)], 56));
|
|
198
|
+
// fallthrough
|
|
199
|
+
case 7:
|
|
200
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 6)], 48));
|
|
201
|
+
// fallthrough
|
|
202
|
+
case 6:
|
|
203
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 5)], 40));
|
|
204
|
+
// fallthrough
|
|
205
|
+
case 5:
|
|
206
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 4)], 32));
|
|
207
|
+
// fallthrough
|
|
208
|
+
case 4:
|
|
209
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 3)], 24));
|
|
210
|
+
// fallthrough
|
|
211
|
+
case 3:
|
|
212
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 2)], 16));
|
|
213
|
+
// fallthrough
|
|
214
|
+
case 2:
|
|
215
|
+
k1 = MurmurHash3.x64Xor(k1, MurmurHash3.x64LeftShift([0, key.charCodeAt(i + 1)], 8));
|
|
216
|
+
// fallthrough
|
|
217
|
+
case 1:
|
|
218
|
+
k1 = MurmurHash3.x64Xor(k1, [0, key.charCodeAt(i)]);
|
|
219
|
+
k1 = MurmurHash3.x64Multiply(k1, c1);
|
|
220
|
+
k1 = MurmurHash3.x64Rotl(k1, 31);
|
|
221
|
+
k1 = MurmurHash3.x64Multiply(k1, c2);
|
|
222
|
+
h1 = MurmurHash3.x64Xor(h1, k1);
|
|
223
|
+
// fallthrough
|
|
224
|
+
}
|
|
225
|
+
h1 = MurmurHash3.x64Xor(h1, [0, key.length]);
|
|
226
|
+
h2 = MurmurHash3.x64Xor(h2, [0, key.length]);
|
|
227
|
+
h1 = MurmurHash3.x64Add(h1, h2);
|
|
228
|
+
h2 = MurmurHash3.x64Add(h2, h1);
|
|
229
|
+
h1 = MurmurHash3.x64Fmix(h1);
|
|
230
|
+
h2 = MurmurHash3.x64Fmix(h2);
|
|
231
|
+
h1 = MurmurHash3.x64Add(h1, h2);
|
|
232
|
+
h2 = MurmurHash3.x64Add(h2, h1);
|
|
233
|
+
return (('00000000' + (h1[0] >>> 0).toString(16)).slice(-8) +
|
|
234
|
+
('00000000' + (h1[1] >>> 0).toString(16)).slice(-8) +
|
|
235
|
+
('00000000' + (h2[0] >>> 0).toString(16)).slice(-8) +
|
|
236
|
+
('00000000' + (h2[1] >>> 0).toString(16)).slice(-8));
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function gen_buvid_fp(browserData) {
|
|
240
|
+
// 将所有自定义数据整合
|
|
241
|
+
const components = [
|
|
242
|
+
{ key: 'userAgent', value: browserData.userAgent },
|
|
243
|
+
{ key: 'webdriver', value: browserData.webdriver }, // WebDriver 信息
|
|
244
|
+
{ key: 'language', value: browserData.language },
|
|
245
|
+
{ key: 'colorDepth', value: browserData.colorDepth },
|
|
246
|
+
{ key: 'deviceMemory', value: browserData.deviceMemory },
|
|
247
|
+
{ key: 'pixelRatio', value: browserData.pixelRatio }, // 设备像素比
|
|
248
|
+
{ key: 'hardwareConcurrency', value: browserData.hardwareConcurrency },
|
|
249
|
+
{ key: 'screenResolution', value: browserData.screenResolution }, // 屏幕分辨率
|
|
250
|
+
{ key: 'availableScreenResolution', value: browserData.availableScreenResolution }, // 可用屏幕分辨率
|
|
251
|
+
{ key: 'timezoneOffset', value: browserData.timezoneOffset }, // 时区偏移
|
|
252
|
+
{ key: 'timezone', value: browserData.timezone },
|
|
253
|
+
{ key: 'sessionStorage', value: browserData.sessionStorage ? 1 : 0 }, // sessionStorage 支持
|
|
254
|
+
{ key: 'localStorage', value: browserData.localStorage ? 1 : 0 }, // localStorage 支持
|
|
255
|
+
{ key: 'indexedDb', value: browserData.indexedDb ? 1 : 0 }, // IndexedDB 支持
|
|
256
|
+
{ key: 'addBehavior', value: browserData.addBehavior ? 1 : 0 }, // addBehavior 支持
|
|
257
|
+
{ key: 'openDatabase', value: browserData.openDatabase ? 1 : 0 }, // openDatabase 支持
|
|
258
|
+
{ key: 'cpuClass', value: browserData.cpuClass },
|
|
259
|
+
{ key: 'platform', value: browserData.platform },
|
|
260
|
+
{ key: 'doNotTrack', value: browserData.doNotTrack },
|
|
261
|
+
{ key: 'plugins', value: browserData.plugins.map(p => p.name).join(',') }, // 插件名称
|
|
262
|
+
{ key: 'canvas', value: browserData.canvas },
|
|
263
|
+
{ key: 'webgl', value: browserData.webgl },
|
|
264
|
+
{ key: 'webglVendorAndRenderer', value: browserData.webglVendorAndRenderer },
|
|
265
|
+
{ key: 'adBlock', value: browserData.adBlock ? 1 : 0 }, // 是否存在广告拦截器
|
|
266
|
+
{ key: 'hasLiedLanguages', value: browserData.hasLiedLanguages ? 1 : 0 },
|
|
267
|
+
{ key: 'hasLiedResolution', value: browserData.hasLiedResolution ? 1 : 0 },
|
|
268
|
+
{ key: 'hasLiedOs', value: browserData.hasLiedOs ? 1 : 0 },
|
|
269
|
+
{ key: 'hasLiedBrowser', value: browserData.hasLiedBrowser ? 1 : 0 },
|
|
270
|
+
{ key: 'touchSupport', value: browserData.touchSupport }, // 支持的触摸点数
|
|
271
|
+
{ key: 'fonts', value: browserData.fonts.map(f => f.replace(/\s+/g, '')).join(',') }, // 字体列表
|
|
272
|
+
{ key: 'fontsFlash', value: browserData.hasLiedOs ? browserData.fonts.map(f => f.replace(/\s+/g, '')).join(',') : 'flash not installed' },
|
|
273
|
+
{ key: 'audio', value: browserData.audio }, // 音频指纹
|
|
274
|
+
{ key: 'enumerateDevices', value: browserData.enumerateDevices.map(f => f.replace(/\s+/g, '')).join(',') }
|
|
275
|
+
];
|
|
276
|
+
const values = components.map(component => component.value).join('~~~');
|
|
277
|
+
// 使用 MurmurHash3 计算指纹
|
|
278
|
+
const fingerprint = MurmurHash3.x64hash128(values, 31); // 调用之前定义的 x64hash128 函数
|
|
279
|
+
return fingerprint;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export { gen_buvid_fp };
|
|
@@ -7,10 +7,10 @@ async function getDmImg() {
|
|
|
7
7
|
const dm_cover_img_str = 'QU5HTEUgKEludGVsLCBJbnRlbChSKSBIRCBHcmFwaGljcyBEaXJlY3QzRDExIHZzXzVfMCBwc181XzApLCBvciBzaW1pbGFyR29vZ2xlIEluYy4gKEludGVsKQ';
|
|
8
8
|
const dm_img_inter = { ds: [], wh: [0, 0, 0], of: [0, 0, 0] };
|
|
9
9
|
return {
|
|
10
|
-
dm_img_list: dm_img_list,
|
|
10
|
+
dm_img_list: String(dm_img_list).replace(/\s+/g, ''),
|
|
11
11
|
dm_img_str: dm_img_str,
|
|
12
12
|
dm_cover_img_str: dm_cover_img_str,
|
|
13
|
-
dm_img_inter: dm_img_inter
|
|
13
|
+
dm_img_inter: String(dm_img_inter).replace(/\s+/g, '')
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import lodash from 'lodash';
|
|
3
3
|
import { Redis } from 'yunzaijs';
|
|
4
|
-
import
|
|
5
|
-
import { readSyncCookie, cookieWithBiliTicket } from './bilibili.models.js';
|
|
4
|
+
import BiliApi from './bilibili.main.api.js';
|
|
5
|
+
import { readSyncCookie, cookieWithBiliTicket } from './bilibili.main.models.js';
|
|
6
6
|
|
|
7
7
|
async function getWebId(uid) {
|
|
8
8
|
const w_webid_key = 'Yz:yuki:bili:w_webid';
|
|
9
9
|
const w_webid = await Redis.get(w_webid_key);
|
|
10
10
|
if (w_webid) {
|
|
11
|
-
return w_webid;
|
|
11
|
+
return String(w_webid);
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
14
|
const url = `https://space.bilibili.com/${uid ? uid : 401742377}/dynamic`;
|
|
@@ -30,7 +30,7 @@ async function getWebId(uid) {
|
|
|
30
30
|
const access_id = decoded__RENDER_DATA__JsonString.match(accessIdRegex);
|
|
31
31
|
if (access_id && access_id[1]) {
|
|
32
32
|
await Redis.set(w_webid_key, access_id[1], { EX: 43197 * 1000 });
|
|
33
|
-
return access_id[1];
|
|
33
|
+
return String(access_id[1]);
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
36
|
console.error('Failed to get access_id from __RENDER_DATA__');
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import md5 from 'md5';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
import { Redis } from 'yunzaijs';
|
|
4
|
+
import BiliApi from './bilibili.main.api.js';
|
|
5
|
+
|
|
6
|
+
const mixinKeyEncTab = [
|
|
7
|
+
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26,
|
|
8
|
+
17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52
|
|
9
|
+
];
|
|
10
|
+
// 对 imgKey 和 subKey 进行字符顺序打乱编码
|
|
11
|
+
const getMixinKey = (orig) => mixinKeyEncTab
|
|
12
|
+
.map(n => orig[n])
|
|
13
|
+
.join('')
|
|
14
|
+
.slice(0, 32);
|
|
15
|
+
// 为请求参数进行 wbi 签名
|
|
16
|
+
function encWbi(params, img_key, sub_key) {
|
|
17
|
+
const mixin_key = getMixinKey(img_key + sub_key), curr_time = Math.round(Date.now() / 1000), chr_filter = /[!'()*]/g;
|
|
18
|
+
Object.assign(params, { wts: curr_time }); // 添加 wts 字段
|
|
19
|
+
// 按照 key 重排参数
|
|
20
|
+
const query = Object.keys(params)
|
|
21
|
+
.sort()
|
|
22
|
+
.map(key => {
|
|
23
|
+
// 过滤 value 中的 "!'()*" 字符
|
|
24
|
+
const value = params[key].toString().replace(chr_filter, '');
|
|
25
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
26
|
+
})
|
|
27
|
+
.join('&');
|
|
28
|
+
const wbi_sign = md5(query + mixin_key); // 计算 w_rid
|
|
29
|
+
//return query + "&w_rid=" + wbi_sign;
|
|
30
|
+
return {
|
|
31
|
+
query: query,
|
|
32
|
+
w_rid: wbi_sign,
|
|
33
|
+
time_stamp: curr_time
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// 获取最新的 img_key 和 sub_key
|
|
37
|
+
async function getWbiKeys(headers, cookie) {
|
|
38
|
+
const IMG_SUB_KEY = 'Yz:yuki:bili:wbi_img_key';
|
|
39
|
+
const wbi_img_data = await Redis.get(IMG_SUB_KEY);
|
|
40
|
+
if (wbi_img_data) {
|
|
41
|
+
const wbi_img_data_json = JSON.parse(wbi_img_data);
|
|
42
|
+
return {
|
|
43
|
+
img_key: wbi_img_data_json.img_key,
|
|
44
|
+
sub_key: wbi_img_data_json.sub_key
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const res = await fetch('https://api.bilibili.com/x/web-interface/nav', {
|
|
49
|
+
headers: {
|
|
50
|
+
// SESSDATA 字段
|
|
51
|
+
'Cookie': cookie,
|
|
52
|
+
'User-Agent': headers['User-Agent'],
|
|
53
|
+
'Referer': 'https://www.bilibili.com/' //对于直接浏览器调用可能不适用
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const { data: { wbi_img: { img_url, sub_url } } } = (await res.json());
|
|
57
|
+
const wbi_img_data = {
|
|
58
|
+
img_key: img_url.slice(img_url.lastIndexOf('/') + 1, img_url.lastIndexOf('.')),
|
|
59
|
+
sub_key: sub_url.slice(sub_url.lastIndexOf('/') + 1, sub_url.lastIndexOf('.'))
|
|
60
|
+
};
|
|
61
|
+
const { microtime } = await getTimeStamp(); // 获取的 microtime 已经是北京时间(毫秒)
|
|
62
|
+
// 创建当前北京时间的 Date 对象
|
|
63
|
+
const current_zh_cn_Time = new Date(microtime);
|
|
64
|
+
// 打印当前北京时间
|
|
65
|
+
console.log(`当前北京时间: ${current_zh_cn_Time}`);
|
|
66
|
+
// 创建明天0点的时间对象
|
|
67
|
+
const tomorrow = new Date(current_zh_cn_Time);
|
|
68
|
+
tomorrow.setHours(0, 0, 0, 0); // 设置明天的0点
|
|
69
|
+
tomorrow.setDate(tomorrow.getDate() + 1); // 日期加1,表示明天
|
|
70
|
+
// 计算剩余秒数
|
|
71
|
+
const secondsUntilTomorrow = Math.floor((tomorrow.getTime() - current_zh_cn_Time.getTime()) / 1000);
|
|
72
|
+
await Redis.set(IMG_SUB_KEY, JSON.stringify(wbi_img_data), { EX: secondsUntilTomorrow - 2 }); // 设置缓存,过期时间为第二天0点前2秒
|
|
73
|
+
return wbi_img_data;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**获取适用于 RTC 的时间戳*/
|
|
77
|
+
async function getTimeStamp() {
|
|
78
|
+
const res = await fetch(BiliApi.BILIBIL_API.biliServerTimeStamp, {
|
|
79
|
+
method: 'GET',
|
|
80
|
+
headers: {
|
|
81
|
+
'Host': 'api.live.bilibili.com',
|
|
82
|
+
'User-Agent': `${BiliApi.USER_AGENT}`
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const { data: { timestamp, microtime } } = (await res.json());
|
|
86
|
+
return {
|
|
87
|
+
timestamp: timestamp,
|
|
88
|
+
microtime: microtime
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/misc/sign/wbi.md#javascript
|
|
93
|
+
* 对实际请求参数进行 wbi 签名, 生成 wbi 签名
|
|
94
|
+
* @param {object} params 除了 wbi 签名外的全部请求参数,例如 api get请求的查询参数 { uid: 12345678, jsonp: jsonp}
|
|
95
|
+
* @param {object} headers 必需要 referer 和 UA 两个请求头
|
|
96
|
+
*/
|
|
97
|
+
async function getWbiSign(params, headers, cookie) {
|
|
98
|
+
const { img_key, sub_key } = await getWbiKeys(headers, cookie);
|
|
99
|
+
return encWbi(params, img_key, sub_key);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { getWbiSign };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { Bot } from 'yunzaijs';
|
|
3
2
|
import { WeiboApi } from './weibo.api.js';
|
|
4
3
|
import { WeiboQuery } from './weibo.query.js';
|
|
5
4
|
|
|
@@ -48,7 +47,7 @@ class WeiboGetWebData {
|
|
|
48
47
|
return data.cards.filter(WeiboQuery.filterCardTypeCustom);
|
|
49
48
|
}
|
|
50
49
|
catch (error) {
|
|
51
|
-
|
|
50
|
+
global?.logger?.mark('微博推送:Error fetching sub list:', error);
|
|
52
51
|
return [];
|
|
53
52
|
}
|
|
54
53
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import moment from 'moment';
|
|
2
2
|
import fetch from 'node-fetch';
|
|
3
3
|
import { WeiboApi } from './weibo.api.js';
|
|
4
|
-
import {
|
|
4
|
+
import { Segment } from 'yunzaijs';
|
|
5
5
|
import { JSDOM } from 'jsdom';
|
|
6
6
|
|
|
7
7
|
class WeiboQuery {
|
|
@@ -174,11 +174,11 @@ class WeiboQuery {
|
|
|
174
174
|
static async formatTextDynamicData(upName, raw_post, isForward, setData) {
|
|
175
175
|
let msg = [],
|
|
176
176
|
/**全部图片资源链接*/
|
|
177
|
-
raw_pics_list,
|
|
177
|
+
raw_pics_list = [],
|
|
178
178
|
/**图片高清资源链接*/
|
|
179
|
-
pic_urls,
|
|
179
|
+
pic_urls = [],
|
|
180
180
|
/**图片*/
|
|
181
|
-
pics;
|
|
181
|
+
pics = [];
|
|
182
182
|
let info = raw_post?.mblog || raw_post;
|
|
183
183
|
let retweeted = info && info.retweeted_status ? true : false; //是否为转发动态
|
|
184
184
|
let pic_num = retweeted ? info?.retweeted_status?.pic_num : info?.pic_num;
|
|
@@ -195,7 +195,7 @@ class WeiboQuery {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
catch (err) {
|
|
198
|
-
|
|
198
|
+
global?.logger?.mark(`优纪插件:获取微博动态全文出错:https://m.weibo.cn/detail/${info?.mid}`);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
/**动态发布时间 */
|
|
@@ -216,10 +216,10 @@ class WeiboQuery {
|
|
|
216
216
|
`标题:${info?.page_info?.title || ''}\n`,
|
|
217
217
|
`${this.filterText(info?.text)}\n`,
|
|
218
218
|
`链接:${detail_url}\n`,
|
|
219
|
-
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}
|
|
220
|
-
cover_img
|
|
219
|
+
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
|
|
221
220
|
];
|
|
222
|
-
|
|
221
|
+
pics = [cover_img];
|
|
222
|
+
return { msg, pics };
|
|
223
223
|
case 'DYNAMIC_TYPE_DRAW':
|
|
224
224
|
raw_pics_list = retweeted ? info?.retweeted_status?.pics || [] : info?.pics || [];
|
|
225
225
|
if (!info && !raw_pics_list)
|
|
@@ -227,7 +227,6 @@ class WeiboQuery {
|
|
|
227
227
|
if (raw_pics_list.length > dynamicPicCountLimit)
|
|
228
228
|
raw_pics_list.length = dynamicPicCountLimit;
|
|
229
229
|
pic_urls = raw_pics_list.map((img) => img?.large?.url);
|
|
230
|
-
pics = [];
|
|
231
230
|
for (let pic_url of pic_urls) {
|
|
232
231
|
const temp = Segment.image(pic_url, false, 15000, { referer: 'https://weibo.com' });
|
|
233
232
|
pics.push(temp);
|
|
@@ -238,10 +237,9 @@ class WeiboQuery {
|
|
|
238
237
|
`-----------------------------\n`,
|
|
239
238
|
`${this.dynamicContentLimit(this.filterText(info?.text), setData)}\n`,
|
|
240
239
|
`链接:${detail_url}\n`,
|
|
241
|
-
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}
|
|
242
|
-
...pics
|
|
240
|
+
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
|
|
243
241
|
];
|
|
244
|
-
return msg;
|
|
242
|
+
return { msg, pics };
|
|
245
243
|
case 'DYNAMIC_TYPE_ARTICLE':
|
|
246
244
|
if (!info)
|
|
247
245
|
return;
|
|
@@ -249,7 +247,6 @@ class WeiboQuery {
|
|
|
249
247
|
if (raw_pics_list.length > dynamicPicCountLimit)
|
|
250
248
|
raw_pics_list.length = dynamicPicCountLimit;
|
|
251
249
|
pic_urls = raw_pics_list.map(img => img?.large?.url);
|
|
252
|
-
pics = [];
|
|
253
250
|
for (const pic_url of pic_urls) {
|
|
254
251
|
const temp = Segment.image(pic_url, false, 15000, { referer: 'https://weibo.com' });
|
|
255
252
|
pics.push(temp);
|
|
@@ -260,10 +257,9 @@ class WeiboQuery {
|
|
|
260
257
|
`-----------------------------\n`,
|
|
261
258
|
`正文:${this.dynamicContentLimit(this.filterText(info?.text), setData)}\n`,
|
|
262
259
|
`链接:${detail_url}\n`,
|
|
263
|
-
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}
|
|
264
|
-
...pics
|
|
260
|
+
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}`
|
|
265
261
|
];
|
|
266
|
-
return msg;
|
|
262
|
+
return { msg, pics };
|
|
267
263
|
case 'DYNAMIC_TYPE_FORWARD':
|
|
268
264
|
if (!info)
|
|
269
265
|
return;
|
|
@@ -272,11 +268,13 @@ class WeiboQuery {
|
|
|
272
268
|
const origin_post_info = info?.retweeted_status;
|
|
273
269
|
isForward = true;
|
|
274
270
|
let orig = await this.formatTextDynamicData(upName, origin_post_info, isForward, setData);
|
|
275
|
-
|
|
276
|
-
|
|
271
|
+
let origContent = [];
|
|
272
|
+
if (orig && typeof orig === 'object') {
|
|
273
|
+
origContent = orig.msg.slice(2);
|
|
274
|
+
pics = orig.pics;
|
|
277
275
|
}
|
|
278
276
|
else {
|
|
279
|
-
return
|
|
277
|
+
return 'continue';
|
|
280
278
|
}
|
|
281
279
|
title = `微博【${upName}】转发动态推送:\n`;
|
|
282
280
|
msg = [
|
|
@@ -286,9 +284,9 @@ class WeiboQuery {
|
|
|
286
284
|
`链接:${detail_url}\n`,
|
|
287
285
|
`时间:${created_time ? moment(created_time).format('YYYY年MM月DD日 HH:mm:ss') : ''}\n`,
|
|
288
286
|
'\n---以下为转发内容---\n',
|
|
289
|
-
...
|
|
287
|
+
...origContent
|
|
290
288
|
];
|
|
291
|
-
return msg;
|
|
289
|
+
return { msg, pics };
|
|
292
290
|
default:
|
|
293
291
|
logger?.mark(`未处理的微博推送【${upName}】:${type}`);
|
|
294
292
|
return 'continue';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
|
-
import { Redis,
|
|
2
|
+
import { Redis, Segment, Bot } from 'yunzaijs';
|
|
3
3
|
import Config from '../../utils/config.js';
|
|
4
4
|
import { renderPage } from '../../utils/image.js';
|
|
5
5
|
import { WeiboGetWebData } from './weibo.get.web.data.js';
|
|
@@ -39,6 +39,8 @@ class WeiboTask {
|
|
|
39
39
|
uidMap.set(chatType, new Map());
|
|
40
40
|
}
|
|
41
41
|
const chatTypeMap = uidMap.get(chatType); // 建立当前 chatType (group 或 private) 的 uid 映射
|
|
42
|
+
if (chatTypeMap === undefined)
|
|
43
|
+
continue; // 如果 chatTypeMap 未定义,跳过此次循环
|
|
42
44
|
for (let chatId in weiboPushData[chatType]) {
|
|
43
45
|
const subUpsOfChat = Array.prototype.slice.call(weiboPushData[chatType][chatId] || []);
|
|
44
46
|
for (let subInfoOfup of subUpsOfChat) {
|
|
@@ -126,7 +128,7 @@ class WeiboTask {
|
|
|
126
128
|
*/
|
|
127
129
|
async sendDynamic(chatId, bot_id, upName, pushDynamicData, weiboConfigData, chatType) {
|
|
128
130
|
const id_str = WeiboQuery.getDynamicId(pushDynamicData); // 获取动态 ID
|
|
129
|
-
let sended, markKey;
|
|
131
|
+
let sended = null, markKey = '';
|
|
130
132
|
if (chatType === 'group') {
|
|
131
133
|
markKey = this.groupKey;
|
|
132
134
|
sended = await Redis.get(`${markKey}${chatId}:${id_str}`);
|
|
@@ -166,8 +168,8 @@ class WeiboTask {
|
|
|
166
168
|
let imgs = await this.renderDynamicCard(uid, renderData, ScreenshotOptionsData);
|
|
167
169
|
if (!imgs)
|
|
168
170
|
return;
|
|
169
|
-
Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 *
|
|
170
|
-
|
|
171
|
+
Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 * 72 }); // 设置已发送标记
|
|
172
|
+
global?.logger?.mark('优纪插件:微博动态执行推送');
|
|
171
173
|
for (let i = 0; i < imgs.length; i++) {
|
|
172
174
|
const image = imgs[i];
|
|
173
175
|
await this.sendMessage(chatId, bot_id, chatType, Segment.image(image));
|
|
@@ -177,18 +179,32 @@ class WeiboTask {
|
|
|
177
179
|
}
|
|
178
180
|
else {
|
|
179
181
|
const dynamicMsg = await WeiboQuery.formatTextDynamicData(upName, pushDynamicData, false, weiboConfigData); //构建文字动态消息
|
|
180
|
-
Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 *
|
|
181
|
-
if (dynamicMsg
|
|
182
|
+
Redis.set(`${markKey}${chatId}:${id_str}`, '1', { EX: 3600 * 72 }); // 设置已发送标记
|
|
183
|
+
if (dynamicMsg === undefined || dynamicMsg === 'continue') {
|
|
182
184
|
return 'return'; // 如果动态消息构建失败或内部资源获取失败,则直接返回
|
|
183
185
|
}
|
|
184
186
|
if (weiboConfigData.banWords.length > 0) {
|
|
185
187
|
const banWords = new RegExp(weiboConfigData.banWords.join('|'), 'g'); // 构建屏蔽关键字正则表达式
|
|
186
|
-
if (banWords.test(dynamicMsg.join(''))) {
|
|
188
|
+
if (banWords.test(dynamicMsg.msg.join(''))) {
|
|
187
189
|
return 'return'; // 如果动态消息包含屏蔽关键字,则直接返回
|
|
188
190
|
}
|
|
189
191
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
let mergeTextPic = !!weiboConfigData.mergeTextPic === false ? false : true; // 是否合并文字和图片,默认为 true
|
|
193
|
+
if (mergeTextPic) {
|
|
194
|
+
const mergeMsg = [...dynamicMsg.msg, ...dynamicMsg.pics];
|
|
195
|
+
await this.sendMessage(chatId, bot_id, chatType, mergeMsg);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
await this.sendMessage(chatId, bot_id, chatType, dynamicMsg.msg);
|
|
199
|
+
const pics = dynamicMsg.pics;
|
|
200
|
+
if (pics && pics.length > 0) {
|
|
201
|
+
for (let i = 0; i < pics.length; i++) {
|
|
202
|
+
await this.sendMessage(chatId, bot_id, chatType, pics[i]);
|
|
203
|
+
await this.randomDelay(1000, 2000); // 随机延时1-2秒
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
207
|
+
}
|
|
192
208
|
}
|
|
193
209
|
}
|
|
194
210
|
/**
|
|
@@ -280,7 +296,7 @@ class WeiboTask {
|
|
|
280
296
|
?.pickGroup(String(chatId))
|
|
281
297
|
.sendMsg(message) // 发送群聊
|
|
282
298
|
.catch(error => {
|
|
283
|
-
|
|
299
|
+
global?.logger?.error(`群组[${chatId}]推送失败:${JSON.stringify(error)}`);
|
|
284
300
|
});
|
|
285
301
|
}
|
|
286
302
|
else if (chatType === 'private') {
|
|
@@ -288,7 +304,7 @@ class WeiboTask {
|
|
|
288
304
|
?.pickFriend(String(chatId))
|
|
289
305
|
.sendMsg(message)
|
|
290
306
|
.catch(error => {
|
|
291
|
-
|
|
307
|
+
global?.logger?.error(`用户[${chatId}]推送失败:${JSON.stringify(error)}`);
|
|
292
308
|
}); // 发送好友私聊
|
|
293
309
|
}
|
|
294
310
|
}
|