yz-yuki-plugin 2.0.7-17 → 2.0.7-19

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/CHANGELOG.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # 2.0.7
2
+ * 新增动态内容过多时转发发送功能,默认开启
2
3
  * 优化动态检查日志
3
4
  * 优化文字动态内容及排版,修复微博视频图片混合的动态显示缺失问题
4
5
  * 优化获取B站文章动态内容
@@ -60,6 +60,9 @@ banWords:
60
60
  # 设置B站动态消息模式 0 文字模式 1 图片模式
61
61
  pushMsgMode: 1
62
62
 
63
+ # 动态过长(>300字)或条数过多(>2条)时是否以转发形式发送动态,默认 1 开启,0 关闭。
64
+ forwardSendDynamic: 1
65
+
63
66
  # 文字模式时,文字消息与图片附件是否合并在一起发送,默认 1 合并,0 不合并。
64
67
  # 如果合并时图片过多导致发送失败,可设置为 0 单独发送图片。
65
68
  mergeTextPic: 1
@@ -55,6 +55,9 @@ banWords:
55
55
  # 设置微博动态消息模式 0 文字模式 1 图片模式
56
56
  pushMsgMode: 1
57
57
 
58
+ # 动态过长(>300字)或条数过多(>2条)时是否以转发形式发送动态,默认 1 开启,0 关闭。
59
+ forwardSendDynamic: 1
60
+
58
61
  # 文字模式时,文字消息与图片附件是否合并在一起发送,默认 1 合并,0 不合并。
59
62
  # 如果合并时图片过多导致发送失败,可设置为 0 单独发送图片。
60
63
  mergeTextPic: 1
@@ -380,65 +380,119 @@ class BiliTask {
380
380
  async sendDynamicMessage(messageMap, biliConfigData) {
381
381
  let liveAtAll = !!biliConfigData.liveAtAll === true ? true : false; // 直播动态是否@全体成员,默认false
382
382
  let liveAtAllCD = biliConfigData.liveAtAllCD || 1800; // 直播动态@全体成员 冷却时间CD,默认 30 分钟
383
+ let forwardSendDynamic = biliConfigData.forwardSendDynamic === 0 || biliConfigData.forwardSendDynamic === false ? false : true; // 转发动态是否合并发送,默认 true
383
384
  // 直播动态@全体成员的群组/好友列表,默认空数组,为空则不进行@全体成员操作
384
385
  let liveAtAllGroupList = new Set(Array.isArray(biliConfigData?.liveAtAllGroupList) ? Array.from(biliConfigData.liveAtAllGroupList).map(item => String(item)) : []);
385
386
  const LogMark = new Set(); // 日志mark
386
387
  for (const [chatType, botMap] of messageMap) {
387
388
  for (const [bot_id, chatMap] of botMap) {
388
389
  for (const [chatId, messageCombinationList] of chatMap) {
389
- // 遍历组合消息
390
- for (const messageCombination of messageCombinationList) {
391
- const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
392
- let sended = null;
393
- let markKey = '';
394
- if (chatType === 'group') {
395
- markKey = this.groupKey;
396
- sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
397
- }
398
- else if (chatType === 'private') {
399
- markKey = this.privateKey;
400
- sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
390
+ // 区分群聊和私聊
391
+ let markKey = chatType === 'group' ? this.groupKey : this.privateKey;
392
+ if (!LogMark.has('1')) {
393
+ global?.logger?.mark('优纪插件: B站动态执行推送');
394
+ LogMark.add('1');
395
+ }
396
+ let liveAtAllMark = await Redis.get(`${markKey}${chatId}:liveAtAllMark`); // 直播动态@全体成员标记,默认 0
397
+ const hasLiveDynamic = messageCombinationList.some(m => m.dynamicType === 'DYNAMIC_TYPE_LIVE_RCMD');
398
+ // 如果开启了直播动态@全体成员
399
+ if (liveAtAll && !liveAtAllMark && hasLiveDynamic && liveAtAllGroupList.has(String(chatId))) {
400
+ try {
401
+ await this.sendMsgApi(chatId, bot_id, chatType, [Segment.at('all')]);
402
+ await Redis.set(`${markKey}${chatId}:liveAtAllMark`, 1, { EX: liveAtAllCD }); // 设置直播动态@全体成员标记为 1
401
403
  }
402
- const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
403
- if (sended) {
404
- continue; // 如果已经发送过,则直接跳过
404
+ catch (error) {
405
+ logger.error(`直播动态发送@全体成员失败,请检查 <机器人> 是否有 [管理员权限] 或 [聊天平台是否支持] :${error}`);
406
+ let liveAtAllErrMsg = !!biliConfigData.liveAtAllErrMsg === false ? false : true; // 直播动态@全体成员失败是否发送错误提示消息,默认 false
407
+ if (liveAtAllErrMsg) {
408
+ await this.sendMsgApi(chatId, bot_id, chatType, ['直播动态发送@全体成员失败,请检查权限或平台是否支持']);
409
+ }
405
410
  }
406
- if (!LogMark.has('1')) {
407
- global?.logger?.mark('优纪插件: B站动态执行推送');
408
- LogMark.add('1');
411
+ }
412
+ // 统计图片数量和文字长度
413
+ let imageCount = 0;
414
+ let textLength = 0;
415
+ for (const messageCombination of messageCombinationList) {
416
+ const { messages } = messageCombination;
417
+ for (const msg of messages) {
418
+ if (typeof msg === 'object' && msg.type === 'image') {
419
+ imageCount++;
420
+ }
421
+ else if (typeof msg === 'string') {
422
+ textLength += msg.length;
423
+ }
409
424
  }
410
- let liveAtAllMark = await Redis.get(`${markKey}${chatId}:liveAtAllMark`); // 直播动态@全体成员标记,默认 0
411
- // 如果开启了直播动态@全体成员
412
- if (liveAtAll && !liveAtAllMark && dynamicType === 'DYNAMIC_TYPE_LIVE_RCMD' && liveAtAllGroupList.has(String(chatId))) {
413
- try {
414
- await this.sendMessageApi(chatId, bot_id, chatType, [Segment.at('all')]);
415
- await Redis.set(`${markKey}${chatId}:liveAtAllMark`, 1, { EX: liveAtAllCD }); // 设置直播动态@全体成员标记为 1
425
+ }
426
+ // 满足条件才使用合并转发
427
+ const useForward = imageCount > 2 || textLength > 300;
428
+ if (forwardSendDynamic && useForward) {
429
+ const forwardNodes = [];
430
+ // 合并所有消息
431
+ const forwardSendMardKeyList = [];
432
+ forwardNodes.push({
433
+ name: '优纪酱通知',
434
+ uin: String(80000000),
435
+ message: ['有新的B站动态了~'],
436
+ time: Date.now()
437
+ });
438
+ for (const messageCombination of messageCombinationList) {
439
+ const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
440
+ const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
441
+ // 原子性设置标记,防止并发重复
442
+ const setResult = await Redis.set(sendMarkKey, '1', { NX: true, EX: 3600 * 72 });
443
+ if (!setResult) {
444
+ continue; // 已有标记,跳过
416
445
  }
417
- catch (error) {
418
- logger.error(`直播动态发送@全体成员失败,请检查 <机器人> 是否有 [管理员权限] 或 [聊天平台是否支持] :${error}`);
419
- let liveAtAllErrMsg = !!biliConfigData.liveAtAllErrMsg === false ? false : true; // 直播动态@全体成员失败是否发送错误提示消息,默认 false
420
- if (liveAtAllErrMsg) {
421
- await this.sendMessageApi(chatId, bot_id, chatType, ['直播动态发送@全体成员失败,请检查权限或平台是否支持']);
422
- }
446
+ forwardSendMardKeyList.push(sendMarkKey); // 收集合并转发的标记键
447
+ // 每条动态一个 node
448
+ forwardNodes.push({
449
+ name: '匿名消息',
450
+ uin: String(80000000),
451
+ message: messages,
452
+ time: Date.now()
453
+ });
454
+ }
455
+ // 尝试合并转发动态
456
+ if ((await this.sendMsgApi(chatId, bot_id, chatType, '优纪酱B站动态通知~')) &&
457
+ (await this.sendForwardMsgApi(chatId, bot_id, chatType, forwardNodes))) {
458
+ await this.randomDelay(1000, 2000);
459
+ continue; // 合并转发成功,跳过后续单条发送逻辑
460
+ }
461
+ else {
462
+ for (const sendMarkKey of forwardSendMardKeyList) {
463
+ await Redis.del(sendMarkKey); // 发送消息失败,删除合并转发成功标记
423
464
  }
424
465
  }
466
+ }
467
+ // 合并转发失败,回退为原有方式
468
+ for (const messageCombination of messageCombinationList) {
469
+ const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
470
+ const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
471
+ // 原子性设置标记,防止并发重复
472
+ const setResult = await Redis.set(sendMarkKey, '1', { NX: true, EX: 3600 * 72 });
473
+ if (!setResult) {
474
+ continue; // 已有标记,跳过
475
+ }
476
+ let sendSuccess = true;
425
477
  if (sendMode === 'SINGLE') {
426
- let allSent = true;
427
478
  for (let i = 0; i < messages.length; i++) {
428
- if (!(await this.sendMessageApi(chatId, bot_id, chatType, messages[i]))) {
429
- allSent = false;
430
- break; // 如果有任何一条消息发送失败,停止发送后续消息
479
+ if (!(await this.sendMsgApi(chatId, bot_id, chatType, messages[i]))) {
480
+ sendSuccess = false;
481
+ break;
431
482
  }
432
483
  }
433
- if (allSent) {
434
- await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
435
- await this.randomDelay(1000, 2000); // 随机延时1-2秒
484
+ if (!sendSuccess) {
485
+ await Redis.del(sendMarkKey); // 失败删除标记
486
+ }
487
+ else {
488
+ await this.randomDelay(1000, 2000);
436
489
  }
437
490
  }
438
491
  else if (sendMode === 'MERGE') {
439
- if (await this.sendMessageApi(chatId, bot_id, chatType, messages)) {
440
- await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
492
+ if (!(await this.sendMsgApi(chatId, bot_id, chatType, messages))) {
493
+ await Redis.del(sendMarkKey); // 失败删除标记
441
494
  }
495
+ await this.randomDelay(1000, 2000);
442
496
  }
443
497
  }
444
498
  }
@@ -453,7 +507,7 @@ class BiliTask {
453
507
  * @param chatType 聊天类型
454
508
  * @param message 消息内容
455
509
  */
456
- async sendMessageApi(chatId, bot_id, chatType, message) {
510
+ async sendMsgApi(chatId, bot_id, chatType, message) {
457
511
  try {
458
512
  if (chatType === 'group') {
459
513
  await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message); // 发送群聊
@@ -468,6 +522,30 @@ class BiliTask {
468
522
  return false; // 发送失败
469
523
  }
470
524
  }
525
+ /**
526
+ * 发送合并转发消息
527
+ * @param chatId 聊天 ID
528
+ * @param bot_id 机器人 ID
529
+ * @param chatType 聊天类型
530
+ * @param message 消息内容
531
+ * @returns 是否发送成功
532
+ */
533
+ async sendForwardMsgApi(chatId, bot_id, chatType, forwardNodes) {
534
+ const forwardMsg = await Bot.makeForwardMsg(forwardNodes);
535
+ try {
536
+ if (chatType === 'group') {
537
+ await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(forwardMsg); // 发送群聊合并转发
538
+ }
539
+ else if (chatType === 'private') {
540
+ await (Bot[bot_id] ?? Bot)?.pickFriend(String(chatId)).sendMsg(forwardMsg); // 发送好友私聊合并转发
541
+ }
542
+ return true; // 发送成功
543
+ }
544
+ catch (error) {
545
+ global?.logger?.error(`${chatType === 'group' ? '群聊' : '私聊'} ${chatId} 合并转发消息发送失败:${JSON.stringify(error)}`);
546
+ return false; // 发送失败
547
+ }
548
+ }
471
549
  /**
472
550
  * 随机延时
473
551
  * @param min 最小延时时间
@@ -344,48 +344,101 @@ class WeiboTask {
344
344
  * @param biliConfigData 微博配置数据
345
345
  */
346
346
  async sendDynamicMessage(messageMap, weiboConfigData) {
347
+ let forwardSendDynamic = weiboConfigData.forwardSendDynamic === 0 || weiboConfigData.forwardSendDynamic === false ? false : true; // 转发动态是否合并发送,默认 true
347
348
  const LogMark = new Set(); // 日志mark
348
349
  for (const [chatType, botMap] of messageMap) {
349
350
  for (const [bot_id, chatMap] of botMap) {
350
351
  for (const [chatId, messageCombinationList] of chatMap) {
351
- // 遍历组合消息
352
+ // 区分群聊和私聊
353
+ let markKey = chatType === 'group' ? this.groupKey : this.privateKey;
354
+ if (!LogMark.has('1')) {
355
+ global?.logger?.mark('优纪插件: B站动态执行推送');
356
+ LogMark.add('1');
357
+ }
358
+ // 统计图片数量和文字长度
359
+ let imageCount = 0;
360
+ let textLength = 0;
352
361
  for (const messageCombination of messageCombinationList) {
353
- const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
354
- let sended = null;
355
- let markKey = '';
356
- if (chatType === 'group') {
357
- markKey = this.groupKey;
358
- sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
362
+ const { messages } = messageCombination;
363
+ for (const msg of messages) {
364
+ if (typeof msg === 'object' && msg.type === 'image') {
365
+ imageCount++;
366
+ }
367
+ else if (typeof msg === 'string') {
368
+ textLength += msg.length;
369
+ }
359
370
  }
360
- else if (chatType === 'private') {
361
- markKey = this.privateKey;
362
- sended = await Redis.get(`${markKey}${chatId}:${dynamicUUid_str}`);
371
+ }
372
+ // 满足条件才使用合并转发
373
+ const useForward = imageCount > 2 || textLength > 300;
374
+ if (forwardSendDynamic && useForward) {
375
+ const forwardNodes = [];
376
+ // 合并所有消息
377
+ const forwardSendMardKeyList = [];
378
+ forwardNodes.push({
379
+ name: '优纪酱通知',
380
+ uin: String(80000000),
381
+ message: ['有新的微博动态了~'],
382
+ time: Date.now()
383
+ });
384
+ for (const messageCombination of messageCombinationList) {
385
+ const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
386
+ const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
387
+ // 原子性设置标记,防止并发重复
388
+ const setResult = await Redis.set(sendMarkKey, '1', { NX: true, EX: 3600 * 72 });
389
+ if (!setResult) {
390
+ continue; // 已有标记,跳过
391
+ }
392
+ forwardSendMardKeyList.push(sendMarkKey); // 收集合并转发的标记键
393
+ // 每条动态一个 node
394
+ forwardNodes.push({
395
+ name: '匿名消息',
396
+ uin: String(80000000),
397
+ message: messages,
398
+ time: Date.now()
399
+ });
363
400
  }
364
- const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
365
- if (sended) {
366
- continue; // 如果已经发送过,则直接跳过
401
+ // 尝试合并转发动态
402
+ if ((await this.sendMsgApi(chatId, bot_id, chatType, '优纪酱微博动态通知~')) &&
403
+ (await this.sendForwardMsgApi(chatId, bot_id, chatType, forwardNodes))) {
404
+ await this.randomDelay(1000, 2000);
405
+ continue; // 合并转发成功,跳过后续单条发送逻辑
406
+ }
407
+ else {
408
+ for (const sendMarkKey of forwardSendMardKeyList) {
409
+ await Redis.del(sendMarkKey); // 发送消息失败,删除合并转发成功标记
410
+ }
367
411
  }
368
- if (!LogMark.has('1')) {
369
- global?.logger?.mark('优纪插件: B站动态执行推送');
370
- LogMark.add('1');
412
+ }
413
+ // 合并转发失败,回退为原有方式
414
+ for (const messageCombination of messageCombinationList) {
415
+ const { sendMode, dynamicUUid_str, dynamicType, messages } = messageCombination;
416
+ const sendMarkKey = `${markKey}${chatId}:${dynamicUUid_str}`;
417
+ // 原子性设置标记,防止并发重复
418
+ const setResult = await Redis.set(sendMarkKey, '1', { NX: true, EX: 3600 * 72 });
419
+ if (!setResult) {
420
+ continue; // 已有标记,跳过
371
421
  }
422
+ let sendSuccess = true;
372
423
  if (sendMode === 'SINGLE') {
373
- let allSent = true;
374
424
  for (let i = 0; i < messages.length; i++) {
375
- if (!(await this.sendMessageApi(chatId, bot_id, chatType, messages[i]))) {
376
- allSent = false;
377
- break; // 如果有任何一条消息发送失败,停止发送后续消息
425
+ if (!(await this.sendMsgApi(chatId, bot_id, chatType, messages[i]))) {
426
+ sendSuccess = false;
427
+ break;
378
428
  }
379
429
  }
380
- if (allSent) {
381
- await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
382
- await this.randomDelay(1000, 2000); // 随机延时1-2秒
430
+ if (!sendSuccess) {
431
+ await Redis.del(sendMarkKey); // 失败删除标记
432
+ }
433
+ else {
434
+ await this.randomDelay(1000, 2000);
383
435
  }
384
436
  }
385
437
  else if (sendMode === 'MERGE') {
386
- if (await this.sendMessageApi(chatId, bot_id, chatType, messages)) {
387
- await Redis.set(sendMarkKey, '1', { EX: 3600 * 72 }); // 发送成功后设置标记
438
+ if (!(await this.sendMsgApi(chatId, bot_id, chatType, messages))) {
439
+ await Redis.del(sendMarkKey); // 失败删除标记
388
440
  }
441
+ await this.randomDelay(1000, 2000);
389
442
  }
390
443
  }
391
444
  }
@@ -400,7 +453,7 @@ class WeiboTask {
400
453
  * @param chatType 聊天类型
401
454
  * @param message 消息内容
402
455
  */
403
- async sendMessageApi(chatId, bot_id, chatType, message) {
456
+ async sendMsgApi(chatId, bot_id, chatType, message) {
404
457
  try {
405
458
  if (chatType === 'group') {
406
459
  await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(message); // 发送群聊
@@ -415,6 +468,30 @@ class WeiboTask {
415
468
  return false; // 发送失败
416
469
  }
417
470
  }
471
+ /**
472
+ * 发送合并转发消息
473
+ * @param chatId 聊天 ID
474
+ * @param bot_id 机器人 ID
475
+ * @param chatType 聊天类型
476
+ * @param message 消息内容
477
+ * @returns 是否发送成功
478
+ */
479
+ async sendForwardMsgApi(chatId, bot_id, chatType, forwardNodes) {
480
+ const forwardMsg = await Bot.makeForwardMsg(forwardNodes);
481
+ try {
482
+ if (chatType === 'group') {
483
+ await (Bot[bot_id] ?? Bot)?.pickGroup(String(chatId)).sendMsg(forwardMsg); // 发送群聊合并转发
484
+ }
485
+ else if (chatType === 'private') {
486
+ await (Bot[bot_id] ?? Bot)?.pickFriend(String(chatId)).sendMsg(forwardMsg); // 发送好友私聊合并转发
487
+ }
488
+ return true; // 发送成功
489
+ }
490
+ catch (error) {
491
+ global?.logger?.error(`${chatType === 'group' ? '群聊' : '私聊'} ${chatId} 合并转发消息发送失败:${JSON.stringify(error)}`);
492
+ return false; // 发送失败
493
+ }
494
+ }
418
495
  /**
419
496
  * 随机延时
420
497
  * @param min 最小延时时间
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yz-yuki-plugin",
3
- "version": "2.0.7-17",
3
+ "version": "2.0.7-19",
4
4
  "description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
5
5
  "author": "snowtafir",
6
6
  "type": "module",