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 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', 'wbi', 'notifier'];
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
- /* protected stop(): void | Promise<void> {
50
- this.logger.info('已停止工作')
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.ctx.wbi.getWbi({ mid });
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() { this.loginNotifier && this.loginNotifier.dispose(); }
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.ctx.wbi.decrypt(data.bili_cookies);
266
+ const decryptedCookies = this.decrypt(data.bili_cookies);
212
267
  // 解密refresh_token
213
- const decryptedRefreshToken = this.ctx.wbi.decrypt(data.bili_refresh_token);
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
- this.refreshCookieTimer && this.refreshCookieTimer();
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 crypto.subtle.importKey("jwk", {
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 crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
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.ctx.wbi.encrypt(this.getCookies());
373
- 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);
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;
@@ -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>;
@@ -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 = __importDefault(require("jimp"));
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', 'wbi', 'database', 'sm'];
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
- this.loginTimer && this.loginTimer();
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.wbi.encrypt(ctx.ba.getCookies());
293
- 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);
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 && session.send('未订阅该用户,无需取消订阅');
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.sendPrivateMsgAndRebootService(ctx, bot, '测试biliAPI等服务自动重启功能')
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 && await this.sendMsg(guildId, bot, `${upName}发布了一条含有屏蔽关键字的动态`);
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 && await this.sendMsg(guildId, bot, `${upName}发布了一条转发动态,已屏蔽`);
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.sendPrivateMsgAndRebootService(ctx, bot);
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 && await this.sendMsg(guildId, bot, `${upName}发布了一条含有屏蔽关键字的动态`);
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 && await this.sendMsg(guildId, bot, `${upName}发布了一条转发动态,已屏蔽`);
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.sendPrivateMsgAndRebootService(ctx, bot);
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.sendPrivateMsgAndRebootService(ctx, bot);
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.sendPrivateMsgAndRebootService(ctx, bot);
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.sendPrivateMsgAndRebootService(ctx, bot);
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.sendPrivateMsgAndRebootService(ctx, bot);
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.default.read(userface).then(async (image) => {
1330
- return await image.resize(100, 100).getBufferAsync(jimp_1.default.MIME_PNG);
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.sendPrivateMsgAndRebootService(ctx, bot);
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
- this.subNotifier && this.subNotifier.dispose();
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.sendPrivateMsgAndRebootService(ctx, bot);
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 && this.subManager[index].liveDispose();
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 && this.subManager[index].dynamicDispose();
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: () => Promise<boolean>;
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 = 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-alpha.0",
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.0-beta.1",
31
- "axios-cookiejar-support": "^5.0.2",
32
- "jimp": "^0.22.12",
33
- "jsdom": "^24.0.0",
34
- "luxon": "^3.4.4",
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.3",
36
+ "qrcode": "^1.5.4",
37
37
  "tough-cookie": "^4.1.4"
38
38
  },
39
39
  "devDependencies": {
40
- "@eslint/js": "^9.6.0",
41
- "@types/luxon": "^3",
42
- "@types/md5": "^2",
43
- "@types/qrcode": "^1",
44
- "@types/tough-cookie": "^4",
45
- "eslint": "9.x",
46
- "globals": "^15.6.0",
47
- "koishi-plugin-puppeteer": "^3.8.4",
48
- "typescript-eslint": "^7.14.1"
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
@@ -172,6 +172,8 @@
172
172
  - ver 1.3.4 新增消息推送失败是否自动重发的选项,修复了一些潜在的bug
173
173
  - ver 1.3.5 动态监测循环时间新增20分钟选项
174
174
  - ver 1.3.6-alpha.0 修复bug:无限重复的报错提示
175
+ - ver 1.3.6-beta.0 取消出错自动重启插件功能
176
+ - ver 1.3.6-rc.0 修复重启或更新后提示未登录或订阅时提示请登录后再订阅的问题
175
177
 
176
178
  ## 交流群
177
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;