koishi-plugin-bilibili-notify 2.0.0-alpha.2 → 2.0.0-alpha.21

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.
@@ -19,6 +19,7 @@ var LiveType;
19
19
  })(LiveType || (LiveType = {}));
20
20
  class ComRegister {
21
21
  static inject = ['ba', 'gi', 'database', 'sm'];
22
+ qqRelatedBotList = ['qq', 'onebot', 'red', 'satori', 'chronocat'];
22
23
  logger;
23
24
  config;
24
25
  loginTimer;
@@ -26,63 +27,19 @@ class ComRegister {
26
27
  rebootCount = 0;
27
28
  subNotifier;
28
29
  subManager = [];
30
+ // 检查登录数据库是否有数据
31
+ loginDBData;
29
32
  // 机器人实例
30
- bot;
33
+ privateBot;
31
34
  // 动态销毁函数
32
35
  dynamicDispose;
33
36
  // 发送消息方式
34
37
  sendMsgFunc;
35
38
  // 构造函数
36
39
  constructor(ctx, config) {
37
- this.logger = ctx.logger('cr');
38
- this.config = config;
39
- // 拿到机器人实例
40
- this.bot = ctx.bots.find(bot => bot.platform === config.platform);
41
- if (!this.bot) {
42
- ctx.notifier.create({
43
- type: 'danger',
44
- content: '未找到对应机器人实例,请检查配置是否正确或重新配置适配器!'
45
- });
46
- this.logger.error('未找到对应机器人实例,请检查配置是否正确或重新配置适配器!');
47
- }
48
- // 从数据库获取订阅
49
- this.getSubFromDatabase(ctx);
50
- // 判断消息发送方式
51
- if (config.automaticResend) {
52
- this.sendMsgFunc = async (guild, content) => {
53
- // 多次尝试发送消息
54
- const attempts = 3;
55
- for (let i = 0; i < attempts; i++) {
56
- try {
57
- // 发送消息
58
- await this.bot.sendMessage(guild, content);
59
- // 防止消息发送速度过快被忽略
60
- await ctx.sleep(500);
61
- // 成功发送消息,跳出循环
62
- break;
63
- }
64
- catch (e) {
65
- if (i === attempts - 1) { // 已尝试三次
66
- this.logger.error(`发送群组ID:${guild}消息失败!原因: ` + e.message);
67
- console.log(e);
68
- this.sendPrivateMsg(`发送群组ID:${guild}消息失败,请查看日志`);
69
- }
70
- }
71
- }
72
- };
73
- }
74
- else {
75
- this.sendMsgFunc = async (guild, content) => {
76
- try {
77
- // 发送消息
78
- await this.bot.sendMessage(guild, content);
79
- }
80
- catch (e) {
81
- this.logger.error(`发送群组ID:${guild}消息失败!原因: ` + e.message);
82
- await this.sendPrivateMsg(`发送群组ID:${guild}消息失败,请查看日志`);
83
- }
84
- };
85
- }
40
+ // 初始化
41
+ this.init(ctx, config);
42
+ // 注册指令
86
43
  const statusCom = ctx.command('status', '插件状态相关指令', { permissions: ['authority:5'] });
87
44
  statusCom.subcommand('.dyn', '查看动态监测运行状态')
88
45
  .usage('查看动态监测运行状态')
@@ -99,9 +56,30 @@ class ComRegister {
99
56
  .usage('查看订阅管理对象')
100
57
  .example('status sm')
101
58
  .action(async () => {
102
- console.log(this.subManager);
59
+ this.logger.info(this.subManager);
103
60
  return '查看控制台';
104
61
  });
62
+ statusCom
63
+ .subcommand('.bot', '查询当前拥有的机器人信息', { hidden: true })
64
+ .usage('查询当前拥有的机器人信息')
65
+ .example('status bot 查询当前拥有的机器人信息')
66
+ .action(() => {
67
+ this.logger.info('开始输出BOT信息');
68
+ ctx.bots.forEach(bot => {
69
+ this.logger.info('--------------------------------');
70
+ this.logger.info('平台:' + bot.platform);
71
+ this.logger.info('名称:' + bot.user.name);
72
+ this.logger.info('--------------------------------');
73
+ });
74
+ });
75
+ statusCom
76
+ .subcommand('.env', '查询当前环境的信息', { hidden: true })
77
+ .usage('查询当前环境的信息')
78
+ .example('status env 查询当前环境的信息')
79
+ .action(async ({ session }) => {
80
+ await session.send(`Guild ID:${session.event.guild.id}`);
81
+ await session.send(`Channel ID: ${session.event.channel.id}`);
82
+ });
105
83
  const biliCom = ctx.command('bili', 'bili-notify插件相关指令', { permissions: ['authority:3'] });
106
84
  biliCom.subcommand('.login', '登录B站之后才可以进行之后的操作')
107
85
  .usage('使用二维码登录,登录B站之后才可以进行之后的操作')
@@ -173,7 +151,7 @@ class ComRegister {
173
151
  // 销毁定时器
174
152
  this.loginTimer();
175
153
  // 订阅之前的订阅
176
- await this.getSubFromDatabase(ctx);
154
+ await this.loadSubFromDatabase(ctx);
177
155
  // 清除控制台通知
178
156
  ctx.ba.disposeNotifier();
179
157
  // 发送成功登录推送
@@ -216,6 +194,7 @@ class ComRegister {
216
194
  await session.send(this.unsubSingle(ctx, sub.uid, 1)); /* 1为取消订阅Dynamic */
217
195
  // 将存在flag设置为true
218
196
  exist = true;
197
+ // 结束循环
219
198
  return;
220
199
  }
221
200
  // 取消全部订阅 执行dispose方法,销毁定时器
@@ -228,20 +207,18 @@ class ComRegister {
228
207
  // 将订阅对象移出订阅关注组
229
208
  const removeUserFromGroupData = await ctx.ba.removeUserFromGroup(sub.uid);
230
209
  // 判断是否移出成功 22105关注对象为自己
231
- if (removeUserFromGroupData.code !== 0 || removeUserFromGroupData.data !== 22105) {
210
+ if (removeUserFromGroupData.code !== 0 && removeUserFromGroupData.code !== 22105) {
232
211
  // 移出失败
233
212
  await session.send('取消订阅对象失败,请稍后重试');
213
+ // 将存在flag设置为true
214
+ exist = true;
215
+ // 结束循环
234
216
  return;
235
217
  }
236
218
  // id--
237
219
  this.num--;
238
220
  // 判断是否还有动态订阅
239
- if (this.dynamicDispose && !this.subManager.find((sub) => sub.dynamic === true)) { // 没有动态订阅
240
- // 将动态检测关闭
241
- this.dynamicDispose();
242
- // 将动态监测置为空
243
- this.dynamicDispose = null;
244
- }
221
+ this.checkIfUserIsTheLastOneWhoSubDyn();
245
222
  // 发送成功通知
246
223
  await session.send('已取消订阅该用户');
247
224
  // 更新控制台提示
@@ -263,30 +240,19 @@ class ComRegister {
263
240
  return subTable;
264
241
  });
265
242
  biliCom
266
- .subcommand('.sub <mid:string> [...guildId:string]', '订阅用户动态和直播通知')
243
+ .subcommand('.sub <mid:string> [...groupId:string]', '订阅用户动态和直播通知')
244
+ .option('multiplatform', '-m <value:string>', { type: /^(?:-?[A-Za-z0-9]+@?(?:,-?[A-Za-z0-9]+@?)*\.[A-Za-z0-9]+)(?:;(?:-?[A-Za-z0-9]+@?(?:,-?[A-Za-z0-9]+@?)*\.[A-Za-z0-9]+))*$/ })
267
245
  .option('live', '-l')
268
246
  .option('dynamic', '-d')
269
- .usage('订阅用户动态和直播通知,若需要订阅直播请加上-l,需要订阅动态则加上-d。若没有加任何参数,之后会向你单独询问,尖括号中为必选参数,中括号为可选参数,目标群号若不填,则默认为当前群聊')
270
- .example('bili sub 1194210119 目标QQ群号(实验性) -l -d 订阅UID为1194210119的UP主的动态和直播')
271
- .action(async ({ session, options }, mid, ...guildId) => {
247
+ .option('atAll', '-a')
248
+ .usage('订阅用户动态和直播通知,若需要订阅直播请加上-l,需要订阅动态则加上-d')
249
+ .example('bili sub 1194210119 目标群号或频道号 -l -d 订阅UID为1194210119的UP主的动态和直播')
250
+ .action(async ({ session, options }, mid, ...groupId) => {
272
251
  this.logger.info('调用bili.sub指令');
273
252
  // 先判断是否订阅直播,再判断是否解锁订阅限制,最后判断直播订阅是否已超三个
274
253
  if (options.live && !this.config.unlockSubLimits && (this.subManager.reduce((acc, cur) => acc + (cur.live ? 1 : 0), 0) >= 3)) {
275
254
  return '直播订阅已达上限,请取消部分直播订阅后再进行订阅';
276
255
  }
277
- // 检查是否是不支持的平台
278
- switch (session.event.platform) {
279
- case 'lark':
280
- case 'red':
281
- case 'onebot':
282
- case 'telegram':
283
- case 'satori':
284
- case 'chronocat':
285
- case 'qq':
286
- case 'qqguild':
287
- case 'discord': break;
288
- default: return '暂不支持该平台';
289
- }
290
256
  // 检查是否登录
291
257
  if (!(await this.checkIfIsLogin(ctx))) {
292
258
  // 未登录直接返回
@@ -295,54 +261,102 @@ class ComRegister {
295
261
  // 检查必选参数是否已填
296
262
  if (!mid)
297
263
  return '请输入用户uid';
298
- // 检查数据库是否有数据
299
- const loginDBData = (await ctx.database.get('loginBili', 1, ['dynamic_group_id']))[0];
300
- // 判断是否有数据
301
- if (loginDBData.dynamic_group_id === '' || loginDBData.dynamic_group_id === null) {
302
- // 没有数据,没有创建分组,尝试创建分组
303
- const createGroupData = await ctx.ba.createGroup("订阅");
304
- // 如果分组已创建,则获取分组id
305
- if (createGroupData.code === 22106) {
306
- // 分组已存在,拿到之前的分组id
307
- const allGroupData = await ctx.ba.getAllGroup();
308
- // 遍历所有分组
309
- for (const group of allGroupData.data) {
310
- // 找到订阅分组
311
- if (group.name === '订阅') {
312
- // 拿到分组id
313
- loginDBData.dynamic_group_id = group.tagid;
314
- // 结束循环
315
- break;
264
+ // 订阅对象
265
+ const subUserData = await this.subUserInBili(ctx, mid);
266
+ // 判断是否订阅对象存在
267
+ if (!subUserData.flag)
268
+ return '订阅对象失败,请稍后重试!';
269
+ // 定义目标变量
270
+ let target = [];
271
+ // 判断是否使用了多群组推送
272
+ if (groupId.length > 0) {
273
+ // 定义channelIdArr
274
+ const channelIdArr = [];
275
+ // 遍历输入的群组
276
+ groupId.forEach(group => {
277
+ channelIdArr.push({
278
+ channelId: group,
279
+ dynamic: true,
280
+ live: true,
281
+ atAll: options.atAll
282
+ });
283
+ });
284
+ target.push({
285
+ channelIdArr,
286
+ platform: session.event.platform
287
+ });
288
+ }
289
+ else {
290
+ // 判断是否使用多平台功能
291
+ if (options.multiplatform) {
292
+ // 分割字符串,赋值给target
293
+ target = this.splitMultiPlatformStr(options.multiplatform);
294
+ }
295
+ // 判断是否使用了多平台
296
+ if (target.length > 0) {
297
+ for (const [index, { channelIdArr, platform }] of target.entries()) {
298
+ if (channelIdArr.length > 0) { // 输入了推送群号或频道号
299
+ // 拿到对应的bot
300
+ const bot = this.getBot(ctx, platform);
301
+ // 判断是否配置了对应平台的机器人
302
+ if (!ctx.bots.some(bot => bot.platform === platform)) {
303
+ // 发送提示消息
304
+ await session.send('您未配置对应平台的机器人,不能在该平台进行订阅操作');
305
+ // 直接返回
306
+ return;
307
+ }
308
+ // 判断是否需要加入的群全部推送
309
+ if (channelIdArr[0].channelId !== 'all') {
310
+ // 定义满足条件的群组数组
311
+ const targetArr = [];
312
+ // 获取机器人加入的群组
313
+ const guildList = await bot.getGuildList();
314
+ // 遍历target数组
315
+ for (const channelId of channelIdArr) {
316
+ // 定义是否加入群组标志
317
+ let flag = false;
318
+ // 遍历群组
319
+ for (const guild of guildList.data) {
320
+ // 获取频道列表
321
+ const channelList = await bot.getChannelList(guild.id);
322
+ // 判断机器人是否加入群聊或频道
323
+ if (channelList.data.some(channel => channel.id === channelId.channelId)) {
324
+ // 加入群聊或频道
325
+ targetArr.push(channelId);
326
+ // 设置标志位为true
327
+ flag = true;
328
+ // 结束循环
329
+ break;
330
+ }
331
+ }
332
+ if (!flag) {
333
+ // 不满足条件发送错误提示
334
+ await session.send(`您的机器未加入${channelId.channelId},无法对该群或频道进行推送`);
335
+ }
336
+ }
337
+ // 判断targetArr是否为空
338
+ if (target.length === 0) {
339
+ // 为空则默认为当前环境
340
+ target = [{ channelIdArr: [{ channelId: session.event.channel.id, dynamic: true, live: true, atAll: options.atAll ? options.atAll : false }], platform: session.event.platform }];
341
+ // 没有满足条件的群组或频道
342
+ await session.send('没有满足条件的群组或频道,默认订阅到当前聊天环境');
343
+ }
344
+ // 将符合条件的群组添加到target中
345
+ target[index].channelIdArr = targetArr;
346
+ }
347
+ // 如果为all则全部推送,不需要进行处理
348
+ }
349
+ else {
350
+ // 未填写群号或频道号,默认为当前环境
351
+ target = [{ channelIdArr: [{ channelId: session.event.channel.id, dynamic: true, live: true, atAll: options.atAll ? options.atAll : false }], platform: session.event.platform }];
352
+ // 发送提示消息
353
+ await session.send('没有填写群号或频道号,默认订阅到当前聊天环境');
316
354
  }
317
355
  }
318
356
  }
319
- else if (createGroupData.code !== 0) {
320
- // 创建分组失败
321
- return '创建关注分组出错';
322
- }
323
- // 创建成功,保存到数据库
324
- ctx.database.set('loginBili', 1, { dynamic_group_id: loginDBData.dynamic_group_id });
325
- }
326
- // 订阅对象
327
- const subUserData = await ctx.ba.follow(mid);
328
- // 判断是否订阅成功
329
- switch (subUserData.code) {
330
- case -101: return '账号未登录,请使用指令bili login登录后再进行订阅操作';
331
- case -102: return '账号被封停,无法进行订阅操作';
332
- case 22002: return '因对方隐私设置,无法进行订阅操作';
333
- case 22003: return '你已将对方拉黑,无法进行订阅操作';
334
- case 22013: return '账号已注销,无法进行订阅操作';
335
- case 40061: return '账号不存在,请检查uid输入是否正确或用户是否存在';
336
- case 22001: break; // 订阅对象为自己 无需添加到分组
337
- case 22014: // 已关注订阅对象 无需再次关注
338
- case 0: { // 执行订阅成功
339
- // 把订阅对象添加到分组中
340
- const copyUserToGroupData = await ctx.ba.copyUserToGroup(mid, loginDBData.dynamic_group_id);
341
- // 判断是否添加成功
342
- if (copyUserToGroupData.code !== 0) {
343
- // 添加失败
344
- return '添加订阅对象到分组失败,请稍后重试';
345
- }
357
+ else {
358
+ // 用户直接订阅,将当前环境赋值给target
359
+ target = [{ channelIdArr: [{ channelId: session.event.channel.id, dynamic: true, live: true, atAll: options.atAll ? options.atAll : false }], platform: session.event.platform }];
346
360
  }
347
361
  }
348
362
  // 定义外围变量
@@ -380,88 +394,14 @@ class ComRegister {
380
394
  // 返回错误信息
381
395
  return msg;
382
396
  }
383
- // 定义目标群组id
384
- let targetId;
385
- // 定义目标群组id数组
386
- let targetIdArr;
387
- // 判断是否输入了QQ群号
388
- if (guildId.length > 0) { // 输入了QQ群号
389
- // 定义方法
390
- const checkIfGuildHasJoined = async (bot) => {
391
- // 获取机器人加入的群组
392
- const guildList = await bot.getGuildList();
393
- // 定义满足条件的群组数组
394
- const targetArr = [];
395
- // 判断群号是否符合条件
396
- for (const guild of guildId) {
397
- // 判断是否机器人加入了该群
398
- if (guildList.data.some(cv => cv.id === guild)) { // 机器人加入了该群
399
- // 保存到数组
400
- targetArr.push(guild);
401
- // 继续下一个循环
402
- continue;
403
- }
404
- // 不满足条件发送错误提示
405
- session.send(`您的机器未加入${guild},无法对该群进行推送`);
406
- }
407
- // 返回数组
408
- return targetArr;
409
- };
410
- // 定义可用的群组数组
411
- let okGuild = [];
412
- // 判断是否需要加入的群全部推送
413
- if (guildId[0] === 'all') {
414
- // 判断是否有群机器人相关Bot
415
- if (['qq', 'onebot', 'red', 'satori', 'chronocat'].includes(session.event.platform)) {
416
- // 推送所有群组
417
- okGuild.push('all');
418
- }
419
- else {
420
- // 发送错误提示并返回
421
- session.send('您尚未配置任何QQ群相关机器人,不能对QQ群进行操作');
422
- // 直接返回
423
- return;
424
- }
425
- }
426
- else {
427
- // 判断是否有群机器人相关Bot
428
- switch (session.event.platform) {
429
- case 'qq':
430
- case 'onebot':
431
- case 'red':
432
- case 'satori':
433
- case 'chronocat': {
434
- // 检查机器人是否加入该群
435
- okGuild = await checkIfGuildHasJoined(this.bot);
436
- break;
437
- }
438
- default: {
439
- // 发送错误提示并返回
440
- session.send('您尚未配置任何QQ群相关机器人,不能对QQ群进行操作');
441
- // 直接返回
442
- return;
443
- }
444
- }
445
- }
446
- // 将可用的目标赋值给数组
447
- targetIdArr = okGuild;
448
- // 将群号用空格进行分割
449
- targetId = okGuild.join(' ');
450
- }
451
- else { // 没有输入QQ群号
452
- // 为当前群聊环境进行推送
453
- targetId = session.event.channel.id;
454
- // 将目标id转换为数组进行赋值
455
- targetIdArr = [session.event.channel.id];
456
- }
457
397
  // 获取data
458
398
  const { data } = content;
459
399
  // 判断是否需要订阅直播和动态
460
- const [liveMsg, dynamicMsg] = await this.checkIfNeedSub(options.live, options.dynamic, session, data);
400
+ const [liveMsg, dynamicMsg] = await this.checkIfNeedSub(options.live, options.dynamic, session, data.live_room);
461
401
  // 判断是否未订阅任何消息
462
- if (!liveMsg && !dynamicMsg) {
402
+ if (!liveMsg && !dynamicMsg)
463
403
  return '您未订阅该UP的任何消息';
464
- }
404
+ // 获取到对应的订阅对象
465
405
  const subUser = this.subManager.find(sub => sub.uid === mid);
466
406
  // 判断要订阅的用户是否已经存在于订阅管理对象中
467
407
  if (subUser) {
@@ -476,44 +416,22 @@ class ComRegister {
476
416
  }
477
417
  // 获取直播房间号
478
418
  const roomId = data.live_room?.roomid.toString();
479
- // 保存到数据库中
480
- const sub = await ctx.database.create('bilibili', {
481
- uid: mid,
482
- room_id: roomId,
483
- dynamic: dynamicMsg ? 1 : 0,
484
- video: 1,
485
- live: liveMsg ? 1 : 0,
486
- targetId,
487
- platform: session.event.platform,
488
- time: new Date()
489
- });
490
- // 订阅数+1
491
- this.num++;
492
- // 保存新订阅对象
493
- this.subManager.push({
494
- id: sub.id,
495
- uid: mid,
496
- targetIdArr,
497
- roomId,
498
- platform: session.event.platform,
499
- live: liveMsg,
500
- dynamic: dynamicMsg,
501
- liveDispose: null
502
- });
503
419
  // 获取用户信息
504
420
  let userData;
505
421
  try {
506
- const { data } = await ctx.ba.getMasterInfo(sub.uid);
422
+ const { data } = await ctx.ba.getMasterInfo(mid);
507
423
  userData = data;
508
424
  }
509
425
  catch (e) {
510
426
  this.logger.error('bili sub指令 getMasterInfo() 发生了错误,错误为:' + e.message);
511
427
  return '订阅出错啦,请重试';
512
428
  }
513
- console.log(liveMsg, dynamicMsg);
429
+ // 定义live销毁函数
430
+ let liveDispose;
514
431
  // 订阅直播
515
432
  if (liveMsg) {
516
- await session.execute(`bili live ${roomId} ${targetId.split(',').join(' ')}`);
433
+ // 开始循环检测
434
+ liveDispose = ctx.setInterval(this.liveDetect(ctx, roomId, target), config.liveLoopTime * 1000);
517
435
  // 发送订阅消息通知
518
436
  await session.send(`订阅${userData.info.uname}直播通知`);
519
437
  }
@@ -521,40 +439,37 @@ class ComRegister {
521
439
  if (dynamicMsg) {
522
440
  // 判断是否开启动态监测
523
441
  if (!this.dynamicDispose) {
524
- // 开启动态监测
525
- if (this.config.dynamicDebugMode) {
526
- this.dynamicDispose = ctx.setInterval(this.debug_dynamicDetect(ctx), config.dynamicLoopTime * 1000);
527
- }
528
- else {
529
- this.dynamicDispose = ctx.setInterval(this.dynamicDetect(ctx), config.dynamicLoopTime * 1000);
530
- }
442
+ this.enableDynamicDetect(ctx);
531
443
  }
532
444
  // 发送订阅消息通知
533
445
  await session.send(`订阅${userData.info.uname}动态通知`);
534
446
  }
447
+ // 保存到数据库中
448
+ const sub = await ctx.database.create('bilibili', {
449
+ uid: mid,
450
+ room_id: roomId,
451
+ dynamic: dynamicMsg ? 1 : 0,
452
+ live: liveMsg ? 1 : 0,
453
+ target: JSON.stringify(target),
454
+ platform: session.event.platform,
455
+ time: new Date()
456
+ });
457
+ // 订阅数+1
458
+ this.num++;
459
+ // 保存新订阅对象
460
+ this.subManager.push({
461
+ id: sub.id,
462
+ uid: mid,
463
+ roomId,
464
+ target,
465
+ platform: session.event.platform,
466
+ live: liveMsg,
467
+ dynamic: dynamicMsg,
468
+ liveDispose
469
+ });
535
470
  // 新增订阅展示到控制台
536
471
  this.updateSubNotifier(ctx);
537
472
  });
538
- biliCom
539
- .subcommand('.live <roomId:string> <...guildId:string>', '订阅主播开播通知', { hidden: true })
540
- .usage('订阅主播开播通知')
541
- .example('bili live 26316137 订阅房间号为26316137的直播间')
542
- .action(async (_, roomId, ...guildId) => {
543
- this.logger.info('调用bili.live指令');
544
- // 如果room_id为空则返回
545
- if (!roomId)
546
- return `${roomId}非法调用 dynamic 指令`; // 订阅主播房间号不能为空
547
- if (!guildId)
548
- return `${roomId}非法调用 dynamic 指令`; // 目标群组或频道不能为空
549
- // 要订阅的对象不在订阅管理对象中,直接返回
550
- const index = this.subManager.findIndex(sub => sub.roomId === roomId);
551
- if (index === -1)
552
- return '请勿直接调用该指令';
553
- // 开始循环检测
554
- const dispose = ctx.setInterval(this.liveDetect(ctx, roomId, guildId), config.liveLoopTime * 1000);
555
- // 保存销毁函数
556
- this.subManager[index].liveDispose = dispose;
557
- });
558
473
  biliCom
559
474
  .subcommand('.status <roomId:string>', '查询主播当前直播状态', { hidden: true })
560
475
  .usage('查询主播当前直播状态')
@@ -598,19 +513,6 @@ class ComRegister {
598
513
  // pic不存在,说明使用的是page模式
599
514
  await session.send(koishi_1.h.image(buffer, 'image/png'));
600
515
  });
601
- biliCom
602
- .subcommand('.bot', '查询当前拥有的机器人信息', { hidden: true })
603
- .usage('查询当前拥有的机器人信息')
604
- .example('bili bot 查询当前拥有的机器人信息')
605
- .action(() => {
606
- this.logger.info('开始输出BOT信息');
607
- ctx.bots.forEach(bot => {
608
- this.logger.info('--------------------------------');
609
- this.logger.info('平台:' + bot.platform);
610
- this.logger.info('名称:' + bot.user.name);
611
- this.logger.info('--------------------------------');
612
- });
613
- });
614
516
  biliCom
615
517
  .subcommand('.private', '向主人账号发送一条测试消息', { hidden: true })
616
518
  .usage('向主人账号发送一条测试消息')
@@ -622,15 +524,88 @@ class ComRegister {
622
524
  await session.send('已发送消息,如未收到则说明您的机器人不支持发送私聊消息或您的信息填写有误');
623
525
  });
624
526
  }
527
+ async init(ctx, config) {
528
+ // 设置logger
529
+ this.logger = ctx.logger('cr');
530
+ // 将config设置给类属性
531
+ this.config = config;
532
+ // 拿到私人机器人实例
533
+ this.privateBot = ctx.bots.find(bot => bot.platform === config.master.platform);
534
+ if (!this.privateBot) {
535
+ ctx.notifier.create({
536
+ content: '您未配置私人机器人,将无法向您推送机器人状态!'
537
+ });
538
+ this.logger.error('您未配置私人机器人,将无法向您推送机器人状态!');
539
+ }
540
+ // 检查登录数据库是否有数据
541
+ this.loginDBData = (await ctx.database.get('loginBili', 1, ['dynamic_group_id']))[0];
542
+ // 从配置获取订阅
543
+ config.sub && await this.loadSubFromConfig(ctx, config.sub);
544
+ // 从数据库获取订阅
545
+ await this.loadSubFromDatabase(ctx);
546
+ // 判断消息发送方式
547
+ if (config.automaticResend) {
548
+ this.sendMsgFunc = async (bot, channelId, content) => {
549
+ // 多次尝试发送消息
550
+ const attempts = 3;
551
+ for (let i = 0; i < attempts; i++) {
552
+ try {
553
+ // 发送消息
554
+ await bot.sendMessage(channelId, content);
555
+ // 防止消息发送速度过快被忽略
556
+ await ctx.sleep(500);
557
+ // 成功发送消息,跳出循环
558
+ break;
559
+ }
560
+ catch (e) {
561
+ if (i === attempts - 1) { // 已尝试三次
562
+ this.logger.error(`发送群组ID:${channelId}消息失败!原因: ` + e.message);
563
+ console.log(e);
564
+ this.sendPrivateMsg(`发送群组ID:${channelId}消息失败,请查看日志`);
565
+ }
566
+ }
567
+ }
568
+ };
569
+ }
570
+ else {
571
+ this.sendMsgFunc = async (bot, guild, content) => {
572
+ try {
573
+ // 发送消息
574
+ await bot.sendMessage(guild, content);
575
+ }
576
+ catch (e) {
577
+ this.logger.error(`发送群组ID:${guild}消息失败!原因: ` + e.message);
578
+ await this.sendPrivateMsg(`发送群组ID:${guild}消息失败,请查看日志`);
579
+ }
580
+ };
581
+ }
582
+ // 检查是否需要动态监测
583
+ this.checkIfDynamicDetectIsNeeded(ctx);
584
+ // 在控制台中显示订阅对象
585
+ this.updateSubNotifier(ctx);
586
+ }
587
+ splitMultiPlatformStr(str) {
588
+ return str.split(';').map(cv => cv.split('.')).map(([idStr, platform]) => {
589
+ const channelIdArr = idStr.split(',').map(id => {
590
+ const atAll = /@$/.test(id); // 使用正则表达式检查 id 是否以 @ 结尾
591
+ const channelId = atAll ? id.slice(0, -1) : id; // 去除末尾的 @
592
+ return { channelId, dynamic: true, live: true, atAll };
593
+ });
594
+ return { channelIdArr, platform };
595
+ });
596
+ }
597
+ getBot(ctx, pf) {
598
+ return ctx.bots.find(bot => bot.platform === pf);
599
+ }
625
600
  async sendPrivateMsg(content) {
626
601
  if (this.config.master.enable) {
627
602
  if (this.config.master.masterAccountGuildId) {
628
603
  // 向机器人主人发送消息
629
- await this.bot.sendPrivateMessage(this.config.master.masterAccount, content, this.config.master.masterAccountGuildId);
604
+ await this.privateBot.sendPrivateMessage(this.config.master.masterAccount, content, this.config.master.masterAccountGuildId);
630
605
  }
631
606
  else {
632
607
  // 向机器人主人发送消息
633
- await this.bot.sendPrivateMessage(this.config.master.masterAccount, content);
608
+ await this.privateBot.sendPrivateMessage(this.config.master.masterAccount, content);
634
609
  }
635
610
  }
636
611
  }
@@ -675,22 +650,45 @@ class ComRegister {
675
650
  // 结束
676
651
  return;
677
652
  }
678
- async sendMsg(targets, content) {
679
- // 定义需要发送的数组
680
- let sendArr = [];
681
- // 判断是否需要推送所有机器人加入的群
682
- if (targets[0] === 'all') {
683
- // 获取所有guild
684
- for (const guild of (await this.bot.getGuildList()).data) {
685
- sendArr.push(guild.id);
653
+ async sendMsg(ctx, targets, content, live) {
654
+ for (const target of targets) {
655
+ // 获取机器人实例
656
+ const bot = this.getBot(ctx, target.platform);
657
+ // 定义需要发送的数组
658
+ let sendArr = [];
659
+ // 判断是否需要推送所有机器人加入的群
660
+ if (target.channelIdArr[0].channelId === 'all') {
661
+ // 获取所有guild
662
+ for (const guild of (await bot.getGuildList()).data) {
663
+ sendArr.push({
664
+ channelId: guild.id,
665
+ dynamic: target.channelIdArr[0].dynamic,
666
+ live: target.channelIdArr[0].live,
667
+ atAll: target.channelIdArr[0].atAll
668
+ });
669
+ }
670
+ }
671
+ else {
672
+ sendArr = target.channelIdArr;
673
+ }
674
+ // 判断是否是直播推送,如果是则需要进一步判断是否需要艾特群体成员
675
+ if (live) {
676
+ // 直播开播推送,判断是否需要艾特全体成员
677
+ for (const channel of sendArr) {
678
+ // 判断是否需要推送直播消息
679
+ if (channel.live) {
680
+ await this.sendMsgFunc(bot, channel.channelId, (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [content, channel.atAll ? (0, jsx_runtime_1.jsx)("at", { type: "all" }) : ''] }));
681
+ }
682
+ }
683
+ }
684
+ else {
685
+ for (const channel of sendArr) {
686
+ // 判断是否需要推送动态消息和直播消息
687
+ if (channel.dynamic || channel.live) {
688
+ await this.sendMsgFunc(bot, channel.channelId, content);
689
+ }
690
+ }
686
691
  }
687
- }
688
- else {
689
- sendArr = targets;
690
- }
691
- // 循环给每个群组发送
692
- for (const guild of sendArr) {
693
- await this.sendMsgFunc(guild, content);
694
692
  }
695
693
  }
696
694
  dynamicDetect(ctx) {
@@ -820,13 +818,13 @@ class ComRegister {
820
818
  if (e.message === '出现关键词,屏蔽该动态') {
821
819
  // 如果需要发送才发送
822
820
  if (this.config.filter.notify) {
823
- await this.sendMsg(sub.targetIdArr, `${upName}发布了一条含有屏蔽关键字的动态`);
821
+ await this.sendMsg(ctx, sub.target, `${upName}发布了一条含有屏蔽关键字的动态`);
824
822
  }
825
823
  return;
826
824
  }
827
825
  if (e.message === '已屏蔽转发动态') {
828
826
  if (this.config.filter.notify) {
829
- await this.sendMsg(sub.targetIdArr, `${upName}发布了一条转发动态,已屏蔽`);
827
+ await this.sendMsg(ctx, sub.target, `${upName}发布了一条转发动态,已屏蔽`);
830
828
  }
831
829
  return;
832
830
  }
@@ -844,12 +842,12 @@ class ComRegister {
844
842
  if (pic) {
845
843
  this.logger.info('推送动态中,使用render模式');
846
844
  // pic存在,使用的是render模式
847
- await this.sendMsg(sub.targetIdArr, pic + (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: dUrl }));
845
+ await this.sendMsg(ctx, sub.target, pic + (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: dUrl }));
848
846
  }
849
847
  else if (buffer) {
850
848
  this.logger.info('推送动态中,使用page模式');
851
849
  // pic不存在,说明使用的是page模式
852
- await this.sendMsg(sub.targetIdArr, (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), dUrl] }));
850
+ await this.sendMsg(ctx, sub.target, (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), dUrl] }));
853
851
  }
854
852
  else {
855
853
  this.logger.info(items[num].modules.module_author.name + '发布了一条动态,但是推送失败');
@@ -1004,13 +1002,13 @@ class ComRegister {
1004
1002
  if (e.message === '出现关键词,屏蔽该动态') {
1005
1003
  // 如果需要发送才发送
1006
1004
  if (this.config.filter.notify) {
1007
- await this.sendMsg(sub.targetIdArr, `${upName}发布了一条含有屏蔽关键字的动态`);
1005
+ await this.sendMsg(ctx, sub.target, `${upName}发布了一条含有屏蔽关键字的动态`);
1008
1006
  }
1009
1007
  return;
1010
1008
  }
1011
1009
  if (e.message === '已屏蔽转发动态') {
1012
1010
  if (this.config.filter.notify) {
1013
- await this.sendMsg(sub.targetIdArr, `${upName}发布了一条转发动态,已屏蔽`);
1011
+ await this.sendMsg(ctx, sub.target, `${upName}发布了一条转发动态,已屏蔽`);
1014
1012
  }
1015
1013
  return;
1016
1014
  }
@@ -1028,12 +1026,12 @@ class ComRegister {
1028
1026
  if (pic) {
1029
1027
  this.logger.info('推送动态中,使用render模式');
1030
1028
  // pic存在,使用的是render模式
1031
- await this.sendMsg(sub.targetIdArr, pic + (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: dUrl }));
1029
+ await this.sendMsg(ctx, sub.target, pic + (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: dUrl }));
1032
1030
  }
1033
1031
  else if (buffer) {
1034
1032
  this.logger.info('推送动态中,使用page模式');
1035
1033
  // pic不存在,说明使用的是page模式
1036
- await this.sendMsg(sub.targetIdArr, (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), dUrl] }));
1034
+ await this.sendMsg(ctx, sub.target, (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), dUrl] }));
1037
1035
  }
1038
1036
  else {
1039
1037
  this.logger.info(items[num].modules.module_author.name + '发布了一条动态,但是推送失败');
@@ -1047,7 +1045,7 @@ class ComRegister {
1047
1045
  }
1048
1046
  };
1049
1047
  }
1050
- liveDetect(ctx, roomId, guildId) {
1048
+ liveDetect(ctx, roomId, target) {
1051
1049
  let firstSubscription = true;
1052
1050
  let timer = 0;
1053
1051
  let open = false;
@@ -1057,80 +1055,49 @@ class ComRegister {
1057
1055
  // 相当于锁的作用,防止上一个循环没处理完
1058
1056
  let flag = true;
1059
1057
  // 定义发送直播通知卡片方法
1060
- let sendLiveNotifyCard;
1061
- // 判断直播是否需要@全体成员
1062
- if (this.config.pushUrl) {
1063
- sendLiveNotifyCard = async (data, liveType, liveStartMsg, atAll) => {
1064
- // 定义变量
1065
- let pic;
1066
- let buffer;
1067
- // 多次尝试生成图片
1068
- const attempts = 3;
1069
- for (let i = 0; i < attempts; i++) {
1070
- try {
1071
- // 获取直播通知卡片
1072
- const { pic: picv, buffer: bufferv } = await ctx.gi.generateLiveImg(data, username, userface, liveType);
1073
- // 赋值
1074
- pic = picv;
1075
- buffer = bufferv;
1076
- // 成功则跳出循环
1077
- break;
1078
- }
1079
- catch (e) {
1080
- if (i === attempts - 1) { // 已尝试三次
1081
- this.logger.error('liveDetect generateLiveImg() 推送卡片生成失败,原因:' + e.message);
1082
- // 发送私聊消息并重启服务
1083
- return await this.sendPrivateMsgAndStopService(ctx);
1084
- }
1085
- }
1086
- }
1087
- // 推送直播信息
1088
- // pic 存在,使用的是render模式
1089
- if (pic) {
1090
- const msg = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [atAll && (0, jsx_runtime_1.jsx)("at", { type: "all" }), liveStartMsg && liveStartMsg, liveType !== LiveType.StartBroadcasting ? `https://live.bilibili.com/${roomId}` : ''] });
1091
- return await this.sendMsg(guildId, pic + msg);
1058
+ const sendLiveNotifyCard = async (data, liveType, liveNotifyMsg) => {
1059
+ // 定义变量
1060
+ let pic;
1061
+ let buffer;
1062
+ // 多次尝试生成图片
1063
+ const attempts = 3;
1064
+ for (let i = 0; i < attempts; i++) {
1065
+ try {
1066
+ // 获取直播通知卡片
1067
+ const { pic: picv, buffer: bufferv } = await ctx.gi.generateLiveImg(data, username, userface, liveType);
1068
+ // 赋值
1069
+ pic = picv;
1070
+ buffer = bufferv;
1071
+ // 成功则跳出循环
1072
+ break;
1092
1073
  }
1093
- // pic不存在,说明使用的是page模式
1094
- const msg = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), atAll && (0, jsx_runtime_1.jsx)("at", { type: "all" }), liveStartMsg && liveStartMsg, liveType !== LiveType.StartBroadcasting ? `https://live.bilibili.com/${roomId}` : ''] });
1095
- await this.sendMsg(guildId, msg);
1096
- };
1097
- }
1098
- else {
1099
- sendLiveNotifyCard = async (data, liveType, liveStartMsg, atAll) => {
1100
- // 定义变量
1101
- let pic;
1102
- let buffer;
1103
- // 多次尝试生成图片
1104
- const attempts = 3;
1105
- for (let i = 0; i < attempts; i++) {
1106
- try {
1107
- // 获取直播通知卡片
1108
- const { pic: picv, buffer: bufferv } = await ctx.gi.generateLiveImg(data, username, userface, liveType);
1109
- // 赋值
1110
- pic = picv;
1111
- buffer = bufferv;
1112
- // 成功则跳出循环
1113
- break;
1114
- }
1115
- catch (e) {
1116
- if (i === attempts - 1) { // 已尝试三次
1117
- this.logger.error('liveDetect generateLiveImg() 推送卡片生成失败,原因:' + e.message);
1118
- // 发送私聊消息并重启服务
1119
- return await this.sendPrivateMsgAndStopService(ctx);
1120
- }
1074
+ catch (e) {
1075
+ if (i === attempts - 1) { // 已尝试三次
1076
+ this.logger.error('liveDetect generateLiveImg() 推送卡片生成失败,原因:' + e.message);
1077
+ // 发送私聊消息并重启服务
1078
+ return await this.sendPrivateMsgAndStopService(ctx);
1121
1079
  }
1122
1080
  }
1123
- // 推送直播信息
1124
- // pic 存在,使用的是render模式
1125
- if (pic) {
1126
- const msg = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [atAll && (0, jsx_runtime_1.jsx)("at", { type: "all" }), liveStartMsg && liveStartMsg] });
1127
- return await this.sendMsg(guildId, pic + msg);
1081
+ }
1082
+ // 推送直播信息
1083
+ // pic 存在,使用的是render模式
1084
+ if (pic) {
1085
+ // 只有在开播时才艾特全体成员
1086
+ if (liveType === LiveType.StartBroadcasting) {
1087
+ return await this.sendMsg(ctx, target, pic + (liveNotifyMsg ?? ''), true);
1128
1088
  }
1129
- // pic不存在,说明使用的是page模式
1130
- const msg = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), atAll && (0, jsx_runtime_1.jsx)("at", { type: "all" }), liveStartMsg && liveStartMsg] });
1131
- await this.sendMsg(guildId, msg);
1132
- };
1133
- }
1089
+ // 正常不需要艾特全体成员
1090
+ return await this.sendMsg(ctx, target, pic + (liveNotifyMsg ?? ''));
1091
+ }
1092
+ // pic不存在,说明使用的是page模式
1093
+ const msg = (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [koishi_1.h.image(buffer, 'image/png'), liveNotifyMsg || ''] });
1094
+ // 只有在开播时才艾特全体成员
1095
+ if (liveType === LiveType.StartBroadcasting) {
1096
+ return await this.sendMsg(ctx, target, msg, true);
1097
+ }
1098
+ // 正常不需要艾特全体成员
1099
+ return await this.sendMsg(ctx, target, msg);
1100
+ };
1134
1101
  // 定义获取主播信息方法
1135
1102
  let useMasterInfo;
1136
1103
  if (this.config.changeMasterInfoApi) {
@@ -1197,9 +1164,14 @@ class ComRegister {
1197
1164
  if (data.live_status === 1) { // 当前正在直播
1198
1165
  // 设置开播时间
1199
1166
  liveTime = data.live_time;
1167
+ // 设置直播中消息
1168
+ const liveMsg = this.config.customLive ? this.config.customLive
1169
+ .replace('-name', username)
1170
+ .replace('-time', await ctx.gi.getTimeDifference(liveTime))
1171
+ .replace('-link', `https://live.bilibili.com/${data.short_id === 0 ? data.room_id : data.short_id}`) : null;
1200
1172
  // 发送直播通知卡片
1201
1173
  if (this.config.restartPush)
1202
- sendLiveNotifyCard(data, LiveType.LiveBroadcast);
1174
+ sendLiveNotifyCard(data, LiveType.LiveBroadcast, liveMsg);
1203
1175
  // 改变开播状态
1204
1176
  open = true;
1205
1177
  } // 未开播,直接返回
@@ -1215,11 +1187,13 @@ class ComRegister {
1215
1187
  // 下播了将定时器清零
1216
1188
  timer = 0;
1217
1189
  // 定义下播通知消息
1218
- const liveEndMsg = this.config.customLiveEnd
1190
+ const liveEndMsg = this.config.customLiveEnd ? this.config.customLiveEnd
1219
1191
  .replace('-name', username)
1220
- .replace('-time', await ctx.gi.getTimeDifference(liveTime));
1192
+ .replace('-time', await ctx.gi.getTimeDifference(liveTime)) : null;
1193
+ // 更改直播时长
1194
+ data.live_time = liveTime;
1221
1195
  // 发送@全体成员通知
1222
- await sendLiveNotifyCard(data, LiveType.StartBroadcasting, liveEndMsg);
1196
+ await sendLiveNotifyCard(data, LiveType.StopBroadcast, liveEndMsg);
1223
1197
  }
1224
1198
  // 未进循环,还未开播,继续循环
1225
1199
  break;
@@ -1248,20 +1222,13 @@ class ComRegister {
1248
1222
  }
1249
1223
  }
1250
1224
  }
1251
- // 定义开播通知语
1252
- const liveStartMsg = this.config.customLiveStart
1225
+ // 定义开播通知语
1226
+ const liveStartMsg = this.config.customLiveStart ? this.config.customLiveStart
1253
1227
  .replace('-name', username)
1254
1228
  .replace('-time', await ctx.gi.getTimeDifference(liveTime))
1255
- .replace('-link', `https://live.bilibili.com/${data.short_id === 0 ? data.room_id : data.short_id}`);
1256
- // 判断是否需要@全体成员
1257
- if (this.config.liveStartAtAll) {
1258
- // 发送@全体成员通知
1259
- await sendLiveNotifyCard(data, LiveType.StartBroadcasting, liveStartMsg, true);
1260
- }
1261
- else {
1262
- // 发送直播通知卡片
1263
- await sendLiveNotifyCard(data, LiveType.StartBroadcasting, liveStartMsg);
1264
- }
1229
+ .replace('-link', `https://live.bilibili.com/${data.short_id === 0 ? data.room_id : data.short_id}`) : null;
1230
+ // 发送消息
1231
+ await sendLiveNotifyCard(data, LiveType.StartBroadcasting, liveStartMsg);
1265
1232
  }
1266
1233
  else { // 还在直播
1267
1234
  if (this.config.pushTime > 0) {
@@ -1270,8 +1237,13 @@ class ComRegister {
1270
1237
  if (timer >= (6 * 60 * this.config.pushTime)) { // 到时间推送直播消息
1271
1238
  // 到时间重新计时
1272
1239
  timer = 0;
1240
+ // 定义直播中通知消息
1241
+ const liveMsg = this.config.customLive ? this.config.customLive
1242
+ .replace('-name', username)
1243
+ .replace('-time', await ctx.gi.getTimeDifference(liveTime))
1244
+ .replace('-link', `https://live.bilibili.com/${data.short_id === 0 ? data.room_id : data.short_id}`) : null;
1273
1245
  // 发送直播通知卡片
1274
- sendLiveNotifyCard(data, LiveType.LiveBroadcast);
1246
+ sendLiveNotifyCard(data, LiveType.LiveBroadcast, liveMsg);
1275
1247
  }
1276
1248
  }
1277
1249
  // 否则继续循环
@@ -1293,10 +1265,10 @@ class ComRegister {
1293
1265
  });
1294
1266
  return table ? table : '没有订阅任何UP';
1295
1267
  }
1296
- async checkIfNeedSub(liveSub, dynamicSub, session, data) {
1268
+ async checkIfNeedSub(liveSub, dynamicSub, session, liveRoomData) {
1297
1269
  // 定义方法:用户直播间是否存在
1298
1270
  const liveRoom = async () => {
1299
- if (!data.live_room) {
1271
+ if (!liveRoomData) {
1300
1272
  // 未开通直播间
1301
1273
  await session.send('该用户未开通直播间,无法订阅直播');
1302
1274
  // 返回false
@@ -1357,7 +1329,158 @@ class ComRegister {
1357
1329
  check();
1358
1330
  });
1359
1331
  }
1360
- async getSubFromDatabase(ctx) {
1332
+ async subUserInBili(ctx, mid) {
1333
+ // 获取关注分组信息
1334
+ const checkGroupIsReady = async () => {
1335
+ // 判断是否有数据
1336
+ if (this.loginDBData.dynamic_group_id === '' || this.loginDBData.dynamic_group_id === null) {
1337
+ // 没有数据,没有创建分组,尝试创建分组
1338
+ const createGroupData = await ctx.ba.createGroup("订阅");
1339
+ // 如果分组已创建,则获取分组id
1340
+ if (createGroupData.code === 22106) {
1341
+ // 分组已存在,拿到之前的分组id
1342
+ const allGroupData = await ctx.ba.getAllGroup();
1343
+ // 遍历所有分组
1344
+ for (const group of allGroupData.data) {
1345
+ // 找到订阅分组
1346
+ if (group.name === '订阅') {
1347
+ // 拿到分组id
1348
+ this.loginDBData.dynamic_group_id = group.tagid;
1349
+ // 结束循环
1350
+ break;
1351
+ }
1352
+ }
1353
+ }
1354
+ else if (createGroupData.code !== 0) {
1355
+ console.log(createGroupData);
1356
+ // 创建分组失败
1357
+ return false;
1358
+ }
1359
+ // 创建成功,保存到数据库
1360
+ ctx.database.set('loginBili', 1, { dynamic_group_id: this.loginDBData.dynamic_group_id });
1361
+ // 创建成功
1362
+ return true;
1363
+ }
1364
+ return true;
1365
+ };
1366
+ // 判断分组是否准备好
1367
+ const flag = await checkGroupIsReady();
1368
+ // 判断是否创建成功
1369
+ if (!flag) {
1370
+ // 创建分组失败
1371
+ return { flag: false, msg: '创建分组失败,请尝试重启插件' };
1372
+ }
1373
+ // 获取分组明细
1374
+ const relationGroupDetailData = await ctx.ba.getRelationGroupDetail(this.loginDBData.dynamic_group_id);
1375
+ // 判断分组信息是否获取成功
1376
+ if (relationGroupDetailData.code !== 0) {
1377
+ if (relationGroupDetailData.code === 22104) {
1378
+ // 将原先的分组id置空
1379
+ this.loginDBData.dynamic_group_id = null;
1380
+ // 分组不存在
1381
+ const flag = await checkGroupIsReady();
1382
+ // 判断是否创建成功
1383
+ if (!flag) {
1384
+ // 创建分组失败
1385
+ return { flag: false, msg: '创建分组失败,请尝试重启插件' };
1386
+ }
1387
+ return { flag: true, msg: '分组不存在,已重新创建分组' };
1388
+ }
1389
+ // 获取分组明细失败
1390
+ return { flag: false, msg: '获取分组明细失败' };
1391
+ }
1392
+ relationGroupDetailData.data.forEach(user => {
1393
+ if (user.mid === mid) {
1394
+ // 已关注订阅对象
1395
+ return { flag: true, msg: '订阅对象已存在于分组中' };
1396
+ }
1397
+ });
1398
+ // 订阅对象
1399
+ const subUserData = await ctx.ba.follow(mid);
1400
+ // 判断是否订阅成功
1401
+ switch (subUserData.code) {
1402
+ case -101: return { flag: false, msg: '账号未登录,请使用指令bili login登录后再进行订阅操作' };
1403
+ case -102: return { flag: false, msg: '账号被封停,无法进行订阅操作' };
1404
+ case 22002: return { flag: false, msg: '因对方隐私设置,无法进行订阅操作' };
1405
+ case 22003: return { flag: false, msg: '你已将对方拉黑,无法进行订阅操作' };
1406
+ case 22013: return { flag: false, msg: '账号已注销,无法进行订阅操作' };
1407
+ case 40061: return { flag: false, msg: '账号不存在,请检查uid输入是否正确或用户是否存在' };
1408
+ case 22001: break; // 订阅对象为自己 无需添加到分组
1409
+ case 22014: // 已关注订阅对象 无需再次关注
1410
+ case 0: { // 执行订阅成功
1411
+ // 把订阅对象添加到分组中
1412
+ const copyUserToGroupData = await ctx.ba.copyUserToGroup(mid, this.loginDBData.dynamic_group_id);
1413
+ // 判断是否添加成功
1414
+ if (copyUserToGroupData.code !== 0) {
1415
+ // 添加失败
1416
+ return { flag: false, msg: '添加订阅对象到分组失败,请稍后重试' };
1417
+ }
1418
+ }
1419
+ }
1420
+ // 订阅成功
1421
+ return { flag: true, msg: '用户订阅成功' };
1422
+ }
1423
+ async loadSubFromConfig(ctx, subs) {
1424
+ for (const sub of subs) {
1425
+ // 定义Data
1426
+ let data;
1427
+ // 定义直播销毁函数
1428
+ let liveDispose;
1429
+ // 判断是否需要订阅直播
1430
+ if (sub.live) {
1431
+ // 获取用户信息
1432
+ let content;
1433
+ // 设置重试次数
1434
+ const attempts = 3;
1435
+ for (let i = 0; i < attempts; i++) {
1436
+ try {
1437
+ // 获取用户信息
1438
+ content = await ctx.ba.getUserInfo(sub.uid);
1439
+ // 成功则跳出循环
1440
+ break;
1441
+ }
1442
+ catch (e) {
1443
+ this.logger.error('getSubFromDatabase() getUserInfo() 发生了错误,错误为:' + e.message);
1444
+ if (i === attempts - 1) { // 已尝试三次
1445
+ // 发送私聊消息并重启服务
1446
+ return await this.sendPrivateMsgAndStopService(ctx);
1447
+ }
1448
+ }
1449
+ }
1450
+ // 获取data
1451
+ data = content.data;
1452
+ // 检查roomid是否存在
1453
+ if (!data.live_room) {
1454
+ // 用户没有开通直播间,无法订阅直播
1455
+ sub.live = false;
1456
+ // 发送提示
1457
+ this.logger.warn(`UID:${sub.uid} 用户没有开通直播间,无法订阅直播!`);
1458
+ }
1459
+ // 判断是否订阅直播
1460
+ if (sub.live) {
1461
+ // 订阅直播
1462
+ liveDispose = ctx.setInterval(this.liveDetect(ctx, data.live_room.roomid, sub.target), this.config.liveLoopTime * 1000);
1463
+ }
1464
+ }
1465
+ // 在B站中订阅该对象
1466
+ const subInfo = await this.subUserInBili(ctx, sub.uid);
1467
+ // 判断订阅是否成功
1468
+ if (!subInfo.flag)
1469
+ this.logger.warn(subInfo.msg);
1470
+ // 将该订阅添加到sm中
1471
+ this.subManager.push({
1472
+ id: +sub.uid,
1473
+ uid: sub.uid,
1474
+ roomId: sub.live ? data.live_room.roomid : '',
1475
+ target: sub.target,
1476
+ platform: '',
1477
+ live: sub.live,
1478
+ dynamic: sub.dynamic,
1479
+ liveDispose
1480
+ });
1481
+ }
1482
+ }
1483
+ async loadSubFromDatabase(ctx) {
1361
1484
  // 判断登录信息是否已加载完毕
1362
1485
  await this.checkIfLoginInfoIsLoaded(ctx);
1363
1486
  // 如果未登录,则直接返回
@@ -1384,8 +1507,21 @@ class ComRegister {
1384
1507
  // 跳过下面的步骤
1385
1508
  continue;
1386
1509
  }
1510
+ // 判断用户是否在B站中订阅了
1511
+ const subUserData = await this.subUserInBili(ctx, sub.uid);
1512
+ // 判断是否订阅
1513
+ if (!subUserData.flag) {
1514
+ // log
1515
+ this.logger.warn(`UID:${sub.uid} ${subUserData.msg},自动取消订阅`);
1516
+ // 发送私聊消息
1517
+ await this.sendPrivateMsg(`UID:${sub.uid} ${subUserData.msg},自动取消订阅`);
1518
+ // 删除该条数据
1519
+ await ctx.database.remove('bilibili', { id: sub.id });
1520
+ // 跳过下面的步骤
1521
+ continue;
1522
+ }
1387
1523
  // 获取推送目标数组
1388
- const targetIdArr = sub.targetId.split(' ');
1524
+ const target = JSON.parse(sub.target);
1389
1525
  /* 判断数据库是否被篡改 */
1390
1526
  // 获取用户信息
1391
1527
  let content;
@@ -1435,13 +1571,14 @@ class ComRegister {
1435
1571
  }
1436
1572
  }
1437
1573
  // 检测房间号是否被篡改
1438
- if (sub.live && (!data.live_room || data.live_room.roomid.toString() !== sub.room_id)) {
1574
+ if (sub.live && (!data.live_room || data.live_room.roomid != sub.room_id)) {
1439
1575
  // 房间号被篡改,删除该订阅
1440
1576
  await deleteSub();
1441
1577
  // log
1442
1578
  this.logger.info(`UID:${sub.uid} 房间号被篡改,自动取消订阅`);
1443
1579
  // Send msg
1444
1580
  await this.sendPrivateMsg(`UID:${sub.uid} 房间号被篡改,自动取消订阅`);
1581
+ // 直接返回
1445
1582
  return;
1446
1583
  }
1447
1584
  // 构建订阅对象
@@ -1449,27 +1586,28 @@ class ComRegister {
1449
1586
  id: sub.id,
1450
1587
  uid: sub.uid,
1451
1588
  roomId: sub.room_id,
1452
- targetIdArr,
1589
+ target,
1453
1590
  platform: sub.platform,
1454
- live: +sub.live === 1 ? true : false,
1455
- dynamic: +sub.dynamic === 1 ? true : false,
1591
+ live: sub.live === 1 ? true : false,
1592
+ dynamic: sub.dynamic === 1 ? true : false,
1456
1593
  liveDispose: null
1457
1594
  };
1458
1595
  // 判断是否订阅直播
1459
1596
  if (sub.live) {
1460
1597
  // 判断订阅直播数是否超过限制
1461
1598
  if (!this.config.unlockSubLimits && liveSubNum >= 3) {
1599
+ // 将live改为false
1462
1600
  subManagerItem.live = false;
1463
1601
  // log
1464
1602
  this.logger.warn(`UID:${sub.uid} 订阅直播数超过限制,自动取消订阅`);
1465
1603
  // 发送错误消息
1466
- this.sendPrivateMsg(`UID:${sub.uid} 订阅直播数超过限制,自动取消订阅`);
1604
+ await this.sendPrivateMsg(`UID:${sub.uid} 订阅直播数超过限制,自动取消订阅`);
1467
1605
  }
1468
1606
  else {
1469
1607
  // 直播订阅数+1
1470
1608
  liveSubNum++;
1471
1609
  // 订阅直播,开始循环检测
1472
- const dispose = ctx.setInterval(this.liveDetect(ctx, sub.room_id, targetIdArr), this.config.liveLoopTime * 1000);
1610
+ const dispose = ctx.setInterval(this.liveDetect(ctx, sub.room_id, target), this.config.liveLoopTime * 1000);
1473
1611
  // 保存销毁函数
1474
1612
  subManagerItem.liveDispose = dispose;
1475
1613
  }
@@ -1477,18 +1615,20 @@ class ComRegister {
1477
1615
  // 保存新订阅对象
1478
1616
  this.subManager.push(subManagerItem);
1479
1617
  }
1618
+ }
1619
+ checkIfDynamicDetectIsNeeded(ctx) {
1480
1620
  // 检查是否有订阅对象需要动态监测
1481
- if (this.subManager.some(sub => sub.dynamic)) {
1482
- // 开始动态监测
1483
- if (this.config.dynamicDebugMode) {
1484
- this.dynamicDispose = ctx.setInterval(this.debug_dynamicDetect(ctx), 10000);
1485
- }
1486
- else {
1487
- this.dynamicDispose = ctx.setInterval(this.dynamicDetect(ctx), 10000 /* this.config.dynamicLoopTime * 1000 */);
1488
- }
1621
+ if (this.subManager.some(sub => sub.dynamic))
1622
+ this.enableDynamicDetect(ctx);
1623
+ }
1624
+ enableDynamicDetect(ctx) {
1625
+ // 开始动态监测
1626
+ if (this.config.dynamicDebugMode) {
1627
+ this.dynamicDispose = ctx.setInterval(this.debug_dynamicDetect(ctx), this.config.dynamicLoopTime * 1000);
1628
+ }
1629
+ else {
1630
+ this.dynamicDispose = ctx.setInterval(this.dynamicDetect(ctx), this.config.dynamicLoopTime * 1000);
1489
1631
  }
1490
- // 在控制台中显示订阅对象
1491
- this.updateSubNotifier(ctx);
1492
1632
  }
1493
1633
  unsubSingle(ctx, id /* UID或RoomId */, type /* 0取消Live订阅,1取消Dynamic订阅 */) {
1494
1634
  // 定义返回消息
@@ -1504,7 +1644,7 @@ class ComRegister {
1504
1644
  // num--
1505
1645
  this.num--;
1506
1646
  // 判断是否还存在订阅了动态的对象,不存在则停止动态监测
1507
- this.checkIfUserThatIsSubDynAndUnsub();
1647
+ this.checkIfUserIsTheLastOneWhoSubDyn();
1508
1648
  };
1509
1649
  try {
1510
1650
  switch (type) {
@@ -1549,7 +1689,7 @@ class ComRegister {
1549
1689
  // 取消订阅
1550
1690
  this.subManager[index].dynamic = false;
1551
1691
  // 判断是否还存在订阅了动态的对象,不存在则停止动态监测
1552
- this.checkIfUserThatIsSubDynAndUnsub();
1692
+ this.checkIfUserIsTheLastOneWhoSubDyn();
1553
1693
  // 如果没有对这个UP的任何订阅,则移除
1554
1694
  if (checkIfNoSubExist(sub)) {
1555
1695
  // 从管理对象中移除
@@ -1570,8 +1710,8 @@ class ComRegister {
1570
1710
  this.updateSubNotifier(ctx);
1571
1711
  }
1572
1712
  }
1573
- checkIfUserThatIsSubDynAndUnsub() {
1574
- if (this.subManager.some(sub => sub.dynamic)) {
1713
+ checkIfUserIsTheLastOneWhoSubDyn() {
1714
+ if (this.dynamicDispose && !this.subManager.some(sub => sub.dynamic)) {
1575
1715
  // 停止动态监测
1576
1716
  this.dynamicDispose();
1577
1717
  this.dynamicDispose = null;
@@ -1583,7 +1723,7 @@ class ComRegister {
1583
1723
  if (sub.live)
1584
1724
  await this.subManager[i].liveDispose();
1585
1725
  // 判断是否还存在订阅了动态的对象,不存在则停止动态监测
1586
- this.checkIfUserThatIsSubDynAndUnsub();
1726
+ this.checkIfUserIsTheLastOneWhoSubDyn();
1587
1727
  // 从数据库中删除订阅
1588
1728
  await ctx.database.remove('bilibili', { uid: this.subManager[i].uid });
1589
1729
  // 将该订阅对象从订阅管理对象中移除
@@ -1608,21 +1748,34 @@ class ComRegister {
1608
1748
  }
1609
1749
  (function (ComRegister) {
1610
1750
  ComRegister.Config = koishi_1.Schema.object({
1611
- platform: koishi_1.Schema.string(),
1751
+ sub: koishi_1.Schema.array(koishi_1.Schema.object({
1752
+ uid: koishi_1.Schema.string().description('订阅用户UID'),
1753
+ dynamic: koishi_1.Schema.boolean().description('是否订阅用户动态'),
1754
+ live: koishi_1.Schema.boolean().description('是否订阅用户直播'),
1755
+ target: koishi_1.Schema.array(koishi_1.Schema.object({
1756
+ channelIdArr: koishi_1.Schema.array(koishi_1.Schema.object({
1757
+ channelId: koishi_1.Schema.string().description('频道/群组号'),
1758
+ dynamic: koishi_1.Schema.boolean().description('该频道/群组是否推送动态信息'),
1759
+ live: koishi_1.Schema.boolean().description('该频道/群组是否推送直播通知'),
1760
+ atAll: koishi_1.Schema.boolean().description('推送开播通知时是否艾特全体成员')
1761
+ })).description('频道/群组信息'),
1762
+ platform: koishi_1.Schema.string().description('推送平台')
1763
+ })).description('订阅用户需要发送的频道/群组信息')
1764
+ })).role('table').description('手动输入订阅信息,方便自定义订阅内容,这里的订阅内容不会存入数据库。uid: 订阅用户UID,dynamic: 是否需要订阅动态,live: 是否需要订阅直播'),
1612
1765
  master: koishi_1.Schema.object({
1613
1766
  enable: koishi_1.Schema.boolean(),
1767
+ platform: koishi_1.Schema.string(),
1614
1768
  masterAccount: koishi_1.Schema.string(),
1615
1769
  masterAccountGuildId: koishi_1.Schema.string()
1616
1770
  }),
1617
1771
  unlockSubLimits: koishi_1.Schema.boolean().required(),
1618
1772
  automaticResend: koishi_1.Schema.boolean().required(),
1619
1773
  changeMasterInfoApi: koishi_1.Schema.boolean().required(),
1620
- liveStartAtAll: koishi_1.Schema.boolean().required(),
1621
1774
  restartPush: koishi_1.Schema.boolean().required(),
1622
- pushUrl: koishi_1.Schema.boolean().required(),
1623
1775
  pushTime: koishi_1.Schema.number().required(),
1624
1776
  liveLoopTime: koishi_1.Schema.number().default(10),
1625
1777
  customLiveStart: koishi_1.Schema.string().required(),
1778
+ customLive: koishi_1.Schema.string(),
1626
1779
  customLiveEnd: koishi_1.Schema.string().required(),
1627
1780
  dynamicUrl: koishi_1.Schema.boolean().required(),
1628
1781
  dynamicLoopTime: koishi_1.Schema.number().default(60),