koishi-plugin-bilibili-notify 1.3.6-alpha.0 → 1.3.6-rc.0
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 +98 -20
- package/lib/comRegister.d.ts +1 -0
- package/lib/comRegister.js +49 -32
- package/lib/index.d.ts +1 -1
- package/lib/index.js +19 -11
- package/package.json +16 -16
- 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,18 +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/no-namespace */
|
|
7
6
|
/* eslint-disable @typescript-eslint/ban-types */
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-namespace */
|
|
8
8
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
9
|
const koishi_1 = require("koishi");
|
|
10
|
+
const md5_1 = __importDefault(require("md5"));
|
|
11
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
12
|
const axios_1 = __importDefault(require("axios"));
|
|
11
13
|
const tough_cookie_1 = require("tough-cookie");
|
|
12
14
|
const axios_cookiejar_support_1 = require("axios-cookiejar-support");
|
|
13
15
|
const jsdom_1 = require("jsdom");
|
|
14
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
|
+
];
|
|
15
23
|
// 在getUserInfo中检测到番剧出差的UID时,要传回的数据:
|
|
16
24
|
const bangumiTripData = { "code": 0, "data": { "live_room": { "roomid": 931774 } } };
|
|
17
|
-
// const GET_DYNAMIC_LIST = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all'
|
|
18
25
|
const GET_USER_SPACE_DYNAMIC_LIST = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space';
|
|
19
26
|
const GET_COOKIES_INFO = 'https://passport.bilibili.com/x/passport-login/web/cookie/info';
|
|
20
27
|
const GET_USER_INFO = 'https://api.bilibili.com/x/space/wbi/acc/info';
|
|
@@ -26,7 +33,7 @@ const GET_MASTER_INFO = 'https://api.live.bilibili.com/live_user/v1/Master/info'
|
|
|
26
33
|
const GET_TIME_NOW = 'https://api.bilibili.com/x/report/click/now';
|
|
27
34
|
const GET_SERVER_UTC_TIME = 'https://interface.bilibili.com/serverdate.js';
|
|
28
35
|
class BiliAPI extends koishi_1.Service {
|
|
29
|
-
static inject = ['database', '
|
|
36
|
+
static inject = ['database', 'notifier'];
|
|
30
37
|
jar;
|
|
31
38
|
client;
|
|
32
39
|
apiConfig;
|
|
@@ -43,12 +50,50 @@ class BiliAPI extends koishi_1.Service {
|
|
|
43
50
|
this.createNewClient();
|
|
44
51
|
// 从数据库加载cookies
|
|
45
52
|
this.loadCookiesFromDatabase();
|
|
46
|
-
// 输出日志
|
|
47
|
-
// this.logger.info('工作中')
|
|
48
53
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
52
97
|
async getServerUTCTime() {
|
|
53
98
|
try {
|
|
54
99
|
const { data } = await this.client.get(GET_SERVER_UTC_TIME);
|
|
@@ -101,7 +146,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
101
146
|
return bangumiTripData;
|
|
102
147
|
}
|
|
103
148
|
try {
|
|
104
|
-
const wbi = await this.
|
|
149
|
+
const wbi = await this.getWbi({ mid });
|
|
105
150
|
const { data } = await this.client.get(`${GET_USER_INFO}?${wbi}`);
|
|
106
151
|
return data;
|
|
107
152
|
}
|
|
@@ -109,6 +154,15 @@ class BiliAPI extends koishi_1.Service {
|
|
|
109
154
|
throw new Error('网络异常,本次请求失败!');
|
|
110
155
|
}
|
|
111
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
|
+
}
|
|
112
166
|
async getMyselfInfo() {
|
|
113
167
|
try {
|
|
114
168
|
const { data } = await this.client.get(GET_MYSELF_INFO);
|
|
@@ -154,7 +208,8 @@ class BiliAPI extends koishi_1.Service {
|
|
|
154
208
|
throw new Error('网络异常,本次请求失败!');
|
|
155
209
|
}
|
|
156
210
|
}
|
|
157
|
-
disposeNotifier() {
|
|
211
|
+
disposeNotifier() { if (this.loginNotifier)
|
|
212
|
+
this.loginNotifier.dispose(); }
|
|
158
213
|
getRandomUserAgent() {
|
|
159
214
|
const userAgents = [
|
|
160
215
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
|
|
@@ -208,9 +263,9 @@ class BiliAPI extends koishi_1.Service {
|
|
|
208
263
|
// 尝试解密
|
|
209
264
|
try {
|
|
210
265
|
// 解密数据
|
|
211
|
-
const decryptedCookies = this.
|
|
266
|
+
const decryptedCookies = this.decrypt(data.bili_cookies);
|
|
212
267
|
// 解密refresh_token
|
|
213
|
-
const decryptedRefreshToken = this.
|
|
268
|
+
const decryptedRefreshToken = this.decrypt(data.bili_refresh_token);
|
|
214
269
|
// 解析从数据库读到的cookies
|
|
215
270
|
const cookies = JSON.parse(decryptedCookies);
|
|
216
271
|
// 返回值
|
|
@@ -244,11 +299,18 @@ class BiliAPI extends koishi_1.Service {
|
|
|
244
299
|
return;
|
|
245
300
|
}
|
|
246
301
|
// 定义CSRF Token
|
|
247
|
-
let csrf;
|
|
302
|
+
let csrf, expires, domain, path, secure, httpOnly, sameSite;
|
|
248
303
|
cookies.forEach(cookieData => {
|
|
249
304
|
// 获取key为bili_jct的值
|
|
250
|
-
if (cookieData.key === 'bili_jct')
|
|
305
|
+
if (cookieData.key === 'bili_jct') {
|
|
251
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
|
+
}
|
|
252
314
|
// 创建一个完整的 Cookie 实例
|
|
253
315
|
const cookie = new tough_cookie_1.Cookie({
|
|
254
316
|
key: cookieData.key,
|
|
@@ -262,6 +324,18 @@ class BiliAPI extends koishi_1.Service {
|
|
|
262
324
|
});
|
|
263
325
|
this.jar.setCookieSync(cookie, `http${cookie.secure ? 's' : ''}://${cookie.domain}${cookie.path}`, {});
|
|
264
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}`, {});
|
|
265
339
|
// Login info is loaded
|
|
266
340
|
this.loginInfoIsLoaded = true;
|
|
267
341
|
// restart plugin check
|
|
@@ -271,7 +345,8 @@ class BiliAPI extends koishi_1.Service {
|
|
|
271
345
|
}
|
|
272
346
|
enableRefreshCookiesDetect() {
|
|
273
347
|
// 判断之前是否启动检测
|
|
274
|
-
|
|
348
|
+
if (this.refreshCookieTimer)
|
|
349
|
+
this.refreshCookieTimer();
|
|
275
350
|
// Open scheduled tasks and check if token need refresh
|
|
276
351
|
this.refreshCookieTimer = this.ctx.setInterval(async () => {
|
|
277
352
|
// 从数据库获取登录信息
|
|
@@ -322,7 +397,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
322
397
|
// 如果请求失败,有可能是404,直接刷新cookie
|
|
323
398
|
}
|
|
324
399
|
// 定义Key
|
|
325
|
-
const publicKey = await
|
|
400
|
+
const publicKey = await crypto_1.default.subtle.importKey("jwk", {
|
|
326
401
|
kty: "RSA",
|
|
327
402
|
n: "y4HdjgJHBlbaBN04VERG4qNBIFHP6a3GozCl75AihQloSWCXC5HDNgyinEnhaQ_4-gaMud_GF50elYXLlCToR9se9Z8z433U3KjM-3Yx7ptKkmQNAMggQwAVKgq3zYAoidNEWuxpkY_mAitTSRLnsJW-NCTa0bqBFF6Wm1MxgfE",
|
|
328
403
|
e: "AQAB",
|
|
@@ -330,7 +405,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
330
405
|
// 定义获取CorrespondPath方法
|
|
331
406
|
async function getCorrespondPath(timestamp) {
|
|
332
407
|
const data = new TextEncoder().encode(`refresh_${timestamp}`);
|
|
333
|
-
const encrypted = new Uint8Array(await
|
|
408
|
+
const encrypted = new Uint8Array(await crypto_1.default.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
|
|
334
409
|
return encrypted.reduce((str, c) => str + c.toString(16).padStart(2, "0"), "");
|
|
335
410
|
}
|
|
336
411
|
// 获取CorrespondPath
|
|
@@ -369,8 +444,8 @@ class BiliAPI extends koishi_1.Service {
|
|
|
369
444
|
}
|
|
370
445
|
}
|
|
371
446
|
// 更新 新的cookies和refresh_token
|
|
372
|
-
const encryptedCookies = this.
|
|
373
|
-
const encryptedRefreshToken = this.
|
|
447
|
+
const encryptedCookies = this.encrypt(this.getCookies());
|
|
448
|
+
const encryptedRefreshToken = this.encrypt(refreshData.data.refresh_token);
|
|
374
449
|
await this.ctx.database.upsert('loginBili', [{
|
|
375
450
|
id: 1,
|
|
376
451
|
bili_cookies: encryptedCookies,
|
|
@@ -404,7 +479,10 @@ class BiliAPI extends koishi_1.Service {
|
|
|
404
479
|
}
|
|
405
480
|
(function (BiliAPI) {
|
|
406
481
|
BiliAPI.Config = koishi_1.Schema.object({
|
|
407
|
-
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()
|
|
408
486
|
});
|
|
409
487
|
})(BiliAPI || (BiliAPI = {}));
|
|
410
488
|
exports.default = BiliAPI;
|
package/lib/comRegister.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ declare class ComRegister {
|
|
|
32
32
|
getTheCorrespondingBotBasedOnTheSession(session: Session): Bot<Context, any>;
|
|
33
33
|
sendPrivateMsg(bot: Bot<Context>, content: string): Promise<void>;
|
|
34
34
|
sendPrivateMsgAndRebootService(ctx: Context, bot: Bot<Context>): Promise<void>;
|
|
35
|
+
sendPrivateMsgAndStopService(ctx: Context, bot: Bot<Context>): Promise<void>;
|
|
35
36
|
sendMsg(targets: Array<string>, bot: Bot<Context>, content: any): Promise<void>;
|
|
36
37
|
dynamicDetect(ctx: Context, bot: Bot<Context>, uid: string, guildId: Array<string>): () => Promise<void>;
|
|
37
38
|
debug_dynamicDetect(ctx: Context, bot: Bot<Context>, uid: string, guildId: Array<string>): () => Promise<void>;
|
package/lib/comRegister.js
CHANGED
|
@@ -4,13 +4,13 @@ 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/ban-types */
|
|
7
8
|
/* eslint-disable @typescript-eslint/no-namespace */
|
|
8
9
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
10
10
|
const koishi_1 = require("koishi");
|
|
11
11
|
// 导入qrcode
|
|
12
12
|
const qrcode_1 = __importDefault(require("qrcode"));
|
|
13
|
-
const jimp_1 =
|
|
13
|
+
const jimp_1 = require("jimp");
|
|
14
14
|
var LiveType;
|
|
15
15
|
(function (LiveType) {
|
|
16
16
|
LiveType[LiveType["NotLiveBroadcast"] = 0] = "NotLiveBroadcast";
|
|
@@ -18,7 +18,7 @@ var LiveType;
|
|
|
18
18
|
LiveType[LiveType["LiveBroadcast"] = 2] = "LiveBroadcast";
|
|
19
19
|
})(LiveType || (LiveType = {}));
|
|
20
20
|
class ComRegister {
|
|
21
|
-
static inject = ['ba', 'gi', '
|
|
21
|
+
static inject = ['ba', 'gi', 'database', 'sm'];
|
|
22
22
|
logger;
|
|
23
23
|
config;
|
|
24
24
|
loginTimer;
|
|
@@ -46,12 +46,6 @@ class ComRegister {
|
|
|
46
46
|
sendMsgFunc;
|
|
47
47
|
constructor(ctx, config) {
|
|
48
48
|
this.logger = ctx.logger('cr');
|
|
49
|
-
/* ctx.on('ready', () => {
|
|
50
|
-
this.logger.info('工作中');
|
|
51
|
-
})
|
|
52
|
-
ctx.on('dispose', () => {
|
|
53
|
-
this.logger.info('已停止工作');
|
|
54
|
-
}) */
|
|
55
49
|
this.config = config;
|
|
56
50
|
// 拿到各类机器人
|
|
57
51
|
ctx.bots.forEach(bot => {
|
|
@@ -261,7 +255,8 @@ class ComRegister {
|
|
|
261
255
|
await session.send(koishi_1.h.image(buffer, 'image/png'));
|
|
262
256
|
});
|
|
263
257
|
// 检查之前是否存在登录定时器
|
|
264
|
-
|
|
258
|
+
if (this.loginTimer)
|
|
259
|
+
this.loginTimer();
|
|
265
260
|
// 设置flag
|
|
266
261
|
let flag = true;
|
|
267
262
|
// 发起登录请求检查登录状态
|
|
@@ -289,8 +284,8 @@ class ComRegister {
|
|
|
289
284
|
return await session.send('二维码已失效,请重新登录');
|
|
290
285
|
}
|
|
291
286
|
if (loginContent.data.code === 0) { // 登录成功
|
|
292
|
-
const encryptedCookies = ctx.
|
|
293
|
-
const encryptedRefreshToken = ctx.
|
|
287
|
+
const encryptedCookies = ctx.ba.encrypt(ctx.ba.getCookies());
|
|
288
|
+
const encryptedRefreshToken = ctx.ba.encrypt(loginContent.data.refresh_token);
|
|
294
289
|
await ctx.database.upsert('loginBili', [{
|
|
295
290
|
id: 1,
|
|
296
291
|
bili_cookies: encryptedCookies,
|
|
@@ -364,7 +359,8 @@ class ComRegister {
|
|
|
364
359
|
}
|
|
365
360
|
}));
|
|
366
361
|
// 未订阅该用户,无需取消订阅
|
|
367
|
-
!exist
|
|
362
|
+
if (!exist)
|
|
363
|
+
session.send('未订阅该用户,无需取消订阅');
|
|
368
364
|
});
|
|
369
365
|
biliCom
|
|
370
366
|
.subcommand('.show', '展示订阅对象')
|
|
@@ -431,7 +427,7 @@ class ComRegister {
|
|
|
431
427
|
msg = '用户不存在';
|
|
432
428
|
break;
|
|
433
429
|
case -352:
|
|
434
|
-
msg = '
|
|
430
|
+
msg = '风控校验失败';
|
|
435
431
|
break;
|
|
436
432
|
default:
|
|
437
433
|
msg = '未知错误,错误信息:' + content.message;
|
|
@@ -708,7 +704,7 @@ class ComRegister {
|
|
|
708
704
|
// 获得对应bot
|
|
709
705
|
const bot = this.getTheCorrespondingBotBasedOnTheSession(session)
|
|
710
706
|
// 发送提示消息,重启服务
|
|
711
|
-
await this.
|
|
707
|
+
await this.sendPrivateMsgAndStopService(ctx, bot, '测试biliAPI等服务自动重启功能')
|
|
712
708
|
}) */
|
|
713
709
|
/* biliCom
|
|
714
710
|
.subcommand('.sendall', '测试给机器人加入的所有群发送消息', { hidden: true })
|
|
@@ -812,6 +808,16 @@ class ComRegister {
|
|
|
812
808
|
await ctx.sm.disposePlugin();
|
|
813
809
|
}
|
|
814
810
|
}
|
|
811
|
+
async sendPrivateMsgAndStopService(ctx, bot) {
|
|
812
|
+
// 发送消息
|
|
813
|
+
await this.sendPrivateMsg(bot, '插件发生未知错误,请检查机器人状态后使用指令 sys start 启动插件');
|
|
814
|
+
// logger
|
|
815
|
+
this.logger.error('插件发生未知错误,请检查机器人状态后使用指令 sys start 启动插件');
|
|
816
|
+
// 关闭插件
|
|
817
|
+
await ctx.sm.disposePlugin();
|
|
818
|
+
// 结束
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
815
821
|
async sendMsg(targets, bot, content) {
|
|
816
822
|
// 定义需要发送的数组
|
|
817
823
|
let sendArr = [];
|
|
@@ -946,18 +952,22 @@ class ComRegister {
|
|
|
946
952
|
return updatePoint(num);
|
|
947
953
|
if (e.message === '出现关键词,屏蔽该动态') {
|
|
948
954
|
// 如果需要发送才发送
|
|
949
|
-
this.config.filter.notify
|
|
955
|
+
if (this.config.filter.notify) {
|
|
956
|
+
await this.sendMsg(guildId, bot, `${upName}发布了一条含有屏蔽关键字的动态`);
|
|
957
|
+
}
|
|
950
958
|
return updatePoint(num);
|
|
951
959
|
}
|
|
952
960
|
if (e.message === '已屏蔽转发动态') {
|
|
953
|
-
this.config.filter.notify
|
|
961
|
+
if (this.config.filter.notify) {
|
|
962
|
+
await this.sendMsg(guildId, bot, `${upName}发布了一条转发动态,已屏蔽`);
|
|
963
|
+
}
|
|
954
964
|
return updatePoint(num);
|
|
955
965
|
}
|
|
956
966
|
// 未知错误
|
|
957
967
|
if (i === attempts - 1) {
|
|
958
968
|
this.logger.error('dynamicDetect generateDynamicImg() 推送卡片发送失败,原因:' + e.message);
|
|
959
969
|
// 发送私聊消息并重启服务
|
|
960
|
-
return await this.
|
|
970
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
961
971
|
}
|
|
962
972
|
}
|
|
963
973
|
}
|
|
@@ -1109,18 +1119,22 @@ class ComRegister {
|
|
|
1109
1119
|
return updatePoint(num);
|
|
1110
1120
|
if (e.message === '出现关键词,屏蔽该动态') {
|
|
1111
1121
|
// 如果需要发送才发送
|
|
1112
|
-
this.config.filter.notify
|
|
1122
|
+
if (this.config.filter.notify) {
|
|
1123
|
+
await this.sendMsg(guildId, bot, `${upName}发布了一条含有屏蔽关键字的动态`);
|
|
1124
|
+
}
|
|
1113
1125
|
return updatePoint(num);
|
|
1114
1126
|
}
|
|
1115
1127
|
if (e.message === '已屏蔽转发动态') {
|
|
1116
|
-
this.config.filter.notify
|
|
1128
|
+
if (this.config.filter.notify) {
|
|
1129
|
+
await this.sendMsg(guildId, bot, `${upName}发布了一条转发动态,已屏蔽`);
|
|
1130
|
+
}
|
|
1117
1131
|
return updatePoint(num);
|
|
1118
1132
|
}
|
|
1119
1133
|
// 未知错误
|
|
1120
1134
|
if (i === attempts - 1) {
|
|
1121
1135
|
this.logger.error('dynamicDetect generateDynamicImg() 推送卡片发送失败,原因:' + e.message);
|
|
1122
1136
|
// 发送私聊消息并重启服务
|
|
1123
|
-
return await this.
|
|
1137
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1124
1138
|
}
|
|
1125
1139
|
}
|
|
1126
1140
|
}
|
|
@@ -1185,7 +1199,7 @@ class ComRegister {
|
|
|
1185
1199
|
if (i === attempts - 1) { // 已尝试三次
|
|
1186
1200
|
this.logger.error('liveDetect generateLiveImg() 推送卡片生成失败,原因:' + e.message);
|
|
1187
1201
|
// 发送私聊消息并重启服务
|
|
1188
|
-
return await this.
|
|
1202
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1189
1203
|
}
|
|
1190
1204
|
}
|
|
1191
1205
|
}
|
|
@@ -1221,7 +1235,7 @@ class ComRegister {
|
|
|
1221
1235
|
if (i === attempts - 1) { // 已尝试三次
|
|
1222
1236
|
this.logger.error('liveDetect generateLiveImg() 推送卡片生成失败,原因:' + e.message);
|
|
1223
1237
|
// 发送私聊消息并重启服务
|
|
1224
|
-
return await this.
|
|
1238
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1225
1239
|
}
|
|
1226
1240
|
}
|
|
1227
1241
|
}
|
|
@@ -1273,7 +1287,7 @@ class ComRegister {
|
|
|
1273
1287
|
this.logger.error('liveDetect getLiveRoomInfo 发生了错误,错误为:' + e.message);
|
|
1274
1288
|
if (i === attempts - 1) { // 已尝试三次
|
|
1275
1289
|
// 发送私聊消息并重启服务
|
|
1276
|
-
return await this.
|
|
1290
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1277
1291
|
}
|
|
1278
1292
|
}
|
|
1279
1293
|
}
|
|
@@ -1294,7 +1308,7 @@ class ComRegister {
|
|
|
1294
1308
|
this.logger.error('liveDetect getMasterInfo() 发生了错误,错误为:' + e.message);
|
|
1295
1309
|
if (i === attempts - 1) { // 已尝试三次
|
|
1296
1310
|
// 发送私聊消息并重启服务
|
|
1297
|
-
return await this.
|
|
1311
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1298
1312
|
}
|
|
1299
1313
|
}
|
|
1300
1314
|
}
|
|
@@ -1326,8 +1340,8 @@ class ComRegister {
|
|
|
1326
1340
|
let resizedImage;
|
|
1327
1341
|
// Jimp无法处理Webp格式,直接跳过
|
|
1328
1342
|
try {
|
|
1329
|
-
resizedImage = await jimp_1.
|
|
1330
|
-
return await image.resize(100, 100).
|
|
1343
|
+
resizedImage = await jimp_1.Jimp.read(userface).then(async (image) => {
|
|
1344
|
+
return await image.resize({ w: 100, h: 100 }).getBuffer(jimp_1.JimpMime.png);
|
|
1331
1345
|
});
|
|
1332
1346
|
}
|
|
1333
1347
|
catch (e) {
|
|
@@ -1362,7 +1376,7 @@ class ComRegister {
|
|
|
1362
1376
|
this.logger.error('liveDetect open getMasterInfo() 发生了错误,错误为:' + e.message);
|
|
1363
1377
|
if (i === attempts - 1) { // 已尝试三次
|
|
1364
1378
|
// 发送私聊消息并重启服务
|
|
1365
|
-
return await this.
|
|
1379
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1366
1380
|
}
|
|
1367
1381
|
}
|
|
1368
1382
|
}
|
|
@@ -1448,7 +1462,8 @@ class ComRegister {
|
|
|
1448
1462
|
}
|
|
1449
1463
|
updateSubNotifier(ctx) {
|
|
1450
1464
|
// 更新控制台提示
|
|
1451
|
-
|
|
1465
|
+
if (this.subNotifier)
|
|
1466
|
+
this.subNotifier.dispose();
|
|
1452
1467
|
// 获取订阅信息
|
|
1453
1468
|
const subInfo = this.subShow();
|
|
1454
1469
|
// 定义table
|
|
@@ -1571,7 +1586,7 @@ class ComRegister {
|
|
|
1571
1586
|
this.logger.error('getSubFromDatabase() getUserInfo() 发生了错误,错误为:' + e.message);
|
|
1572
1587
|
if (i === attempts - 1) { // 已尝试三次
|
|
1573
1588
|
// 发送私聊消息并重启服务
|
|
1574
|
-
return await this.
|
|
1589
|
+
return await this.sendPrivateMsgAndStopService(ctx, bot);
|
|
1575
1590
|
}
|
|
1576
1591
|
}
|
|
1577
1592
|
}
|
|
@@ -1674,7 +1689,8 @@ class ComRegister {
|
|
|
1674
1689
|
if (index === -1)
|
|
1675
1690
|
return '未订阅该用户,无需取消订阅';
|
|
1676
1691
|
// 取消订阅
|
|
1677
|
-
this.subManager[index].live
|
|
1692
|
+
if (this.subManager[index].live)
|
|
1693
|
+
this.subManager[index].liveDispose();
|
|
1678
1694
|
this.subManager[index].liveDispose = null;
|
|
1679
1695
|
this.subManager[index].live = false;
|
|
1680
1696
|
// 如果没有对这个UP的任何订阅,则移除
|
|
@@ -1693,7 +1709,8 @@ class ComRegister {
|
|
|
1693
1709
|
if (index === -1)
|
|
1694
1710
|
return '未订阅该用户,无需取消订阅';
|
|
1695
1711
|
// 取消订阅
|
|
1696
|
-
this.subManager[index].dynamic
|
|
1712
|
+
if (this.subManager[index].dynamic)
|
|
1713
|
+
this.subManager[index].dynamicDispose();
|
|
1697
1714
|
this.subManager[index].dynamicDispose = null;
|
|
1698
1715
|
this.subManager[index].dynamic = false;
|
|
1699
1716
|
// 如果没有对这个UP的任何订阅,则移除
|
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
|
@@ -35,7 +35,6 @@ const koishi_1 = require("koishi");
|
|
|
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'];
|
|
@@ -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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-bilibili-notify",
|
|
3
3
|
"description": "Koishi bilibili notify plugin",
|
|
4
|
-
"version": "1.3.6-
|
|
4
|
+
"version": "1.3.6-rc.0",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Akokko <admin@akokko.com>"
|
|
7
7
|
],
|
|
@@ -27,25 +27,25 @@
|
|
|
27
27
|
"koishi": "^4.17.5"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"axios": "^1.7.
|
|
31
|
-
"axios-cookiejar-support": "^5.0.
|
|
32
|
-
"jimp": "^
|
|
33
|
-
"jsdom": "^24.
|
|
34
|
-
"luxon": "^3.
|
|
30
|
+
"axios": "^1.7.7",
|
|
31
|
+
"axios-cookiejar-support": "^5.0.3",
|
|
32
|
+
"jimp": "^1.6.0",
|
|
33
|
+
"jsdom": "^24.1.3",
|
|
34
|
+
"luxon": "^3.5.0",
|
|
35
35
|
"md5": "^2.3.0",
|
|
36
|
-
"qrcode": "^1.5.
|
|
36
|
+
"qrcode": "^1.5.4",
|
|
37
37
|
"tough-cookie": "^4.1.4"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@eslint/js": "^9.
|
|
41
|
-
"@types/luxon": "^3",
|
|
42
|
-
"@types/md5": "^2",
|
|
43
|
-
"@types/qrcode": "^1",
|
|
44
|
-
"@types/tough-cookie": "^4",
|
|
45
|
-
"eslint": "9.
|
|
46
|
-
"globals": "^15.
|
|
47
|
-
"koishi-plugin-puppeteer": "^3.
|
|
48
|
-
"typescript-eslint": "^7.
|
|
40
|
+
"@eslint/js": "^9.12.0",
|
|
41
|
+
"@types/luxon": "^3.4.2",
|
|
42
|
+
"@types/md5": "^2.3.5",
|
|
43
|
+
"@types/qrcode": "^1.5.5",
|
|
44
|
+
"@types/tough-cookie": "^4.0.5",
|
|
45
|
+
"eslint": "^9.12.0",
|
|
46
|
+
"globals": "^15.11.0",
|
|
47
|
+
"koishi-plugin-puppeteer": "^3.9.0",
|
|
48
|
+
"typescript-eslint": "^7.18.0"
|
|
49
49
|
},
|
|
50
50
|
"koishi": {
|
|
51
51
|
"service": {
|
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;
|