diff --git a/.env.dev b/.env.dev index c6f0f166..c1e75e90 100644 --- a/.env.dev +++ b/.env.dev @@ -27,11 +27,6 @@ BILI_UID= BILI_SESSDATA= BILI_CSRF= -# B站直播与动态检查模式, 是否启用检查池模式 -# 单位时间内检查数量相同, 但总体检查时间间隔会根据订阅量延长, 对于订阅量较大的情况可以有效避免被B站风控 -# 启用HTTP代理时视情况关闭 -ENABLE_BILI_CHECK_POOL_MODE=true - # 全局HTTP代理配置 # 若ENABLE_FORCED_PROXY设置为False, 则当启用 HTTP 代理后当代理不可用时自动使用直连网络(默认) # 若ENABLE_FORCED_PROXY设置为True, 则当启用 HTTP 代理后当代理不可用时仍不会使用直连网络 diff --git a/README.md b/README.md index 1db98ee5..8fe39a6f 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ 基于 [Nonebot2](https://github.com/nonebot/nonebot2) 的 qq 机器人 -![GitHub](https://img.shields.io/github/license/Ailitonia/nonebot2_miya) +![GitHub](https://img.shields.io/github/license/Ailitonia/omega-miya) ![Python](https://img.shields.io/badge/Python-3.8+-blue) -![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/Ailitonia/nonebot2_miya?include_prereleases) -![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/Ailitonia/nonebot2_miya) +![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/Ailitonia/omega-miya?include_prereleases) +![GitHub (Pre-)Release Date](https://img.shields.io/github/release-date-pre/Ailitonia/omega-miya) @@ -56,7 +56,7 @@ ## 关于图片数据 如果你不想自己收集图片数据, 可以将 -[这组图片数据集](https://github.com/Ailitonia/nonebot2_miya/raw/main/archive_data/db_pixiv.7z) +[这组图片数据集](https://github.com/Ailitonia/omega-miya/raw/main/archive_data/db_pixiv.7z) 导入数据库 这个图片集大概有5万条左右, 基本都是按我自己口味收集的图片 @@ -75,7 +75,7 @@ SHA1: `7CFF3593A85979B5D966773F3857577CFCC2FFBD` **注意!该cookies等同于您账号控制权,请不要将这两个值泄露给他人!** - + **注意!该cookies等同于您账号控制权,请不要将这两个值泄露给他人!** diff --git a/bot.py b/bot.py index 8319e8fe..663b85d6 100644 --- a/bot.py +++ b/bot.py @@ -10,18 +10,35 @@ if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) +# File path +bot_root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'omega_miya')) +bot_tmp_path = os.path.abspath(os.path.join(bot_root_path, 'tmp')) +if not os.path.exists(bot_tmp_path): + os.makedirs(bot_tmp_path) + +bot_log_path = os.path.abspath(os.path.join(bot_root_path, 'log')) +if not os.path.exists(bot_log_path): + os.makedirs(bot_log_path) + # Custom logger log_info_name = f"{datetime.today().strftime('%Y%m%d-%H%M%S')}-INFO.log" log_error_name = f"{datetime.today().strftime('%Y%m%d-%H%M%S')}-ERROR.log" -log_file_path = os.path.join(os.path.dirname(__file__), 'omega_miya', 'log') -log_info_path = os.path.join(log_file_path, log_info_name) -log_error_path = os.path.join(log_file_path, log_error_name) +log_info_path = os.path.join(bot_log_path, log_info_name) +log_error_path = os.path.join(bot_log_path, log_error_name) logger.add(log_info_path, rotation="00:00", diagnose=False, level="INFO", format=default_format, encoding='utf-8') logger.add(log_error_path, rotation="00:00", diagnose=False, level="ERROR", format=default_format, encoding='utf-8') # You can pass some keyword args config to init function nonebot.init() + +# 初始化一些系统变量配置 +config = nonebot.get_driver().config +config.root_path_ = bot_root_path +config.tmp_path_ = bot_tmp_path + + +# 注册 cqhttp adapter driver = nonebot.get_driver() driver.register_adapter("cqhttp", CQHTTPBot) diff --git a/omega_miya/plugins/Omega_auth_manage/__init__.py b/omega_miya/plugins/Omega_auth_manage/__init__.py index e87a0b6d..674ad760 100644 --- a/omega_miya/plugins/Omega_auth_manage/__init__.py +++ b/omega_miya/plugins/Omega_auth_manage/__init__.py @@ -23,7 +23,7 @@ # 注册事件响应器 omegaauth = on_command('OmegaAuth', rule=to_me(), aliases={'omegaauth', 'oauth'}, - permission=SUPERUSER, priority=1, block=True) + permission=SUPERUSER, priority=10, block=True) # 修改默认参数处理 diff --git a/omega_miya/plugins/Omega_help/__init__.py b/omega_miya/plugins/Omega_help/__init__.py index 3bad5cff..1d47f799 100644 --- a/omega_miya/plugins/Omega_help/__init__.py +++ b/omega_miya/plugins/Omega_help/__init__.py @@ -39,7 +39,7 @@ level=10, auth_node='basic'), permission=GROUP, - priority=1, + priority=10, block=True) diff --git a/omega_miya/plugins/Omega_manage/__init__.py b/omega_miya/plugins/Omega_manage/__init__.py index 7ccdedf1..28057855 100644 --- a/omega_miya/plugins/Omega_manage/__init__.py +++ b/omega_miya/plugins/Omega_manage/__init__.py @@ -38,7 +38,7 @@ # 注册事件响应器 omega = on_command('Omega', rule=None, aliases={'omega'}, - permission=GROUP_ADMIN | GROUP_OWNER | SUPERUSER, priority=1, block=True) + permission=GROUP_ADMIN | GROUP_OWNER | SUPERUSER, priority=10, block=True) # 修改默认参数处理 diff --git a/omega_miya/plugins/announce/__init__.py b/omega_miya/plugins/announce/__init__.py index b4fecd11..6bd86597 100644 --- a/omega_miya/plugins/announce/__init__.py +++ b/omega_miya/plugins/announce/__init__.py @@ -9,7 +9,7 @@ # 注册事件响应器 -announce = on_command('公告', rule=to_me(), aliases={'announce'}, permission=SUPERUSER, priority=1, block=True) +announce = on_command('公告', rule=to_me(), aliases={'announce'}, permission=SUPERUSER, priority=10, block=True) # 修改默认参数处理 @@ -63,5 +63,5 @@ async def handle_announce(bot: Bot, event: Event, state: T_State): else: logger.warning(f'公告未发送, 不合规的群组类型或群号: {group}') await announce.finish('不合规的群组类型或群号') - logger.warning(f'公告已成功发送群组: {group}') + logger.info(f'公告已成功发送群组: {group}') await announce.finish('公告发送完成') diff --git a/omega_miya/plugins/bilibili_dynamic_monitor/config.py b/omega_miya/plugins/bilibili_dynamic_monitor/config.py new file mode 100644 index 00000000..4f8a9cae --- /dev/null +++ b/omega_miya/plugins/bilibili_dynamic_monitor/config.py @@ -0,0 +1,14 @@ +from pydantic import BaseSettings + + +class Config(BaseSettings): + + # plugin custom config + """ + B站动态检查模式, 是否启用检查池模式 + 单位时间内检查数量相同, 但总体检查时间间隔会根据订阅量延长, 对于订阅量较大的情况可以有效避免被B站风控 + """ + enable_dynamic_check_pool_mode: bool = True + + class Config: + extra = "ignore" diff --git a/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py b/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py index 3e93c152..1f366ce4 100644 --- a/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py +++ b/omega_miya/plugins/bilibili_dynamic_monitor/monitor.py @@ -4,7 +4,7 @@ from nonebot.adapters.cqhttp import MessageSegment from omega_miya.utils.Omega_Base import DBSubscription, DBDynamic, DBTable from .utils import get_user_dynamic_history, get_user_info, get_user_dynamic, get_dynamic_info, pic_2_base64 -from .utils import ENABLE_BILI_CHECK_POOL_MODE +from .utils import ENABLE_DYNAMIC_CHECK_POOL_MODE # 检查池模式使用的检查队列 @@ -137,15 +137,6 @@ async def check_dynamic(dy_uid): dynamic_info[num]['content'], dynamic_info[num]['url'], '=' * 16, origin_dynamic_info['name'], origin_dynamic_info['content'] ) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 原创的动态(有图片) elif dynamic_info[num]['type'] == 2: # 处理图片序列 @@ -157,101 +148,44 @@ async def check_dynamic(dy_uid): msg = '{}发布了新动态!\n\n“{}”\n{}\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['content'], dynamic_info[num]['url'], pic_segs) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 原创的动态(无图片) elif dynamic_info[num]['type'] == 4: msg = '{}发布了新动态!\n\n“{}”\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['content'], dynamic_info[num]['url']) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 视频 elif dynamic_info[num]['type'] == 8: msg = '{}发布了新的视频!\n\n《{}》\n“{}”\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['origin'], dynamic_info[num]['content'], dynamic_info[num]['url']) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 小视频 elif dynamic_info[num]['type'] == 16: msg = '{}发布了新的小视频动态!\n\n“{}”\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['content'], dynamic_info[num]['url']) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 番剧 elif dynamic_info[num]['type'] in [32, 512]: msg = '{}发布了新的番剧!\n\n《{}》\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['origin'], dynamic_info[num]['url']) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 文章 elif dynamic_info[num]['type'] == 64: msg = '{}发布了新的文章!\n\n《{}》\n“{}”\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['origin'], dynamic_info[num]['content'], dynamic_info[num]['url']) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # 音频 elif dynamic_info[num]['type'] == 256: msg = '{}发布了新的音乐!\n\n《{}》\n“{}”\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['origin'], dynamic_info[num]['content'], dynamic_info[num]['url']) - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']}") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " - f"error: {repr(_e)}") - continue # B站活动相关 elif dynamic_info[num]['type'] == 2048: msg = '{}发布了一条活动相关动态!\n\n【{}】\n“{}”\n{}'.format( dynamic_info[num]['name'], dynamic_info[num]['origin'], dynamic_info[num]['content'], dynamic_info[num]['url']) + else: + logger.warning(f"未知的动态类型: {dynamic_info[num]['type']}, id: {dynamic_info[num]['id']}") + msg = None + + # 向群组发送消息 + if msg: for group_id in notice_group: for _bot in bots: try: @@ -261,8 +195,7 @@ async def check_dynamic(dy_uid): logger.warning(f"向群组: {group_id} 发送新动态通知: {dynamic_info[num]['id']} 失败, " f"error: {repr(_e)}") continue - elif dynamic_info[num]['type'] == -1: - logger.warning(f"未知的动态类型: {dynamic_info[num]['id']}") + # 更新动态内容到数据库 dy_id = dynamic_info[num]['id'] dy_type = dynamic_info[num]['type'] @@ -278,7 +211,7 @@ async def check_dynamic(dy_uid): logger.error(f'bilibili_dynamic_monitor: 解析新动态: {dy_uid} 的时发生了错误, error info: {repr(_e)}') # 启用了检查池模式 - if ENABLE_BILI_CHECK_POOL_MODE: + if ENABLE_DYNAMIC_CHECK_POOL_MODE: global checking_pool # checking_pool为空则上一轮检查完了, 重新往里面放新一轮的uid @@ -288,7 +221,7 @@ async def check_dynamic(dy_uid): # 看下checking_pool里面还剩多少 waiting_num = len(checking_pool) - # 默认单次检查并发数为2, 默认检查间隔为30s + # 默认单次检查并发数为2, 默认检查间隔为20s logger.debug(f'bili dynamic pool mode debug info, B_checking_pool: {checking_pool}') if waiting_num >= 2: # 抽取检查对象 @@ -328,7 +261,7 @@ async def check_dynamic(dy_uid): # 分时间段创建计划任务, 夜间闲时降低检查频率 # 根据检查池模式初始化检查时间间隔 -if ENABLE_BILI_CHECK_POOL_MODE: +if ENABLE_DYNAMIC_CHECK_POOL_MODE: # 检查池启用 scheduler.add_job( bilibili_dynamic_monitor, @@ -340,7 +273,7 @@ async def check_dynamic(dy_uid): # day_of_week=None, # hour='9-23', # minute='*/3', - second='15-45/30', + second='*/20', # start_date=None, # end_date=None, # timezone=None, @@ -349,7 +282,7 @@ async def check_dynamic(dy_uid): misfire_grace_time=30 ) else: - # 检查池禁用, 日间 + # 检查池禁用 scheduler.add_job( bilibili_dynamic_monitor, 'cron', @@ -358,32 +291,13 @@ async def check_dynamic(dy_uid): # day='*/1', # week=None, # day_of_week=None, - hour='9-23', + # hour=None, minute='*/3', # second='*/30', # start_date=None, # end_date=None, # timezone=None, - id='bilibili_dynamic_monitor_in_day_pool_disable', - coalesce=True, - misfire_grace_time=30 - ) - # 检查池禁用, 夜间 - scheduler.add_job( - bilibili_dynamic_monitor, - 'cron', - # year=None, - # month=None, - # day='*/1', - # week=None, - # day_of_week=None, - hour='0-8', - minute='*/15', - # second='*/30', - # start_date=None, - # end_date=None, - # timezone=None, - id='bilibili_dynamic_monitor_in_night_pool_disable', + id='bilibili_dynamic_monitor_pool_disable', coalesce=True, misfire_grace_time=30 ) diff --git a/omega_miya/plugins/bilibili_dynamic_monitor/utils.py b/omega_miya/plugins/bilibili_dynamic_monitor/utils.py index 825f3313..04e4c002 100644 --- a/omega_miya/plugins/bilibili_dynamic_monitor/utils.py +++ b/omega_miya/plugins/bilibili_dynamic_monitor/utils.py @@ -2,6 +2,7 @@ import nonebot from omega_miya.utils.Omega_Base import DBTable, Result from omega_miya.utils.Omega_plugin_utils import HttpFetcher, PicEncoder +from .config import Config DYNAMIC_API_URL = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history' GET_DYNAMIC_DETAIL_API_URL = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail' @@ -9,10 +10,11 @@ DYNAMIC_URL = 'https://t.bilibili.com/' global_config = nonebot.get_driver().config +plugin_config = Config(**global_config.dict()) BILI_SESSDATA = global_config.bili_sessdata BILI_CSRF = global_config.bili_csrf BILI_UID = global_config.bili_uid -ENABLE_BILI_CHECK_POOL_MODE = global_config.enable_bili_check_pool_mode +ENABLE_DYNAMIC_CHECK_POOL_MODE = plugin_config.enable_dynamic_check_pool_mode def check_bili_cookies() -> Result: @@ -291,7 +293,7 @@ async def get_user_dynamic_history(dy_uid) -> Result: # 这是动态的链接 url = DYNAMIC_URL + str(cards['desc']['dynamic_id']) name = 'Unknown' - card_dic = dict({'id': dy_id, 'type': -1, 'url': url, + card_dic = dict({'id': dy_id, 'type': cards['desc']['type'], 'url': url, 'name': name, 'content': '', 'origin': ''}) _DYNAMIC_INFO[card_num] = card_dic return Result(error=False, info='Success', result=_DYNAMIC_INFO) @@ -356,5 +358,5 @@ async def get_dynamic_info(dynamic_id) -> Result: 'get_user_dynamic', 'get_user_dynamic_history', 'get_dynamic_info', - 'ENABLE_BILI_CHECK_POOL_MODE' + 'ENABLE_DYNAMIC_CHECK_POOL_MODE' ] diff --git a/omega_miya/plugins/bilibili_live_monitor/config.py b/omega_miya/plugins/bilibili_live_monitor/config.py new file mode 100644 index 00000000..0890ff47 --- /dev/null +++ b/omega_miya/plugins/bilibili_live_monitor/config.py @@ -0,0 +1,16 @@ +from pydantic import BaseSettings + + +class Config(BaseSettings): + + # plugin custom config + """ + b站直播间插件已使用新api获取直播间状态, 请求数大幅降低 + 请始终将enable_new_live_api保持为True! + 非调试请勿修改本配置!!! + """ + enable_new_live_api: bool = True + enable_live_check_pool_mode: bool = False + + class Config: + extra = "ignore" diff --git a/omega_miya/plugins/bilibili_live_monitor/monitor.py b/omega_miya/plugins/bilibili_live_monitor/monitor.py index 20edbe1d..ae026033 100644 --- a/omega_miya/plugins/bilibili_live_monitor/monitor.py +++ b/omega_miya/plugins/bilibili_live_monitor/monitor.py @@ -1,17 +1,20 @@ import asyncio import time import random +from typing import List from nonebot import logger, require, get_driver, get_bots +from nonebot.adapters import Bot from nonebot.adapters.cqhttp import MessageSegment from omega_miya.utils.Omega_Base import DBSubscription, DBHistory, DBTable -from .utils import get_live_info, get_user_info, pic_2_base64, verify_cookies -from .utils import ENABLE_BILI_CHECK_POOL_MODE +from .utils import get_live_info, get_live_info_by_uid_list, get_user_info, pic_2_base64, verify_cookies +from .utils import ENABLE_NEW_LIVE_API, ENABLE_LIVE_CHECK_POOL_MODE # 初始化直播间标题, 状态 live_title = {} live_status = {} live_up_name = {} +live_uid_by_rid = {} # 检查池模式使用的检查队列 checking_pool = [] @@ -21,6 +24,7 @@ async def init_live_info(): global live_title global live_status global live_up_name + global live_uid_by_rid # 定义函数用于初始化直播间信息 async def __init_live_info(room_id: int): @@ -46,6 +50,9 @@ async def __init_live_info(room_id: int): # 直播间标题放入live_title全局变量中 live_title[room_id] = str(live_info['title']) + # 直播间用户uid放入live_uid_by_rid全局变量中 + live_uid_by_rid[room_id] = int(live_info['uid']) + # 直播间up名称放入live_up_name全局变量中 live_up_name[room_id] = str(up_name) @@ -55,11 +62,6 @@ async def __init_live_info(room_id: int): logger.opt(colors=True).error(f'init_live_info: 初始化直播间 {room_id} 失败, error: {repr(_e)}') return - if ENABLE_BILI_CHECK_POOL_MODE: - logger.opt(colors=True).info('Bilibili 检查池模式: 已启用!') - else: - logger.opt(colors=True).info('Bilibili 检查池模式: 已禁用!') - _res = await verify_cookies() if _res.success(): logger.opt(colors=True).info(f'Bilibili 已登录! 当前用户: {_res.result}') @@ -85,6 +87,7 @@ async def init_add_live_info(room_id: int): global live_title global live_status global live_up_name + global live_uid_by_rid try: room_id = int(room_id) @@ -109,6 +112,9 @@ async def init_add_live_info(room_id: int): # 直播间标题放入live_title全局变量中 live_title[room_id] = str(live_info['title']) + # 直播间用户uid放入live_uid_by_rid全局变量中 + live_uid_by_rid[room_id] = int(live_info['uid']) + # 直播间up名称放入live_up_name全局变量中 live_up_name[room_id] = str(up_name) @@ -167,14 +173,144 @@ async def live_db_upgrade(): logger.debug('live_db_upgrade: upgrade subscription info completed') -# 创建直播检查函数 -async def bilibili_live_monitor(): - - logger.debug(f"bilibili_live_monitor: checking started") +# 直播间检查及消息发送函数 +async def live_status_sender(room_id: int, live_info: dict, bots: List[Bot], all_groups: List[int]): + """ + 检查直播间状态并向群组发送消息 + :param room_id: 直播间房间id + :param live_info: 由 get_live_info 或 get_live_info_by_uid_list 获取的直播间信息 + :param bots: bots 列表 + :param all_groups: 所有可能需要通知的群组列表 + """ global live_title global live_status global live_up_name + sub = DBSubscription(sub_type=1, sub_id=room_id) + + # 获取订阅了该直播间的所有群 + sub_group_res = await sub.sub_group_list() + sub_group = sub_group_res.result + # 需通知的群 + notice_group = list(set(all_groups) & set(sub_group)) + + try: + up_name = live_up_name[room_id] + status = live_status[room_id] + title = live_title[room_id] + except KeyError: + await init_add_live_info(room_id=room_id) + try: + up_name = live_up_name[room_id] + status = live_status[room_id] + title = live_title[room_id] + except KeyError: + logger.error(f'直播间: {room_id} 状态失效且获取失败!') + raise Exception('直播间状态失效且获取失败') + + # 检查是否是已开播状态, 若已开播则监测直播间标题变动 + # 为避免开播时同时出现标题变更通知和开播通知, 在检测到直播状态变化时更新标题, 且仅在直播状态为直播中时发送标题变更通知 + if live_info['status'] != live_status[room_id] \ + and live_info['status'] == 1 \ + and live_info['title'] != live_title[room_id]: + # 更新标题 + live_title[room_id] = live_info['title'] + logger.info(f"直播间: {room_id}/{up_name} 标题变更为: {live_info['title']}") + elif live_info['status'] == 1 and live_info['title'] != live_title[room_id]: + # 通知有通知权限且订阅了该直播间的群 + cover_pic = await pic_2_base64(url=live_info.get('cover_img')) + if cover_pic.success(): + msg = f"{up_name}的直播间换标题啦!\n\n【{live_info['title']}】\n{MessageSegment.image(cover_pic.result)}" + else: + # msg = f"{up_name}的直播间换标题啦!\n\n【{live_info['title']}】\n{live_info['url']}" + msg = f"{up_name}的直播间换标题啦!\n\n【{live_info['title']}】" + for group_id in notice_group: + for _bot in bots: + try: + await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) + logger.info(f"向群组: {group_id} 发送直播间: {room_id} 标题变更通知") + except Exception as _e: + logger.warning(f"向群组: {group_id} 发送直播间: {room_id} 标题变更通知失败, error: {repr(_e)}") + continue + live_title[room_id] = live_info['title'] + logger.info(f"直播间: {room_id}/{up_name} 标题变更为: {live_info['title']}") + + # 检测开播/下播 + # 检查直播间状态与原状态是否一致 + if live_info['status'] != live_status[room_id]: + # 现在状态为未开播 + if live_info['status'] == 0: + live_end_info = f"LiveEnd! Room: {room_id}/{up_name}" + new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', + detail_type='live') + await new_event.add(sub_type='live_end', user_id=room_id, user_name=up_name, + raw_data=repr(live_info), msg_data=live_end_info) + + msg = f'{up_name}下播了' + + # 更新直播间状态 + live_status[room_id] = live_info['status'] + logger.info(f"直播间: {room_id}/{up_name} 下播了") + # 现在状态为直播中 + elif live_info['status'] == 1: + # 记录准确开播信息 + live_start_info = f"LiveStart! Room: {room_id}/{up_name}, Title: {live_info['title']}, " \ + f"TrueTime: {live_info['time']}" + new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', + detail_type='live') + await new_event.add(sub_type='live_start', user_id=room_id, user_name=up_name, + raw_data=repr(live_info), msg_data=live_start_info) + + cover_pic = await pic_2_base64(url=live_info.get('cover_img')) + if cover_pic.success(): + msg = f"{live_info['time']}\n{up_name}开播啦!\n\n【{live_info['title']}】" \ + f"\n{MessageSegment.image(cover_pic.result)}" + else: + # msg = f"{live_info['time']}\n{up_name}开播啦!\n\n【{live_info['title']}】\n{live_info['url']}" + msg = f"{live_info['time']}\n{up_name}开播啦!\n\n【{live_info['title']}】" + + live_status[room_id] = live_info['status'] + logger.info(f"直播间: {room_id}/{up_name} 开播了") + # 现在状态为未开播(轮播中) + elif live_info['status'] == 2: + live_end_info = f"LiveEnd! Room: {room_id}/{up_name}" + new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', + detail_type='live') + await new_event.add(sub_type='live_end_with_playlist', user_id=room_id, user_name=up_name, + raw_data=repr(live_info), msg_data=live_end_info) + + msg = f'{up_name}下播了(轮播中)' + + live_status[room_id] = live_info['status'] + logger.info(f"直播间: {room_id}/{up_name} 下播了(轮播中)") + else: + live_unknown_info = f"Unknown live status, Room: {room_id}/{up_name}" + new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', + detail_type='live') + await new_event.add(sub_type='unknown live status', user_id=room_id, user_name=up_name, + raw_data=repr(live_info), msg_data=live_unknown_info) + + live_status[room_id] = live_info['status'] + msg = None + logger.warning(f"直播间: {room_id}/{up_name}, 未知的直播间状态: ") + + if msg: + # 通知有通知权限且订阅了该直播间的群 + for group_id in notice_group: + for _bot in bots: + try: + await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) + logger.info( + f"向群组: {group_id} 发送直播间: {room_id}/{up_name} 直播通知, status: {live_info['status']}") + except Exception as _e: + logger.warning(f"向群组: {group_id} 发送直播间: {room_id}/{up_name} 直播通知失败, error: {repr(_e)}") + continue + + +# 创建直播检查函数 +async def bilibili_live_monitor(): + logger.debug(f"bilibili_live_monitor: checking started") + # 获取当前bot列表 bots = [] for bot_id, bot in get_bots().items(): @@ -190,7 +326,7 @@ async def bilibili_live_monitor(): sub_res = await t.list_col_with_condition('sub_id', 'sub_type', 1) check_sub = [int(x) for x in sub_res.result] - # 注册一个异步函数用于检查直播间状态 + # 检查单个直播间状态 async def check_live(room_id: int): # 获取直播间信息 _res = await get_live_info(room_id=room_id) @@ -199,128 +335,56 @@ async def check_live(room_id: int): return live_info = _res.result - sub = DBSubscription(sub_type=1, sub_id=room_id) + try: + await live_status_sender(room_id=room_id, live_info=live_info, bots=bots, all_groups=all_noitce_groups) + except Exception as _e: + logger.error(f'bilibili_live_monitor: 处理直播间 {room_id} 状态信息是发生错误: {repr(_e)}') + + # 检查列表uid全部用户的直播间状态 + async def check_live_by_rids(room_id_list: list): + uid_list = [] + for room_id in room_id_list: + uid = live_uid_by_rid.get(room_id) + if not uid: + await init_add_live_info(room_id=room_id) + uid = live_uid_by_rid.get(room_id) + if not uid: + logger.warning(f'bilibili_live_monitor: get uid from room_id failed, room_id: {room_id}') + continue + uid_list.append(uid) - # 获取订阅了该直播间的所有群 - sub_group_res = await sub.sub_group_list() - sub_group = sub_group_res.result - # 需通知的群 - notice_group = list(set(all_noitce_groups) & set(sub_group)) + # 获取直播间信息 + live_info_result = await get_live_info_by_uid_list(uid_list=uid_list) + if live_info_result.error: + logger.error(f'bilibili_live_monitor: 获取直播间信息失败: error info: {live_info_result.info}') + return - try: - up_name = live_up_name[room_id] - status = live_status[room_id] - title = live_title[room_id] - except KeyError: - await init_add_live_info(room_id=room_id) - try: - up_name = live_up_name[room_id] - status = live_status[room_id] - title = live_title[room_id] - except KeyError: - logger.error(f'直播间: {room_id} 状态失效且获取失败!') - raise Exception('直播间状态失效且获取失败') - - # 检查是否是已开播状态, 若已开播则监测直播间标题变动 - # 为避免开播时同时出现标题变更通知和开播通知, 在检测到直播状态变化时更新标题, 且仅在直播状态为直播中时发送标题变更通知 - if live_info['status'] != live_status[room_id]\ - and live_info['status'] == 1\ - and live_info['title'] != live_title[room_id]: - # 更新标题 - live_title[room_id] = live_info['title'] - logger.info(f"直播间: {room_id}/{up_name} 标题变更为: {live_info['title']}") - elif live_info['status'] == 1 and live_info['title'] != live_title[room_id]: - # 通知有通知权限且订阅了该直播间的群 - cover_pic = await pic_2_base64(url=live_info.get('cover_img')) - if cover_pic.success(): - msg = f"{up_name}的直播间换标题啦!\n\n【{live_info['title']}】\n{MessageSegment.image(cover_pic.result)}" - else: - # msg = f"{up_name}的直播间换标题啦!\n\n【{live_info['title']}】\n{live_info['url']}" - msg = f"{up_name}的直播间换标题啦!\n\n【{live_info['title']}】" - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送直播间: {room_id} 标题变更通知") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送直播间: {room_id} 标题变更通知失败, error: {repr(_e)}") - continue - live_title[room_id] = live_info['title'] - logger.info(f"直播间: {room_id}/{up_name} 标题变更为: {live_info['title']}") + # 处理直播间短号, 否则会出现订阅短号的群组无法收到通知 + live_info_ = dict(live_info_result.result) + for room_id, live_info in live_info_.copy().items(): + short_id = live_info.get('short_id') + if short_id and short_id != 0: + live_info_.update({short_id: live_info}) - # 检测开播/下播 - # 检查直播间状态与原状态是否一致 - if live_info['status'] != live_status[room_id]: + # 依次处理各直播间信息 + for room_id, live_info in live_info_.items(): try: - # 现在状态为未开播 - if live_info['status'] == 0: - live_start_info = f"LiveEnd! Room: {room_id}/{up_name}" - new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', detail_type='live') - await new_event.add(sub_type='live_end', user_id=room_id, user_name=up_name, - raw_data=repr(live_info), msg_data=live_start_info) - - msg = f'{up_name}下播了' - # 通知有通知权限且订阅了该直播间的群 - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送直播间: {room_id} 下播通知") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送直播间: {room_id} 下播通知失败, error: {repr(_e)}") - continue - # 更新直播间状态 - live_status[room_id] = live_info['status'] - logger.info(f"直播间: {room_id}/{up_name} 下播了") - # 现在状态为直播中 - elif live_info['status'] == 1: - # 记录准确开播信息 - live_start_info = f"LiveStart! Room: {room_id}/{up_name}, Title: {live_info['title']}, " \ - f"TrueTime: {live_info['time']}" - new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', detail_type='live') - await new_event.add(sub_type='live_start', user_id=room_id, user_name=up_name, - raw_data=repr(live_info), msg_data=live_start_info) - - cover_pic = await pic_2_base64(url=live_info.get('cover_img')) - if cover_pic.success(): - msg = f"{live_info['time']}\n{up_name}开播啦!\n\n【{live_info['title']}】" \ - f"\n{MessageSegment.image(cover_pic.result)}" - else: - # msg = f"{live_info['time']}\n{up_name}开播啦!\n\n【{live_info['title']}】\n{live_info['url']}" - msg = f"{live_info['time']}\n{up_name}开播啦!\n\n【{live_info['title']}】" - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送直播间: {room_id} 开播通知") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送直播间: {room_id} 开播通知失败, error: {repr(_e)}") - continue - live_status[room_id] = live_info['status'] - logger.info(f"直播间: {room_id}/{up_name} 开播了") - # 现在状态为未开播(轮播中) - elif live_info['status'] == 2: - live_start_info = f"LiveEnd! Room: {room_id}/{up_name}" - new_event = DBHistory(time=int(time.time()), self_id=-1, post_type='bilibili', detail_type='live') - await new_event.add(sub_type='live_end_with_playlist', user_id=room_id, user_name=up_name, - raw_data=repr(live_info), msg_data=live_start_info) - - msg = f'{up_name}下播了(轮播中)' - for group_id in notice_group: - for _bot in bots: - try: - await _bot.call_api(api='send_group_msg', group_id=group_id, message=msg) - logger.info(f"向群组: {group_id} 发送直播间: {room_id} 下播通知") - except Exception as _e: - logger.warning(f"向群组: {group_id} 发送直播间: {room_id} 下播通知失败, error: {repr(_e)}") - continue - live_status[room_id] = live_info['status'] - logger.info(f"直播间: {room_id}/{up_name} 下播了(轮播中)") + await live_status_sender(room_id=room_id, live_info=live_info, bots=bots, all_groups=all_noitce_groups) except Exception as _e: - logger.warning(f'试图向群组发送直播间: {room_id}/{up_name} 的直播通知时发生了错误: {repr(_e)}') + logger.error(f'bilibili_live_monitor: 处理直播间 {room_id} 状态信息是发生错误: {repr(_e)}') + continue + # 使用了新的API + if ENABLE_NEW_LIVE_API: + # 检查所有在订阅表里面的直播间(异步) + try: + await check_live_by_rids(room_id_list=check_sub) + logger.debug(f"bilibili_live_monitor: enable new api, checking completed, " + f"checked: {', '.join([str(x) for x in check_sub])}.") + except Exception as e: + logger.error(f'bilibili_live_monitor: enable new api, error occurred in checking {repr(e)}') # 启用了检查池模式 - if ENABLE_BILI_CHECK_POOL_MODE: + elif ENABLE_LIVE_CHECK_POOL_MODE: global checking_pool # checking_pool为空则上一轮检查完了, 重新往里面放新一轮的room_id @@ -353,7 +417,6 @@ async def check_live(room_id: int): f"checked: {', '.join([str(x) for x in now_checking])}.") except Exception as e: logger.error(f'bilibili_live_monitor: pool mode enable, error occurred in checking {repr(e)}') - # 没有启用检查池模式 else: # 检查所有在订阅表里面的直播间(异步) @@ -370,8 +433,8 @@ async def check_live(room_id: int): # 分时间段创建计划任务, 夜间闲时降低检查频率 # 根据检查池模式初始化检查时间间隔 -if ENABLE_BILI_CHECK_POOL_MODE: - # 检查池启用 +if ENABLE_NEW_LIVE_API: + # 使用新api scheduler.add_job( bilibili_live_monitor, 'cron', @@ -380,18 +443,18 @@ async def check_live(room_id: int): # day='*/1', # week=None, # day_of_week=None, - # hour='9-23', - # minute='*/2', - second='10-50/20', + # hour=None, + # minute=None, + second='*/30', # start_date=None, # end_date=None, # timezone=None, - id='bilibili_live_monitor_pool_enable', + id='bilibili_live_monitor_enable_new_api', coalesce=True, - misfire_grace_time=20 + misfire_grace_time=30 ) -else: - # 检查池禁用, 日间 +elif ENABLE_LIVE_CHECK_POOL_MODE: + # 检查池启用 scheduler.add_job( bilibili_live_monitor, 'cron', @@ -400,17 +463,18 @@ async def check_live(room_id: int): # day='*/1', # week=None, # day_of_week=None, - hour='9-23', - minute='*/1', - # second='*/30', + # hour='9-23', + # minute='*/2', + second='10-50/20', # start_date=None, # end_date=None, # timezone=None, - id='bilibili_live_monitor_in_day_pool_disable', + id='bilibili_live_monitor_pool_enable', coalesce=True, - misfire_grace_time=30 + misfire_grace_time=20 ) - # 检查池禁用, 夜间 +else: + # 检查池禁用, 日间 scheduler.add_job( bilibili_live_monitor, 'cron', @@ -419,13 +483,13 @@ async def check_live(room_id: int): # day='*/1', # week=None, # day_of_week=None, - hour='0-8', - minute='*/5', - # second='*/30', + # hour=None, + minute='*/1', + # second=None, # start_date=None, # end_date=None, # timezone=None, - id='bilibili_live_monitor_in_night_pool_disable', + id='bilibili_live_monitor_pool_disable', coalesce=True, misfire_grace_time=30 ) diff --git a/omega_miya/plugins/bilibili_live_monitor/utils.py b/omega_miya/plugins/bilibili_live_monitor/utils.py index 24fd8726..edab47f0 100644 --- a/omega_miya/plugins/bilibili_live_monitor/utils.py +++ b/omega_miya/plugins/bilibili_live_monitor/utils.py @@ -1,16 +1,39 @@ import nonebot +import datetime +from typing import List, Union +from nonebot.log import logger from omega_miya.utils.Omega_Base import Result from omega_miya.utils.Omega_plugin_utils import HttpFetcher, PicEncoder +from .config import Config LIVE_API_URL = 'https://api.live.bilibili.com/room/v1/Room/get_info' +LIVE_BY_UIDS_API_URL = 'https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids' USER_INFO_API_URL = 'https://api.bilibili.com/x/space/acc/info' LIVE_URL = 'https://live.bilibili.com/' global_config = nonebot.get_driver().config +plugin_config = Config(**global_config.dict()) BILI_SESSDATA = global_config.bili_sessdata BILI_CSRF = global_config.bili_csrf BILI_UID = global_config.bili_uid -ENABLE_BILI_CHECK_POOL_MODE = global_config.enable_bili_check_pool_mode +ENABLE_NEW_LIVE_API = plugin_config.enable_new_live_api +ENABLE_LIVE_CHECK_POOL_MODE = plugin_config.enable_live_check_pool_mode + +HEADERS = {'accept': 'application/json, text/plain, */*', + 'accept-encoding': 'gzip, deflate', + 'accept-language': 'zh-CN,zh;q=0.9', + 'dnt': '1', + 'origin': 'https://www.bilibili.com', + 'referer': 'https://www.bilibili.com/', + 'sec-ch-ua': '"Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-site', + 'sec-gpc': '1', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/89.0.4389.114 Safari/537.36' + } def check_bili_cookies() -> Result: @@ -31,23 +54,7 @@ async def fetch_json(url: str, paras: dict = None) -> HttpFetcher.FetcherJsonRes if cookies_res.success(): cookies = cookies_res.result - headers = {'accept': 'application/json, text/plain, */*', - 'accept-encoding': 'gzip, deflate', - 'accept-language': 'zh-CN,zh;q=0.9', - 'dnt': '1', - 'origin': 'https://www.bilibili.com', - 'referer': 'https://www.bilibili.com/', - 'sec-ch-ua': '"Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"', - 'sec-ch-ua-mobile': '?0', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-site', - 'sec-gpc': '1', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/89.0.4389.114 Safari/537.36' - } - - fetcher = HttpFetcher(timeout=10, flag='bilibili_live_monitor', headers=headers, cookies=cookies) + fetcher = HttpFetcher(timeout=10, flag='bilibili_live_monitor', headers=HEADERS, cookies=cookies) result = await fetcher.get_json(url=url, params=paras) return result @@ -91,7 +98,9 @@ async def get_live_info(room_id) -> Result: 'title': live_info['data']['title'], 'time': live_info['data']['live_time'], 'uid': live_info['data']['uid'], - 'cover_img': live_info['data']['user_cover'] + 'cover_img': live_info['data']['user_cover'], + 'room_id': live_info['data']['room_id'], + 'short_id': live_info['data']['short_id'] } result = Result(error=False, info='Success', result=_res) except Exception as e: @@ -99,6 +108,47 @@ async def get_live_info(room_id) -> Result: return result +# 获取直播间信息 +async def get_live_info_by_uid_list(uid_list: List[Union[int, str]]) -> Result: + """ + :param uid_list: uid 列表 + :return: result: {直播间房间号: 直播间信息} + """ + payload = {'uids': uid_list} + + fetcher = HttpFetcher(timeout=10, flag='bilibili_live_monitor_list_users_live', headers=HEADERS) + api_result = await fetcher.post_json(url=LIVE_BY_UIDS_API_URL, json=payload) + + if api_result.error: + return Result(error=True, info=api_result.info, result={}) + + api_code = api_result.result.get('code') + api_msg = api_result.result.get('message') + if api_code != 0: + return Result(error=True, info=f'Api error: {api_msg}', result={}) + + result = {} + live_data = dict(api_result.result.get('data')) + for uid, room_info in live_data.items(): + try: + result.update({ + int(room_info.get('room_id')): { + 'status': room_info.get('live_status'), + 'url': LIVE_URL + str(room_info.get('room_id')), + 'title': room_info.get('title'), + 'time': datetime.datetime.fromtimestamp(room_info.get('live_time')).strftime('%Y-%m-%d %H:%M:%S'), + 'uid': int(uid), + 'cover_img': room_info.get('cover_from_user'), + 'room_id': room_info.get('room_id'), + 'short_id': room_info.get('short_id') + } + }) + except Exception as e: + logger.warning(f'bilibili_live_monitor_utils: parse room live info failed, error info: {repr(e)}') + continue + return Result(error=False, info='Success', result=result) + + # 根据用户uid获取用户信息 async def get_user_info(user_uid) -> Result: url = USER_INFO_API_URL @@ -143,8 +193,10 @@ async def verify_cookies() -> Result: __all__ = [ 'get_live_info', + 'get_live_info_by_uid_list', 'get_user_info', 'pic_2_base64', 'verify_cookies', - 'ENABLE_BILI_CHECK_POOL_MODE' + 'ENABLE_NEW_LIVE_API', + 'ENABLE_LIVE_CHECK_POOL_MODE' ] diff --git a/omega_miya/utils/Omega_plugin_utils/__init__.py b/omega_miya/utils/Omega_plugin_utils/__init__.py index 82cfba5e..b91eba8d 100644 --- a/omega_miya/utils/Omega_plugin_utils/__init__.py +++ b/omega_miya/utils/Omega_plugin_utils/__init__.py @@ -6,7 +6,7 @@ from .cooldown import * from .permission import * from .http_fetcher import HttpFetcher -from .picture_to_base64 import PicEncoder +from .picture_encoder import PicEncoder def init_export( diff --git a/omega_miya/utils/Omega_plugin_utils/http_fetcher.py b/omega_miya/utils/Omega_plugin_utils/http_fetcher.py index 03cdc9a8..ecf085a2 100644 --- a/omega_miya/utils/Omega_plugin_utils/http_fetcher.py +++ b/omega_miya/utils/Omega_plugin_utils/http_fetcher.py @@ -211,8 +211,8 @@ async def post_json( self, url: str, params: Dict[str, str] = None, - json: Dict[str, str] = None, - data: Dict[str, str] = None, + json: Dict[str, Any] = None, + data: Dict[str, Any] = None, force_proxy: bool = False, **kwargs: Any) -> FetcherJsonResult: proxy = await self.__get_proxy(always_return_proxy=force_proxy) @@ -253,8 +253,8 @@ async def post_text( self, url: str, params: Dict[str, str] = None, - json: Dict[str, str] = None, - data: Dict[str, str] = None, + json: Dict[str, Any] = None, + data: Dict[str, Any] = None, force_proxy: bool = False, **kwargs: Any) -> FetcherTextResult: proxy = await self.__get_proxy(always_return_proxy=force_proxy) @@ -295,8 +295,8 @@ async def post_bytes( self, url: str, params: Dict[str, str] = None, - json: Dict[str, str] = None, - data: Dict[str, str] = None, + json: Dict[str, Any] = None, + data: Dict[str, Any] = None, force_proxy: bool = False, **kwargs: Any) -> FetcherBytesResult: proxy = await self.__get_proxy(always_return_proxy=force_proxy) diff --git a/omega_miya/utils/Omega_plugin_utils/picture_to_base64.py b/omega_miya/utils/Omega_plugin_utils/picture_encoder.py similarity index 100% rename from omega_miya/utils/Omega_plugin_utils/picture_to_base64.py rename to omega_miya/utils/Omega_plugin_utils/picture_encoder.py diff --git a/omega_miya/utils/nhentai_utils/__init__.py b/omega_miya/utils/nhentai_utils/__init__.py index bfaae6a6..3bb0b792 100644 --- a/omega_miya/utils/nhentai_utils/__init__.py +++ b/omega_miya/utils/nhentai_utils/__init__.py @@ -240,7 +240,7 @@ def __compress7z() -> bool: f.write(json.dumps(gallery)) z.write(manifest_path, 'manifest.json') for index_, url_, local_, file_type_ in request_list: - z.write(local, f'{index_}.{file_type_}') + z.write(local_, f'{index_}.{file_type_}') # 保存密码 with open(password_file, 'w+', encoding='utf-8') as f: f.write(password)