koishi-plugin-bilibili-notify 1.3.6-beta.0 → 1.3.6
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/lib/biliAPI.d.ts +14 -0
- package/lib/biliAPI.js +94 -19
- package/lib/comRegister.js +5 -12
- package/lib/generateImg.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +22 -14
- package/package.json +1 -1
- package/readme.md +2 -0
- package/lib/wbi.d.ts +0 -27
- package/lib/wbi.js +0 -90
package/lib/biliAPI.d.ts
CHANGED
|
@@ -17,11 +17,24 @@ declare class BiliAPI extends Service {
|
|
|
17
17
|
loginInfoIsLoaded: boolean;
|
|
18
18
|
constructor(ctx: Context, config: BiliAPI.Config);
|
|
19
19
|
protected start(): void | Promise<void>;
|
|
20
|
+
getMixinKey: (orig: string) => string;
|
|
21
|
+
encWbi(params: {
|
|
22
|
+
[key: string]: string | number | object;
|
|
23
|
+
}, img_key: string, sub_key: string): string;
|
|
24
|
+
getWbi(params: {
|
|
25
|
+
[key: string]: string | number | object;
|
|
26
|
+
}): Promise<string>;
|
|
27
|
+
encrypt(text: string): string;
|
|
28
|
+
decrypt(text: string): string;
|
|
20
29
|
getServerUTCTime(): Promise<number>;
|
|
21
30
|
getTimeNow(): Promise<any>;
|
|
22
31
|
getUserSpaceDynamic(mid: string): Promise<any>;
|
|
23
32
|
getCookieInfo(refreshToken: string): Promise<any>;
|
|
24
33
|
getUserInfo(mid: string): Promise<any>;
|
|
34
|
+
getWbiKeys(): Promise<{
|
|
35
|
+
img_key: any;
|
|
36
|
+
sub_key: any;
|
|
37
|
+
}>;
|
|
25
38
|
getMyselfInfo(): Promise<any>;
|
|
26
39
|
getLoginQRCode(): Promise<any>;
|
|
27
40
|
getLoginStatus(qrcodeKey: string): Promise<any>;
|
|
@@ -44,6 +57,7 @@ declare class BiliAPI extends Service {
|
|
|
44
57
|
declare namespace BiliAPI {
|
|
45
58
|
interface Config {
|
|
46
59
|
userAgent: string;
|
|
60
|
+
key: string;
|
|
47
61
|
}
|
|
48
62
|
const Config: Schema<Config>;
|
|
49
63
|
}
|
package/lib/biliAPI.js
CHANGED
|
@@ -3,19 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
/* eslint-disable @typescript-eslint/
|
|
7
|
-
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
6
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
8
7
|
/* eslint-disable @typescript-eslint/no-namespace */
|
|
9
8
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
9
|
const koishi_1 = require("koishi");
|
|
10
|
+
const md5_1 = __importDefault(require("md5"));
|
|
11
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
11
12
|
const axios_1 = __importDefault(require("axios"));
|
|
12
13
|
const tough_cookie_1 = require("tough-cookie");
|
|
13
14
|
const axios_cookiejar_support_1 = require("axios-cookiejar-support");
|
|
14
15
|
const jsdom_1 = require("jsdom");
|
|
15
16
|
const luxon_1 = require("luxon");
|
|
17
|
+
const mixinKeyEncTab = [
|
|
18
|
+
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
|
|
19
|
+
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
|
|
20
|
+
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
|
|
21
|
+
36, 20, 34, 44, 52
|
|
22
|
+
];
|
|
16
23
|
// 在getUserInfo中检测到番剧出差的UID时,要传回的数据:
|
|
17
24
|
const bangumiTripData = { "code": 0, "data": { "live_room": { "roomid": 931774 } } };
|
|
18
|
-
// const GET_DYNAMIC_LIST = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all'
|
|
19
25
|
const GET_USER_SPACE_DYNAMIC_LIST = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space';
|
|
20
26
|
const GET_COOKIES_INFO = 'https://passport.bilibili.com/x/passport-login/web/cookie/info';
|
|
21
27
|
const GET_USER_INFO = 'https://api.bilibili.com/x/space/wbi/acc/info';
|
|
@@ -27,7 +33,7 @@ const GET_MASTER_INFO = 'https://api.live.bilibili.com/live_user/v1/Master/info'
|
|
|
27
33
|
const GET_TIME_NOW = 'https://api.bilibili.com/x/report/click/now';
|
|
28
34
|
const GET_SERVER_UTC_TIME = 'https://interface.bilibili.com/serverdate.js';
|
|
29
35
|
class BiliAPI extends koishi_1.Service {
|
|
30
|
-
static inject = ['database', '
|
|
36
|
+
static inject = ['database', 'notifier'];
|
|
31
37
|
jar;
|
|
32
38
|
client;
|
|
33
39
|
apiConfig;
|
|
@@ -44,12 +50,50 @@ class BiliAPI extends koishi_1.Service {
|
|
|
44
50
|
this.createNewClient();
|
|
45
51
|
// 从数据库加载cookies
|
|
46
52
|
this.loadCookiesFromDatabase();
|
|
47
|
-
// 输出日志
|
|
48
|
-
// this.logger.info('工作中')
|
|
49
53
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
// WBI签名
|
|
55
|
+
// 对 imgKey 和 subKey 进行字符顺序打乱编码
|
|
56
|
+
getMixinKey = (orig) => mixinKeyEncTab
|
|
57
|
+
.map((n) => orig[n])
|
|
58
|
+
.join("")
|
|
59
|
+
.slice(0, 32);
|
|
60
|
+
// 为请求参数进行 wbi 签名
|
|
61
|
+
encWbi(params, img_key, sub_key) {
|
|
62
|
+
const mixin_key = this.getMixinKey(img_key + sub_key), curr_time = Math.round(Date.now() / 1000), chr_filter = /[!'()*]/g;
|
|
63
|
+
Object.assign(params, { wts: curr_time }); // 添加 wts 字段
|
|
64
|
+
// 按照 key 重排参数
|
|
65
|
+
const query = Object.keys(params)
|
|
66
|
+
.sort()
|
|
67
|
+
.map((key) => {
|
|
68
|
+
// 过滤 value 中的 "!'()*" 字符
|
|
69
|
+
const value = params[key].toString().replace(chr_filter, "");
|
|
70
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
71
|
+
})
|
|
72
|
+
.join("&");
|
|
73
|
+
const wbi_sign = (0, md5_1.default)(query + mixin_key); // 计算 w_rid
|
|
74
|
+
return query + "&w_rid=" + wbi_sign;
|
|
75
|
+
}
|
|
76
|
+
async getWbi(params) {
|
|
77
|
+
const web_keys = await this.getWbiKeys();
|
|
78
|
+
const img_key = web_keys.img_key, sub_key = web_keys.sub_key;
|
|
79
|
+
const query = this.encWbi(params, img_key, sub_key);
|
|
80
|
+
return query;
|
|
81
|
+
}
|
|
82
|
+
encrypt(text) {
|
|
83
|
+
const iv = crypto_1.default.randomBytes(16);
|
|
84
|
+
const cipher = crypto_1.default.createCipheriv('aes-256-cbc', Buffer.from(this.apiConfig.key), iv);
|
|
85
|
+
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
|
86
|
+
return iv.toString('hex') + ':' + encrypted.toString('hex');
|
|
87
|
+
}
|
|
88
|
+
decrypt(text) {
|
|
89
|
+
const textParts = text.split(':');
|
|
90
|
+
const iv = Buffer.from(textParts.shift(), 'hex');
|
|
91
|
+
const encryptedText = Buffer.from(textParts.join(':'), 'hex');
|
|
92
|
+
const decipher = crypto_1.default.createDecipheriv('aes-256-cbc', Buffer.from(this.apiConfig.key), iv);
|
|
93
|
+
const decrypted = Buffer.concat([decipher.update(encryptedText), decipher.final()]);
|
|
94
|
+
return decrypted.toString();
|
|
95
|
+
}
|
|
96
|
+
// BA API
|
|
53
97
|
async getServerUTCTime() {
|
|
54
98
|
try {
|
|
55
99
|
const { data } = await this.client.get(GET_SERVER_UTC_TIME);
|
|
@@ -102,7 +146,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
102
146
|
return bangumiTripData;
|
|
103
147
|
}
|
|
104
148
|
try {
|
|
105
|
-
const wbi = await this.
|
|
149
|
+
const wbi = await this.getWbi({ mid });
|
|
106
150
|
const { data } = await this.client.get(`${GET_USER_INFO}?${wbi}`);
|
|
107
151
|
return data;
|
|
108
152
|
}
|
|
@@ -110,6 +154,15 @@ class BiliAPI extends koishi_1.Service {
|
|
|
110
154
|
throw new Error('网络异常,本次请求失败!');
|
|
111
155
|
}
|
|
112
156
|
}
|
|
157
|
+
// 获取最新的 img_key 和 sub_key
|
|
158
|
+
async getWbiKeys() {
|
|
159
|
+
const { data } = await this.client.get('https://api.bilibili.com/x/web-interface/nav');
|
|
160
|
+
const { data: { wbi_img: { img_url, sub_url }, }, } = data;
|
|
161
|
+
return {
|
|
162
|
+
img_key: img_url.slice(img_url.lastIndexOf('/') + 1, img_url.lastIndexOf('.')),
|
|
163
|
+
sub_key: sub_url.slice(sub_url.lastIndexOf('/') + 1, sub_url.lastIndexOf('.'))
|
|
164
|
+
};
|
|
165
|
+
}
|
|
113
166
|
async getMyselfInfo() {
|
|
114
167
|
try {
|
|
115
168
|
const { data } = await this.client.get(GET_MYSELF_INFO);
|
|
@@ -210,9 +263,9 @@ class BiliAPI extends koishi_1.Service {
|
|
|
210
263
|
// 尝试解密
|
|
211
264
|
try {
|
|
212
265
|
// 解密数据
|
|
213
|
-
const decryptedCookies = this.
|
|
266
|
+
const decryptedCookies = this.decrypt(data.bili_cookies);
|
|
214
267
|
// 解密refresh_token
|
|
215
|
-
const decryptedRefreshToken = this.
|
|
268
|
+
const decryptedRefreshToken = this.decrypt(data.bili_refresh_token);
|
|
216
269
|
// 解析从数据库读到的cookies
|
|
217
270
|
const cookies = JSON.parse(decryptedCookies);
|
|
218
271
|
// 返回值
|
|
@@ -246,11 +299,18 @@ class BiliAPI extends koishi_1.Service {
|
|
|
246
299
|
return;
|
|
247
300
|
}
|
|
248
301
|
// 定义CSRF Token
|
|
249
|
-
let csrf;
|
|
302
|
+
let csrf, expires, domain, path, secure, httpOnly, sameSite;
|
|
250
303
|
cookies.forEach(cookieData => {
|
|
251
304
|
// 获取key为bili_jct的值
|
|
252
|
-
if (cookieData.key === 'bili_jct')
|
|
305
|
+
if (cookieData.key === 'bili_jct') {
|
|
253
306
|
csrf = cookieData.value;
|
|
307
|
+
expires = new Date(cookieData.expires);
|
|
308
|
+
domain = cookieData.domain;
|
|
309
|
+
path = cookieData.path;
|
|
310
|
+
secure = cookieData.secure;
|
|
311
|
+
httpOnly = cookieData.httpOnly;
|
|
312
|
+
sameSite = cookieData.sameSite;
|
|
313
|
+
}
|
|
254
314
|
// 创建一个完整的 Cookie 实例
|
|
255
315
|
const cookie = new tough_cookie_1.Cookie({
|
|
256
316
|
key: cookieData.key,
|
|
@@ -264,6 +324,18 @@ class BiliAPI extends koishi_1.Service {
|
|
|
264
324
|
});
|
|
265
325
|
this.jar.setCookieSync(cookie, `http${cookie.secure ? 's' : ''}://${cookie.domain}${cookie.path}`, {});
|
|
266
326
|
});
|
|
327
|
+
// 对于某些 IP 地址,需要在 Cookie 中提供任意非空的 buvid3 字段
|
|
328
|
+
const buvid3Cookie = new tough_cookie_1.Cookie({
|
|
329
|
+
key: 'buvid3',
|
|
330
|
+
value: 'some_non_empty_value', // 设置任意非空值
|
|
331
|
+
expires, // 设置过期时间
|
|
332
|
+
domain, // 设置域名
|
|
333
|
+
path, // 设置路径
|
|
334
|
+
secure, // 设置是否为安全 cookie
|
|
335
|
+
httpOnly, // 设置是否为 HttpOnly cookie
|
|
336
|
+
sameSite // 设置 SameSite 属性
|
|
337
|
+
});
|
|
338
|
+
this.jar.setCookieSync(buvid3Cookie, `http${buvid3Cookie.secure ? 's' : ''}://${buvid3Cookie.domain}${buvid3Cookie.path}`, {});
|
|
267
339
|
// Login info is loaded
|
|
268
340
|
this.loginInfoIsLoaded = true;
|
|
269
341
|
// restart plugin check
|
|
@@ -325,7 +397,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
325
397
|
// 如果请求失败,有可能是404,直接刷新cookie
|
|
326
398
|
}
|
|
327
399
|
// 定义Key
|
|
328
|
-
const publicKey = await
|
|
400
|
+
const publicKey = await crypto_1.default.subtle.importKey("jwk", {
|
|
329
401
|
kty: "RSA",
|
|
330
402
|
n: "y4HdjgJHBlbaBN04VERG4qNBIFHP6a3GozCl75AihQloSWCXC5HDNgyinEnhaQ_4-gaMud_GF50elYXLlCToR9se9Z8z433U3KjM-3Yx7ptKkmQNAMggQwAVKgq3zYAoidNEWuxpkY_mAitTSRLnsJW-NCTa0bqBFF6Wm1MxgfE",
|
|
331
403
|
e: "AQAB",
|
|
@@ -333,7 +405,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
333
405
|
// 定义获取CorrespondPath方法
|
|
334
406
|
async function getCorrespondPath(timestamp) {
|
|
335
407
|
const data = new TextEncoder().encode(`refresh_${timestamp}`);
|
|
336
|
-
const encrypted = new Uint8Array(await
|
|
408
|
+
const encrypted = new Uint8Array(await crypto_1.default.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
|
|
337
409
|
return encrypted.reduce((str, c) => str + c.toString(16).padStart(2, "0"), "");
|
|
338
410
|
}
|
|
339
411
|
// 获取CorrespondPath
|
|
@@ -372,8 +444,8 @@ class BiliAPI extends koishi_1.Service {
|
|
|
372
444
|
}
|
|
373
445
|
}
|
|
374
446
|
// 更新 新的cookies和refresh_token
|
|
375
|
-
const encryptedCookies = this.
|
|
376
|
-
const encryptedRefreshToken = this.
|
|
447
|
+
const encryptedCookies = this.encrypt(this.getCookies());
|
|
448
|
+
const encryptedRefreshToken = this.encrypt(refreshData.data.refresh_token);
|
|
377
449
|
await this.ctx.database.upsert('loginBili', [{
|
|
378
450
|
id: 1,
|
|
379
451
|
bili_cookies: encryptedCookies,
|
|
@@ -407,7 +479,10 @@ class BiliAPI extends koishi_1.Service {
|
|
|
407
479
|
}
|
|
408
480
|
(function (BiliAPI) {
|
|
409
481
|
BiliAPI.Config = koishi_1.Schema.object({
|
|
410
|
-
userAgent: koishi_1.Schema.string()
|
|
482
|
+
userAgent: koishi_1.Schema.string(),
|
|
483
|
+
key: koishi_1.Schema.string()
|
|
484
|
+
.pattern(/^[0-9a-f]{32}$/)
|
|
485
|
+
.required()
|
|
411
486
|
});
|
|
412
487
|
})(BiliAPI || (BiliAPI = {}));
|
|
413
488
|
exports.default = BiliAPI;
|
package/lib/comRegister.js
CHANGED
|
@@ -4,8 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const jsx_runtime_1 = require("@satorijs/element/jsx-runtime");
|
|
7
|
-
/* eslint-disable @typescript-eslint/
|
|
8
|
-
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
7
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
9
8
|
/* eslint-disable @typescript-eslint/no-namespace */
|
|
10
9
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
11
10
|
const koishi_1 = require("koishi");
|
|
@@ -19,7 +18,7 @@ var LiveType;
|
|
|
19
18
|
LiveType[LiveType["LiveBroadcast"] = 2] = "LiveBroadcast";
|
|
20
19
|
})(LiveType || (LiveType = {}));
|
|
21
20
|
class ComRegister {
|
|
22
|
-
static inject = ['ba', 'gi', '
|
|
21
|
+
static inject = ['ba', 'gi', 'database', 'sm'];
|
|
23
22
|
logger;
|
|
24
23
|
config;
|
|
25
24
|
loginTimer;
|
|
@@ -47,12 +46,6 @@ class ComRegister {
|
|
|
47
46
|
sendMsgFunc;
|
|
48
47
|
constructor(ctx, config) {
|
|
49
48
|
this.logger = ctx.logger('cr');
|
|
50
|
-
/* ctx.on('ready', () => {
|
|
51
|
-
this.logger.info('工作中');
|
|
52
|
-
})
|
|
53
|
-
ctx.on('dispose', () => {
|
|
54
|
-
this.logger.info('已停止工作');
|
|
55
|
-
}) */
|
|
56
49
|
this.config = config;
|
|
57
50
|
// 拿到各类机器人
|
|
58
51
|
ctx.bots.forEach(bot => {
|
|
@@ -291,8 +284,8 @@ class ComRegister {
|
|
|
291
284
|
return await session.send('二维码已失效,请重新登录');
|
|
292
285
|
}
|
|
293
286
|
if (loginContent.data.code === 0) { // 登录成功
|
|
294
|
-
const encryptedCookies = ctx.
|
|
295
|
-
const encryptedRefreshToken = ctx.
|
|
287
|
+
const encryptedCookies = ctx.ba.encrypt(ctx.ba.getCookies());
|
|
288
|
+
const encryptedRefreshToken = ctx.ba.encrypt(loginContent.data.refresh_token);
|
|
296
289
|
await ctx.database.upsert('loginBili', [{
|
|
297
290
|
id: 1,
|
|
298
291
|
bili_cookies: encryptedCookies,
|
|
@@ -434,7 +427,7 @@ class ComRegister {
|
|
|
434
427
|
msg = '用户不存在';
|
|
435
428
|
break;
|
|
436
429
|
case -352:
|
|
437
|
-
msg = '
|
|
430
|
+
msg = '风控校验失败,请尝试更换UA';
|
|
438
431
|
break;
|
|
439
432
|
default:
|
|
440
433
|
msg = '未知错误,错误信息:' + content.message;
|
package/lib/generateImg.js
CHANGED
|
@@ -183,7 +183,7 @@ class GenerateImg extends koishi_1.Service {
|
|
|
183
183
|
</div>
|
|
184
184
|
${this.giConfig.hideDesc ? '' : `<p class="card-text">${data.description ? data.description : '这个主播很懒,什么都简介都没写'}</p>`}
|
|
185
185
|
<p class="card-link">
|
|
186
|
-
<span>人气:${data.online}</span>
|
|
186
|
+
<span>人气:${data.online > 10000 ? `${data.online / 10000}万` : data.online}</span>
|
|
187
187
|
<span>分区名称:${data.area_name}</span>
|
|
188
188
|
</p>
|
|
189
189
|
<p class="card-link">
|
package/lib/index.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ declare class ServerManager extends Service {
|
|
|
44
44
|
dynamicLoopTime: number;
|
|
45
45
|
constructor(ctx: Context);
|
|
46
46
|
protected start(): void | Promise<void>;
|
|
47
|
-
registerPlugin: () =>
|
|
47
|
+
registerPlugin: () => boolean;
|
|
48
48
|
disposePlugin: () => Promise<boolean>;
|
|
49
49
|
restartPlugin: () => Promise<boolean>;
|
|
50
50
|
}
|
package/lib/index.js
CHANGED
|
@@ -28,14 +28,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.Config = exports.name = exports.inject = void 0;
|
|
30
30
|
exports.apply = apply;
|
|
31
|
-
/* eslint-disable @typescript-eslint/
|
|
31
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
32
32
|
const koishi_1 = require("koishi");
|
|
33
33
|
// import plugins
|
|
34
34
|
// import Authority from './authority'
|
|
35
35
|
const comRegister_1 = __importDefault(require("./comRegister"));
|
|
36
36
|
const Database = __importStar(require("./database"));
|
|
37
37
|
// import Service
|
|
38
|
-
const wbi_1 = __importDefault(require("./wbi"));
|
|
39
38
|
const generateImg_1 = __importDefault(require("./generateImg"));
|
|
40
39
|
const biliAPI_1 = __importDefault(require("./biliAPI"));
|
|
41
40
|
exports.inject = ['puppeteer', 'database', 'notifier'];
|
|
@@ -80,8 +79,8 @@ exports.Config = koishi_1.Schema.object({
|
|
|
80
79
|
.default('render')
|
|
81
80
|
.description('渲染类型,默认为render模式,渲染速度更快,但会出现乱码问题,若出现乱码问题,请切换到page模式。若使用自定义字体,建议选择render模式'),
|
|
82
81
|
userAgent: koishi_1.Schema.string()
|
|
83
|
-
.
|
|
84
|
-
.description('设置请求头User-Agen,请求出现-352
|
|
82
|
+
.required()
|
|
83
|
+
.description('设置请求头User-Agen,请求出现-352时可以尝试修改,UA获取方法可参考:https://blog.csdn.net/qq_44503987/article/details/104929111'),
|
|
85
84
|
dynamic: koishi_1.Schema.object({}).description('动态推送设置'),
|
|
86
85
|
dynamicUrl: koishi_1.Schema.boolean()
|
|
87
86
|
.default(false)
|
|
@@ -246,17 +245,25 @@ class ServerManager extends koishi_1.Service {
|
|
|
246
245
|
break;
|
|
247
246
|
}
|
|
248
247
|
// 注册插件
|
|
249
|
-
this.registerPlugin()
|
|
248
|
+
if (this.registerPlugin()) {
|
|
249
|
+
this.logger.info('插件启动成功');
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
this.logger.error('插件启动失败');
|
|
253
|
+
}
|
|
250
254
|
}
|
|
251
|
-
registerPlugin =
|
|
255
|
+
registerPlugin = () => {
|
|
252
256
|
// 如果已经有服务则返回false
|
|
253
257
|
if (this.servers.length !== 0)
|
|
254
258
|
return false;
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
// 注册插件
|
|
260
|
+
try {
|
|
261
|
+
// BA = BiliAPI
|
|
257
262
|
const ba = this.ctx.plugin(biliAPI_1.default, {
|
|
258
|
-
userAgent: globalConfig.userAgent
|
|
263
|
+
userAgent: globalConfig.userAgent,
|
|
264
|
+
key: globalConfig.key
|
|
259
265
|
});
|
|
266
|
+
// GI = GenerateImg
|
|
260
267
|
const gi = this.ctx.plugin(generateImg_1.default, {
|
|
261
268
|
renderType: this.renderType,
|
|
262
269
|
filter: globalConfig.filter,
|
|
@@ -267,7 +274,7 @@ class ServerManager extends koishi_1.Service {
|
|
|
267
274
|
enableLargeFont: globalConfig.enableLargeFont,
|
|
268
275
|
font: globalConfig.font
|
|
269
276
|
});
|
|
270
|
-
|
|
277
|
+
// CR = ComRegister
|
|
271
278
|
const cr = this.ctx.plugin(comRegister_1.default, {
|
|
272
279
|
master: globalConfig.master,
|
|
273
280
|
unlockSubLimits: globalConfig.unlockSubLimits,
|
|
@@ -287,11 +294,12 @@ class ServerManager extends koishi_1.Service {
|
|
|
287
294
|
// 添加服务
|
|
288
295
|
this.servers.push(ba);
|
|
289
296
|
this.servers.push(gi);
|
|
290
|
-
this.servers.push(wbi);
|
|
291
297
|
this.servers.push(cr);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
this.logger.error('插件注册失败', e);
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
295
303
|
// 成功返回true
|
|
296
304
|
return true;
|
|
297
305
|
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
package/lib/wbi.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Context, Schema, Service } from "koishi";
|
|
2
|
-
declare module 'koishi' {
|
|
3
|
-
interface Context {
|
|
4
|
-
wbi: Wbi;
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
declare class Wbi extends Service {
|
|
8
|
-
wbiConfig: Wbi.Config;
|
|
9
|
-
mixinKeyEncTab: number[];
|
|
10
|
-
constructor(ctx: Context, config: Wbi.Config);
|
|
11
|
-
getMixinKey: (orig: any) => string;
|
|
12
|
-
encWbi(params: any, img_key: any, sub_key: any): string;
|
|
13
|
-
getWbiKeys(): Promise<{
|
|
14
|
-
img_key: any;
|
|
15
|
-
sub_key: any;
|
|
16
|
-
}>;
|
|
17
|
-
getWbi(params: any): Promise<string>;
|
|
18
|
-
encrypt(text: string): string;
|
|
19
|
-
decrypt(text: string): string;
|
|
20
|
-
}
|
|
21
|
-
declare namespace Wbi {
|
|
22
|
-
interface Config {
|
|
23
|
-
key: string;
|
|
24
|
-
}
|
|
25
|
-
const Config: Schema<Config>;
|
|
26
|
-
}
|
|
27
|
-
export default Wbi;
|
package/lib/wbi.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
/* eslint-disable @typescript-eslint/no-namespace */
|
|
7
|
-
const koishi_1 = require("koishi");
|
|
8
|
-
const md5_1 = __importDefault(require("md5"));
|
|
9
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
-
class Wbi extends koishi_1.Service {
|
|
11
|
-
wbiConfig;
|
|
12
|
-
mixinKeyEncTab = [
|
|
13
|
-
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
|
|
14
|
-
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
|
|
15
|
-
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
|
|
16
|
-
36, 20, 34, 44, 52
|
|
17
|
-
];
|
|
18
|
-
constructor(ctx, config) {
|
|
19
|
-
super(ctx, 'wbi');
|
|
20
|
-
this.wbiConfig = config;
|
|
21
|
-
}
|
|
22
|
-
/* protected start(): void | Promise<void> {
|
|
23
|
-
this.logger.info('工作中')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
protected stop(): void | Promise<void> {
|
|
27
|
-
this.logger.info('已停止工作')
|
|
28
|
-
} */
|
|
29
|
-
// 对 imgKey 和 subKey 进行字符顺序打乱编码
|
|
30
|
-
getMixinKey = (orig) => this.mixinKeyEncTab.map(n => orig[n]).join('').slice(0, 32);
|
|
31
|
-
// 为请求参数进行 wbi 签名
|
|
32
|
-
encWbi(params, img_key, sub_key) {
|
|
33
|
-
const mixin_key = this.getMixinKey(img_key + sub_key), curr_time = Math.round(Date.now() / 1000), chr_filter = /[!'()*]/g;
|
|
34
|
-
Object.assign(params, { wts: curr_time }); // 添加 wts 字段
|
|
35
|
-
// 按照 key 重排参数
|
|
36
|
-
const query = Object
|
|
37
|
-
.keys(params)
|
|
38
|
-
.sort()
|
|
39
|
-
.map(key => {
|
|
40
|
-
// 过滤 value 中的 "!'()*" 字符
|
|
41
|
-
const value = params[key].toString().replace(chr_filter, '');
|
|
42
|
-
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
43
|
-
})
|
|
44
|
-
.join('&');
|
|
45
|
-
const wbi_sign = (0, md5_1.default)(query + mixin_key); // 计算 w_rid
|
|
46
|
-
return query + '&w_rid=' + wbi_sign;
|
|
47
|
-
}
|
|
48
|
-
// 获取最新的 img_key 和 sub_key
|
|
49
|
-
async getWbiKeys() {
|
|
50
|
-
const res = await fetch('https://api.bilibili.com/x/web-interface/nav', {
|
|
51
|
-
headers: {
|
|
52
|
-
// SESSDATA 字段
|
|
53
|
-
Cookie: "SESSDATA=xxxxxx"
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
const { data: { wbi_img: { img_url, sub_url } } } = await res.json();
|
|
57
|
-
return {
|
|
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
|
-
}
|
|
62
|
-
async getWbi(params) {
|
|
63
|
-
const web_keys = await this.getWbiKeys();
|
|
64
|
-
const img_key = web_keys.img_key, sub_key = web_keys.sub_key;
|
|
65
|
-
const query = this.encWbi(params, img_key, sub_key);
|
|
66
|
-
return query;
|
|
67
|
-
}
|
|
68
|
-
encrypt(text) {
|
|
69
|
-
const iv = crypto_1.default.randomBytes(16);
|
|
70
|
-
const cipher = crypto_1.default.createCipheriv('aes-256-cbc', Buffer.from(this.wbiConfig.key), iv);
|
|
71
|
-
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
|
72
|
-
return iv.toString('hex') + ':' + encrypted.toString('hex');
|
|
73
|
-
}
|
|
74
|
-
decrypt(text) {
|
|
75
|
-
const textParts = text.split(':');
|
|
76
|
-
const iv = Buffer.from(textParts.shift(), 'hex');
|
|
77
|
-
const encryptedText = Buffer.from(textParts.join(':'), 'hex');
|
|
78
|
-
const decipher = crypto_1.default.createDecipheriv('aes-256-cbc', Buffer.from(this.wbiConfig.key), iv);
|
|
79
|
-
const decrypted = Buffer.concat([decipher.update(encryptedText), decipher.final()]);
|
|
80
|
-
return decrypted.toString();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
(function (Wbi) {
|
|
84
|
-
Wbi.Config = koishi_1.Schema.object({
|
|
85
|
-
key: koishi_1.Schema.string()
|
|
86
|
-
.pattern(/^[0-9a-f]{32}$/)
|
|
87
|
-
.required()
|
|
88
|
-
});
|
|
89
|
-
})(Wbi || (Wbi = {}));
|
|
90
|
-
exports.default = Wbi;
|