koishi-plugin-bilibili-notify 1.3.6-beta.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 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/no-unused-vars */
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', 'wbi', 'notifier'];
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
- /* protected stop(): void | Promise<void> {
51
- this.logger.info('已停止工作')
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.ctx.wbi.getWbi({ mid });
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.ctx.wbi.decrypt(data.bili_cookies);
266
+ const decryptedCookies = this.decrypt(data.bili_cookies);
214
267
  // 解密refresh_token
215
- const decryptedRefreshToken = this.ctx.wbi.decrypt(data.bili_refresh_token);
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 crypto.subtle.importKey("jwk", {
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 crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
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.ctx.wbi.encrypt(this.getCookies());
376
- const encryptedRefreshToken = this.ctx.wbi.encrypt(refreshData.data.refresh_token);
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;
@@ -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/no-unused-vars */
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', 'wbi', 'database', 'sm'];
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.wbi.encrypt(ctx.ba.getCookies());
295
- const encryptedRefreshToken = ctx.wbi.encrypt(loginContent.data.refresh_token);
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 = '风控校验失败';
438
431
  break;
439
432
  default:
440
433
  msg = '未知错误,错误信息:' + content.message;
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: () => Promise<boolean>;
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/no-empty-object-type */
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'];
@@ -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 = async () => {
255
+ registerPlugin = () => {
252
256
  // 如果已经有服务则返回false
253
257
  if (this.servers.length !== 0)
254
258
  return false;
255
- await new Promise(resolve => {
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
- const wbi = this.ctx.plugin(wbi_1.default, { key: globalConfig.key });
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
- resolve('ok');
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-beta.0",
4
+ "version": "1.3.6-rc.0",
5
5
  "contributors": [
6
6
  "Akokko <admin@akokko.com>"
7
7
  ],
package/readme.md CHANGED
@@ -173,6 +173,7 @@
173
173
  - ver 1.3.5 动态监测循环时间新增20分钟选项
174
174
  - ver 1.3.6-alpha.0 修复bug:无限重复的报错提示
175
175
  - ver 1.3.6-beta.0 取消出错自动重启插件功能
176
+ - ver 1.3.6-rc.0 修复重启或更新后提示未登录或订阅时提示请登录后再订阅的问题
176
177
 
177
178
  ## 交流群
178
179
 
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;