diff --git a/README.md b/README.md index 63ae6594..e844a800 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ - [x] 移动图片 (同上) - [x] 删除图片 (同上) - [x] 群内B站订阅 +- [x] 群词条 ### 已实现的超级用户功能 - [x] 添加/删除权限(是真寻的管理员权限,不是群管理员) @@ -272,7 +273,7 @@ -## 详细配置请前往文档,以下为最简部署和配置 +## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话 ## 简单部署 @@ -320,6 +321,18 @@ python bot.py ## 更新 +### 2021/11/23 + +* 替换cos API +* 提供私聊b了,即跨群b了用户 +* 修复游戏抽卡导入角色失败(原神) +* 修复无Pixiv代理时报错 +* 将项目中大部分aiohttp替换为httpx +* 删除了丘丘人翻译插件 +* 新增群词条 +* 修复游戏抽卡碧蓝航线bwiki格式更改导致获取报错 +* 首次启动会生成配置文件后停止程序,配置后再次启动即可 + ### 2021/11/18 * 修复超级用户无法正确拉真寻入群 diff --git a/__version__ b/__version__ index 4ac81ba1..82cda522 100644 --- a/__version__ +++ b/__version__ @@ -1 +1 @@ -__version__: v0.0.6.1 +__version__: v0.0.6.2 diff --git a/basic_plugins/__init__.py b/basic_plugins/__init__.py old mode 100644 new mode 100755 diff --git a/basic_plugins/admin_bot_manage/__init__.py b/basic_plugins/admin_bot_manage/__init__.py old mode 100644 new mode 100755 index 0449b3f4..2cf0aeef --- a/basic_plugins/admin_bot_manage/__init__.py +++ b/basic_plugins/admin_bot_manage/__init__.py @@ -4,7 +4,7 @@ import nonebot Config.add_plugin_config( "admin_bot_manage:custom_welcome_message", - "SET_GROUP_WELCOME_MESSAGE_LEVEL", + "SET_GROUP_WELCOME_MESSAGE_LEVEL [LEVEL]", 2, name="群管理员操作", help_="设置群欢迎消息权限", diff --git a/basic_plugins/admin_bot_manage/admin_config.py b/basic_plugins/admin_bot_manage/admin_config.py old mode 100644 new mode 100755 index bd34b3d5..b3ca2380 --- a/basic_plugins/admin_bot_manage/admin_config.py +++ b/basic_plugins/admin_bot_manage/admin_config.py @@ -1,38 +1,38 @@ -from nonebot import on_notice -from services.log import logger -from nonebot.adapters.cqhttp import Bot, GroupAdminNoticeEvent -from nonebot.typing import T_State -from models.level_user import LevelUser -from models.group_member_info import GroupInfoUser -from configs.config import Config - - -__zx_plugin_name__ = "群管理员变动监测 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -admin_notice = on_notice(priority=5) - - -@admin_notice.handle() -async def _(bot: Bot, event: GroupAdminNoticeEvent, state: T_State): - try: - nickname = ( - await GroupInfoUser.get_member_info(event.user_id, event.group_id) - ).user_name - except AttributeError: - nickname = event.user_id - if event.sub_type == "set": - await LevelUser.set_level( - event.user_id, - event.group_id, - Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"), - ) - logger.info( - f"为新晋管理员 {nickname}({event.user_id}) " - f"添加权限等级:{Config.get_config('admin_bot_manage', 'ADMIN_DEFAULT_AUTH')}" - ) - elif event.sub_type == "unset": - await LevelUser.delete_level(event.user_id, event.group_id) - logger.info(f"将非管理员 {nickname}({event.user_id}) 取消权限等级") +from nonebot import on_notice +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupAdminNoticeEvent +from nonebot.typing import T_State +from models.level_user import LevelUser +from models.group_member_info import GroupInfoUser +from configs.config import Config + + +__zx_plugin_name__ = "群管理员变动监测 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +admin_notice = on_notice(priority=5) + + +@admin_notice.handle() +async def _(bot: Bot, event: GroupAdminNoticeEvent, state: T_State): + try: + nickname = ( + await GroupInfoUser.get_member_info(event.user_id, event.group_id) + ).user_name + except AttributeError: + nickname = event.user_id + if event.sub_type == "set": + await LevelUser.set_level( + event.user_id, + event.group_id, + Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"), + ) + logger.info( + f"为新晋管理员 {nickname}({event.user_id}) " + f"添加权限等级:{Config.get_config('admin_bot_manage', 'ADMIN_DEFAULT_AUTH')}" + ) + elif event.sub_type == "unset": + await LevelUser.delete_level(event.user_id, event.group_id) + logger.info(f"将非管理员 {nickname}({event.user_id}) 取消权限等级") diff --git a/basic_plugins/admin_bot_manage/custom_welcome_message.py b/basic_plugins/admin_bot_manage/custom_welcome_message.py old mode 100644 new mode 100755 index 6eca8539..dc06f9ef --- a/basic_plugins/admin_bot_manage/custom_welcome_message.py +++ b/basic_plugins/admin_bot_manage/custom_welcome_message.py @@ -1,51 +1,51 @@ -from nonebot import on_command -from utils.utils import get_message_text, get_message_imgs -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from .data_source import custom_group_welcome -from nonebot.adapters.cqhttp.permission import GROUP -from configs.config import Config -from services.log import logger - - -__zx_plugin_name__ = "自定义进群欢迎消息 [Admin]" -__plugin_usage__ = """ -usage: - 指令: - 自定义进群欢迎消息 ?[文本] ?[图片] - 示例:自定义进群欢迎消息 欢迎新人![图片] - Note:可以通过[at]来确认是否艾特新成员 - 示例:自定义进群欢迎消息 欢迎你[at] -""".strip() -__plugin_des__ = '简易的自定义群欢迎消息' -__plugin_cmd__ = ['自定义群欢迎消息 ?[文本] ?[图片]'] -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' -__plugin_settings__ = { - "admin_level": Config.get_config("admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"), -} - -custom_welcome = on_command( - "自定义进群欢迎消息", - aliases={"自定义欢迎消息", "自定义群欢迎消息", "设置群欢迎消息"}, - permission=GROUP, - priority=5, - block=True, -) - - -@custom_welcome.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - try: - msg = get_message_text(event.json()) - imgs = get_message_imgs(event.json()) - if not msg and not imgs: - await custom_welcome.finish(__plugin_usage__) - await custom_welcome.send( - await custom_group_welcome(msg, imgs, event.user_id, event.group_id), - at_sender=True, - ) - logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}") - except Exception as e: - logger.error(f"自定义进群欢迎消息发生错误 {type(e)}:{e}") - await custom_welcome.send("发生了一些未知错误...") +from nonebot import on_command +from utils.utils import get_message_text, get_message_imgs +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from .data_source import custom_group_welcome +from nonebot.adapters.cqhttp.permission import GROUP +from configs.config import Config +from services.log import logger + + +__zx_plugin_name__ = "自定义进群欢迎消息 [Admin]" +__plugin_usage__ = """ +usage: + 指令: + 自定义进群欢迎消息 ?[文本] ?[图片] + 示例:自定义进群欢迎消息 欢迎新人![图片] + Note:可以通过[at]来确认是否艾特新成员 + 示例:自定义进群欢迎消息 欢迎你[at] +""".strip() +__plugin_des__ = '简易的自定义群欢迎消息' +__plugin_cmd__ = ['自定义群欢迎消息 ?[文本] ?[图片]'] +__plugin_version__ = 0.1 +__plugin_author__ = 'HibiKier' +__plugin_settings__ = { + "admin_level": Config.get_config("admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"), +} + +custom_welcome = on_command( + "自定义进群欢迎消息", + aliases={"自定义欢迎消息", "自定义群欢迎消息", "设置群欢迎消息"}, + permission=GROUP, + priority=5, + block=True, +) + + +@custom_welcome.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + try: + msg = get_message_text(event.json()) + imgs = get_message_imgs(event.json()) + if not msg and not imgs: + await custom_welcome.finish(__plugin_usage__) + await custom_welcome.send( + await custom_group_welcome(msg, imgs, event.user_id, event.group_id), + at_sender=True, + ) + logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}") + except Exception as e: + logger.error(f"自定义进群欢迎消息发生错误 {type(e)}:{e}") + await custom_welcome.send("发生了一些未知错误...") diff --git a/basic_plugins/admin_bot_manage/data_source.py b/basic_plugins/admin_bot_manage/data_source.py old mode 100644 new mode 100755 index e0954eaa..345ad630 --- a/basic_plugins/admin_bot_manage/data_source.py +++ b/basic_plugins/admin_bot_manage/data_source.py @@ -1,296 +1,290 @@ -from typing import List -from nonebot.adapters.cqhttp.message import MessageSegment -from services.log import logger -from configs.path_config import DATA_PATH -from utils.message_builder import image -from utils.utils import get_local_proxy, get_bot -from pathlib import Path -from models.group_member_info import GroupInfoUser -from datetime import datetime -from services.db_context import db -from models.level_user import LevelUser -from configs.config import Config -from utils.manager import group_manager, plugins2settings_manager, plugins_manager -from utils.image_utils import CreateImg -import aiofiles -import aiohttp -import asyncio -import time -import os - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -async def group_current_status(group_id: int) -> str: - """ - 获取当前所有通知的开关 - :param group_id: 群号 - """ - rst = "[被动技能 状态]\n" - _data = group_manager.get_task_data() - for task in _data.keys(): - rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n' - return rst.strip() - - -custom_welcome_msg_json = ( - Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" -) - - -async def custom_group_welcome( - msg: str, imgs: List[str], user_id: int, group_id: int -) -> str: - """ - 替换群欢迎消息 - :param msg: 欢迎消息文本 - :param imgs: 欢迎消息图片,只取第一张 - :param user_id: 用户id,用于log记录 - :param group_id: 群号 - """ - img_result = "" - img = imgs[0] if imgs else "" - result = "" - if os.path.exists(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"): - os.remove(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") - if not custom_welcome_msg_json.exists(): - custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True) - data = {} - else: - try: - data = json.load(open(custom_welcome_msg_json, "r")) - except FileNotFoundError: - data = {} - try: - if msg: - data[str(group_id)] = str(msg) - json.dump( - data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False - ) - logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}") - result += msg - if img: - async with aiohttp.ClientSession() as session: - async with session.get(img, proxy=get_local_proxy()) as response: - async with aiofiles.open( - DATA_PATH + f"custom_welcome_msg/{group_id}.jpg", "wb" - ) as f: - await f.write(await response.read()) - img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") - logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片") - except Exception as e: - logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}") - return "替换群消息失败.." - return f"替换群欢迎消息成功:\n{result}" + img_result - - -task_data = None - - -async def change_group_switch(cmd: str, group_id: int, is_super: bool = False): - global task_data - """ - 修改群功能状态 - :param cmd: 功能名称 - :param group_id: 群号 - :param is_super: 是否位超级用户,超级用户用于私聊开关功能状态 - """ - if not task_data: - task_data = group_manager.get_task_data() - group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png" - status = cmd[:2] - cmd = cmd[2:] - type_ = "plugin" - modules = plugins2settings_manager.get_plugin_module(cmd, True) - if cmd == "全部被动": - for task in task_data: - if status == "开启": - if not await group_manager.check_group_task_status(group_id, task): - await group_manager.open_group_task(group_id, task) - else: - if await group_manager.check_group_task_status(group_id, task): - await group_manager.close_group_task(group_id, task) - return f"已 {status} 全部被动技能!" - if cmd in [task_data[x] for x in task_data.keys()]: - type_ = "task" - modules = [x for x in task_data.keys() if task_data[x] == cmd] - for module in modules: - if is_super: - module = f"{module}:super" - if status == "开启": - if type_ == "task": - if await group_manager.check_group_task_status(group_id, module): - return f"被动 {task_data[module]} 正处于开启状态!不要重复开启." - await group_manager.open_group_task(group_id, module) - else: - if group_manager.get_plugin_status(module, group_id): - return f"功能 {cmd} 正处于开启状态!不要重复开启." - group_manager.unblock_plugin(module, group_id) - else: - if type_ == "task": - if not await group_manager.check_group_task_status(group_id, module): - return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭." - await group_manager.close_group_task(group_id, module) - else: - if not group_manager.get_plugin_status(module, group_id): - return f"功能 {cmd} 正处于关闭状态!不要重复关闭." - group_manager.block_plugin(module, group_id) - if group_help_file.exists(): - group_help_file.unlink() - if is_super: - for file in os.listdir(Path(DATA_PATH) / 'group_help'): - file = Path(DATA_PATH) / 'group_help' / file - file.unlink() - else: - _help_image = Path(DATA_PATH) / 'group_help' / f"{group_id}.png" - if _help_image.exists(): - _help_image.unlink() - return f"{status} {cmd} 功能!" - - -def set_plugin_status(cmd: str, block_type: str = "all"): - """ - 设置插件功能状态(超级用户使用) - :param cmd: 功能名称 - :param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊 - """ - status = cmd[:2] - cmd = cmd[2:] - module = plugins2settings_manager.get_plugin_module(cmd) - if status == "开启": - plugins_manager.unblock_plugin(module) - else: - plugins_manager.block_plugin(module, block_type=block_type) - for file in os.listdir(Path(DATA_PATH) / 'group_help'): - file = Path(DATA_PATH) / 'group_help' / file - file.unlink() - - -async def get_plugin_status(): - """ - 获取功能状态 - """ - return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status) - - -def _get_plugin_status() -> MessageSegment: - """ - 合成功能状态图片 - """ - rst = "\t功能\n" - flag_str = "状态".rjust(4) + "\n" - for module in plugins_manager.get_data(): - flag = plugins_manager.get_plugin_block_type(module) - flag = flag.upper() + " CLOSE" if flag else "OPEN" - try: - plugin_name = plugins_manager.get(module)["plugin_name"] - if ( - "[Hidden]" in plugin_name - or "[Admin]" in plugin_name - or "[Superuser]" in plugin_name - ): - continue - rst += f"{plugin_name}" - except KeyError: - rst += f"{module}" - if plugins_manager.get(module)["error"]: - rst += "[ERROR]" - rst += "\n" - flag_str += f"{flag}\n" - height = len(rst.split("\n")) * 24 - a = CreateImg(250, height, font_size=20) - a.text((10, 10), rst) - b = CreateImg(200, height, font_size=20) - b.text((10, 10), flag_str) - A = CreateImg(500, height) - A.paste(a) - A.paste(b, (270, 0)) - return image(b64=A.pic2bs4()) - - -async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool: - """ - 更新群成员信息 - :param group_id: 群号 - :param remind_superuser: 失败信息提醒超级用户 - """ - bot = get_bot() - _group_user_list = await bot.get_group_member_list(group_id=group_id) - _error_member_list = [] - _exist_member_list = [] - # try: - for user_info in _group_user_list: - if user_info["card"] == "": - nickname = user_info["nickname"] - else: - nickname = user_info["card"] - async with db.transaction(): - # 更新权限 - if ( - user_info["role"] - in [ - "owner", - "admin", - ] - and not await LevelUser.is_group_flag(user_info["user_id"], group_id) - ): - await LevelUser.set_level( - user_info["user_id"], - user_info["group_id"], - Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"), - ) - if str(user_info["user_id"]) in bot.config.superusers: - await LevelUser.set_level( - user_info["user_id"], user_info["group_id"], 9 - ) - user = await GroupInfoUser.get_member_info( - user_info["user_id"], user_info["group_id"] - ) - if user: - if user.user_name != nickname: - await user.update(user_name=nickname).apply() - logger.info( - f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功" - ) - _exist_member_list.append(int(user_info["user_id"])) - continue - join_time = datetime.strptime( - time.strftime( - "%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"]) - ), - "%Y-%m-%d %H:%M:%S", - ) - if await GroupInfoUser.add_member_info( - user_info["user_id"], - user_info["group_id"], - nickname, - join_time, - ): - _exist_member_list.append(int(user_info["user_id"])) - logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") - else: - _error_member_list.append( - f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n" - ) - _del_member_list = list( - set(_exist_member_list).difference( - set(await GroupInfoUser.get_group_member_id_list(group_id)) - ) - ) - if _del_member_list: - for del_user in _del_member_list: - if await GroupInfoUser.delete_member_info(del_user, group_id): - logger.info(f"退群用户{del_user} 所属{group_id} 已删除") - else: - logger.info(f"退群用户{del_user} 所属{group_id} 删除失败") - if _error_member_list and remind_superuser: - result = "" - for error_user in _error_member_list: - result += error_user - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), message=result[:-1] - ) - return True +from typing import List +from nonebot.adapters.cqhttp.message import MessageSegment +from services.log import logger +from configs.path_config import DATA_PATH +from utils.message_builder import image +from utils.utils import get_local_proxy, get_bot +from pathlib import Path +from models.group_member_info import GroupInfoUser +from datetime import datetime +from services.db_context import db +from models.level_user import LevelUser +from configs.config import Config +from utils.manager import group_manager, plugins2settings_manager, plugins_manager +from utils.image_utils import CreateImg +from utils.http_utils import AsyncHttpx +import asyncio +import time +import os + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +async def group_current_status(group_id: int) -> str: + """ + 获取当前所有通知的开关 + :param group_id: 群号 + """ + rst = "[被动技能 状态]\n" + _data = group_manager.get_task_data() + for task in _data.keys(): + rst += f'{_data[task]}: {"√" if await group_manager.check_group_task_status(group_id, task) else "×"}\n' + return rst.strip() + + +custom_welcome_msg_json = ( + Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" +) + + +async def custom_group_welcome( + msg: str, imgs: List[str], user_id: int, group_id: int +) -> str: + """ + 替换群欢迎消息 + :param msg: 欢迎消息文本 + :param imgs: 欢迎消息图片,只取第一张 + :param user_id: 用户id,用于log记录 + :param group_id: 群号 + """ + img_result = "" + img = imgs[0] if imgs else "" + result = "" + if os.path.exists(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg"): + os.remove(DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") + if not custom_welcome_msg_json.exists(): + custom_welcome_msg_json.parent.mkdir(parents=True, exist_ok=True) + data = {} + else: + try: + data = json.load(open(custom_welcome_msg_json, "r")) + except FileNotFoundError: + data = {} + try: + if msg: + data[str(group_id)] = str(msg) + json.dump( + data, open(custom_welcome_msg_json, "w"), indent=4, ensure_ascii=False + ) + logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}") + result += msg + if img: + await AsyncHttpx.download_file(img, DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") + img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") + logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片") + except Exception as e: + logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}") + return "替换群消息失败.." + return f"替换群欢迎消息成功:\n{result}" + img_result + + +task_data = None + + +async def change_group_switch(cmd: str, group_id: int, is_super: bool = False): + global task_data + """ + 修改群功能状态 + :param cmd: 功能名称 + :param group_id: 群号 + :param is_super: 是否位超级用户,超级用户用于私聊开关功能状态 + """ + if not task_data: + task_data = group_manager.get_task_data() + group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png" + status = cmd[:2] + cmd = cmd[2:] + type_ = "plugin" + modules = plugins2settings_manager.get_plugin_module(cmd, True) + if cmd == "全部被动": + for task in task_data: + if status == "开启": + if not await group_manager.check_group_task_status(group_id, task): + await group_manager.open_group_task(group_id, task) + else: + if await group_manager.check_group_task_status(group_id, task): + await group_manager.close_group_task(group_id, task) + return f"已 {status} 全部被动技能!" + if cmd in [task_data[x] for x in task_data.keys()]: + type_ = "task" + modules = [x for x in task_data.keys() if task_data[x] == cmd] + for module in modules: + if is_super: + module = f"{module}:super" + if status == "开启": + if type_ == "task": + if await group_manager.check_group_task_status(group_id, module): + return f"被动 {task_data[module]} 正处于开启状态!不要重复开启." + await group_manager.open_group_task(group_id, module) + else: + if group_manager.get_plugin_status(module, group_id): + return f"功能 {cmd} 正处于开启状态!不要重复开启." + group_manager.unblock_plugin(module, group_id) + else: + if type_ == "task": + if not await group_manager.check_group_task_status(group_id, module): + return f"被动 {task_data[module]} 正处于关闭状态!不要重复关闭." + await group_manager.close_group_task(group_id, module) + else: + if not group_manager.get_plugin_status(module, group_id): + return f"功能 {cmd} 正处于关闭状态!不要重复关闭." + group_manager.block_plugin(module, group_id) + if group_help_file.exists(): + group_help_file.unlink() + if is_super: + for file in os.listdir(Path(DATA_PATH) / 'group_help'): + file = Path(DATA_PATH) / 'group_help' / file + file.unlink() + else: + _help_image = Path(DATA_PATH) / 'group_help' / f"{group_id}.png" + if _help_image.exists(): + _help_image.unlink() + return f"{status} {cmd} 功能!" + + +def set_plugin_status(cmd: str, block_type: str = "all"): + """ + 设置插件功能状态(超级用户使用) + :param cmd: 功能名称 + :param block_type: 限制类型, 'all': 私聊+群里, 'private': 私聊, 'group': 群聊 + """ + status = cmd[:2] + cmd = cmd[2:] + module = plugins2settings_manager.get_plugin_module(cmd) + if status == "开启": + plugins_manager.unblock_plugin(module) + else: + plugins_manager.block_plugin(module, block_type=block_type) + for file in os.listdir(Path(DATA_PATH) / 'group_help'): + file = Path(DATA_PATH) / 'group_help' / file + file.unlink() + + +async def get_plugin_status(): + """ + 获取功能状态 + """ + return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status) + + +def _get_plugin_status() -> MessageSegment: + """ + 合成功能状态图片 + """ + rst = "\t功能\n" + flag_str = "状态".rjust(4) + "\n" + for module in plugins_manager.get_data(): + flag = plugins_manager.get_plugin_block_type(module) + flag = flag.upper() + " CLOSE" if flag else "OPEN" + try: + plugin_name = plugins_manager.get(module)["plugin_name"] + if ( + "[Hidden]" in plugin_name + or "[Admin]" in plugin_name + or "[Superuser]" in plugin_name + ): + continue + rst += f"{plugin_name}" + except KeyError: + rst += f"{module}" + if plugins_manager.get(module)["error"]: + rst += "[ERROR]" + rst += "\n" + flag_str += f"{flag}\n" + height = len(rst.split("\n")) * 24 + a = CreateImg(250, height, font_size=20) + a.text((10, 10), rst) + b = CreateImg(200, height, font_size=20) + b.text((10, 10), flag_str) + A = CreateImg(500, height) + A.paste(a) + A.paste(b, (270, 0)) + return image(b64=A.pic2bs4()) + + +async def update_member_info(group_id: int, remind_superuser: bool = False) -> bool: + """ + 更新群成员信息 + :param group_id: 群号 + :param remind_superuser: 失败信息提醒超级用户 + """ + bot = get_bot() + _group_user_list = await bot.get_group_member_list(group_id=group_id) + _error_member_list = [] + _exist_member_list = [] + # try: + for user_info in _group_user_list: + if user_info["card"] == "": + nickname = user_info["nickname"] + else: + nickname = user_info["card"] + async with db.transaction(): + # 更新权限 + if ( + user_info["role"] + in [ + "owner", + "admin", + ] + and not await LevelUser.is_group_flag(user_info["user_id"], group_id) + ): + await LevelUser.set_level( + user_info["user_id"], + user_info["group_id"], + Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"), + ) + if str(user_info["user_id"]) in bot.config.superusers: + await LevelUser.set_level( + user_info["user_id"], user_info["group_id"], 9 + ) + user = await GroupInfoUser.get_member_info( + user_info["user_id"], user_info["group_id"] + ) + if user: + if user.user_name != nickname: + await user.update(user_name=nickname).apply() + logger.info( + f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新群昵称成功" + ) + _exist_member_list.append(int(user_info["user_id"])) + continue + join_time = datetime.strptime( + time.strftime( + "%Y-%m-%d %H:%M:%S", time.localtime(user_info["join_time"]) + ), + "%Y-%m-%d %H:%M:%S", + ) + if await GroupInfoUser.add_member_info( + user_info["user_id"], + user_info["group_id"], + nickname, + join_time, + ): + _exist_member_list.append(int(user_info["user_id"])) + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") + else: + _error_member_list.append( + f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n" + ) + _del_member_list = list( + set(_exist_member_list).difference( + set(await GroupInfoUser.get_group_member_id_list(group_id)) + ) + ) + if _del_member_list: + for del_user in _del_member_list: + if await GroupInfoUser.delete_member_info(del_user, group_id): + logger.info(f"退群用户{del_user} 所属{group_id} 已删除") + else: + logger.info(f"退群用户{del_user} 所属{group_id} 删除失败") + if _error_member_list and remind_superuser: + result = "" + for error_user in _error_member_list: + result += error_user + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), message=result[:-1] + ) + return True diff --git a/basic_plugins/admin_bot_manage/rule.py b/basic_plugins/admin_bot_manage/rule.py old mode 100644 new mode 100755 diff --git a/basic_plugins/admin_bot_manage/switch_rule.py b/basic_plugins/admin_bot_manage/switch_rule.py old mode 100644 new mode 100755 index fee7660b..7d580610 --- a/basic_plugins/admin_bot_manage/switch_rule.py +++ b/basic_plugins/admin_bot_manage/switch_rule.py @@ -1,107 +1,107 @@ -from nonebot import on_command, on_message -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP -from .data_source import ( - change_group_switch, - set_plugin_status, - get_plugin_status, - group_current_status, -) -from services.log import logger -from configs.config import NICKNAME, Config -from utils.utils import get_message_text, is_number -from nonebot.permission import SUPERUSER -from .rule import switch_rule - - -__zx_plugin_name__ = "群功能开关 [Admin]" - -__plugin_usage__ = """ -usage: - 群内功能与被动技能开关 - 指令: - 开启/关闭[功能] - 群被动状态 - 开启全部被动 - 关闭全部被动 - 示例:开启/关闭色图 -""".strip() -__plugin_superuser_usage__ = """ -usage: - 功能总开关与指定群禁用 - 指令: - 功能状态 - 开启/关闭[功能] [group] - 开启/关闭[功能] ['private'/'group'] -""".strip() -__plugin_des__ = "群内功能开关" -__plugin_cmd__ = [ - "开启/关闭[功能]", - "群被动状态", - "开启全部被动", - "关闭全部被动", - "功能状态 [_superuser]", - "开启/关闭[功能] [group] [_superuser]", - "开启/关闭[功能] ['private'/'group'] [_superuser]", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "admin_level": Config.get_config("admin_bot_manage", "CHANGE_GROUP_SWITCH_LEVEL"), - "cmd": ["开启功能", "关闭功能", "开关"] -} - -switch_rule_matcher = on_message(rule=switch_rule, priority=4, block=True) - -plugins_status = on_command("功能状态", permission=SUPERUSER, priority=5, block=True) - -group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True) - - -@switch_rule_matcher.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - _cmd = get_message_text(event.json()).split()[0] - if isinstance(event, GroupMessageEvent): - await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id)) - logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {_cmd}") - else: - if str(event.user_id) in bot.config.superusers: - block_type = " ".join(get_message_text(event.json()).split()[1:]) - block_type = block_type if block_type else "a" - if is_number(block_type): - if not int(block_type) in [ - g["group_id"] - for g in await bot.get_group_list(self_id=int(bot.self_id)) - ]: - await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}") - await change_group_switch(_cmd, int(block_type), True) - group_name = (await bot.get_group_info(group_id=int(block_type)))[ - "group_name" - ] - await switch_rule_matcher.send( - f"已禁用群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能" - ) - elif block_type in ["all", "private", "group", "a", "p", "g"]: - block_type = "all" if block_type == "a" else block_type - block_type = "private" if block_type == "p" else block_type - block_type = "group" if block_type == "g" else block_type - set_plugin_status(_cmd, block_type) - if block_type == "all": - await switch_rule_matcher.send(f"已{_cmd[:2]}功能:{_cmd[2:]}") - elif block_type == "private": - await switch_rule_matcher.send(f"已在私聊中{_cmd[:2]}功能:{_cmd[2:]}") - else: - await switch_rule_matcher.send(f"已在群聊中{_cmd[:2]}功能:{_cmd[2:]}") - else: - await switch_rule_matcher.finish("格式错误:关闭[功能] [group]/[p/g]") - logger.info(f"USER {event.user_id} 使用功能管理命令 {_cmd} | {block_type}") - - -@plugins_status.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - await plugins_status.send(await get_plugin_status()) - - -@group_task_status.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await group_task_status.send(await group_current_status(event.group_id)) +from nonebot import on_command, on_message +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP +from .data_source import ( + change_group_switch, + set_plugin_status, + get_plugin_status, + group_current_status, +) +from services.log import logger +from configs.config import NICKNAME, Config +from utils.utils import get_message_text, is_number +from nonebot.permission import SUPERUSER +from .rule import switch_rule + + +__zx_plugin_name__ = "群功能开关 [Admin]" + +__plugin_usage__ = """ +usage: + 群内功能与被动技能开关 + 指令: + 开启/关闭[功能] + 群被动状态 + 开启全部被动 + 关闭全部被动 + 示例:开启/关闭色图 +""".strip() +__plugin_superuser_usage__ = """ +usage: + 功能总开关与指定群禁用 + 指令: + 功能状态 + 开启/关闭[功能] [group] + 开启/关闭[功能] ['private'/'group'] +""".strip() +__plugin_des__ = "群内功能开关" +__plugin_cmd__ = [ + "开启/关闭[功能]", + "群被动状态", + "开启全部被动", + "关闭全部被动", + "功能状态 [_superuser]", + "开启/关闭[功能] [group] [_superuser]", + "开启/关闭[功能] ['private'/'group'] [_superuser]", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "admin_level": Config.get_config("admin_bot_manage", "CHANGE_GROUP_SWITCH_LEVEL"), + "cmd": ["开启功能", "关闭功能", "开关"] +} + +switch_rule_matcher = on_message(rule=switch_rule, priority=4, block=True) + +plugins_status = on_command("功能状态", permission=SUPERUSER, priority=5, block=True) + +group_task_status = on_command("群被动状态", permission=GROUP, priority=5, block=True) + + +@switch_rule_matcher.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + _cmd = get_message_text(event.json()).split()[0] + if isinstance(event, GroupMessageEvent): + await switch_rule_matcher.send(await change_group_switch(_cmd, event.group_id)) + logger.info(f"USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {_cmd}") + else: + if str(event.user_id) in bot.config.superusers: + block_type = " ".join(get_message_text(event.json()).split()[1:]) + block_type = block_type if block_type else "a" + if is_number(block_type): + if not int(block_type) in [ + g["group_id"] + for g in await bot.get_group_list(self_id=int(bot.self_id)) + ]: + await switch_rule_matcher.finish(f"{NICKNAME}未加入群聊:{block_type}") + await change_group_switch(_cmd, int(block_type), True) + group_name = (await bot.get_group_info(group_id=int(block_type)))[ + "group_name" + ] + await switch_rule_matcher.send( + f"已禁用群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能" + ) + elif block_type in ["all", "private", "group", "a", "p", "g"]: + block_type = "all" if block_type == "a" else block_type + block_type = "private" if block_type == "p" else block_type + block_type = "group" if block_type == "g" else block_type + set_plugin_status(_cmd, block_type) + if block_type == "all": + await switch_rule_matcher.send(f"已{_cmd[:2]}功能:{_cmd[2:]}") + elif block_type == "private": + await switch_rule_matcher.send(f"已在私聊中{_cmd[:2]}功能:{_cmd[2:]}") + else: + await switch_rule_matcher.send(f"已在群聊中{_cmd[:2]}功能:{_cmd[2:]}") + else: + await switch_rule_matcher.finish("格式错误:关闭[功能] [group]/[p/g]") + logger.info(f"USER {event.user_id} 使用功能管理命令 {_cmd} | {block_type}") + + +@plugins_status.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await plugins_status.send(await get_plugin_status()) + + +@group_task_status.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await group_task_status.send(await group_current_status(event.group_id)) diff --git a/basic_plugins/admin_bot_manage/timing_task.py b/basic_plugins/admin_bot_manage/timing_task.py old mode 100644 new mode 100755 index 5bb1aadd..7761385d --- a/basic_plugins/admin_bot_manage/timing_task.py +++ b/basic_plugins/admin_bot_manage/timing_task.py @@ -1,51 +1,51 @@ -from utils.utils import scheduler, get_bot -from .data_source import update_member_info -from services.log import logger -from models.group_info import GroupInfo -from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError - - -__zx_plugin_name__ = '管理方面定时任务 [Hidden]' -__plugin_usage__ = '无' -__plugin_des__ = '成员信息和管理权限的定时更新' -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' - - -# 自动更新群员信息 -@scheduler.scheduled_job( - "cron", - hour=2, - minute=1, -) -async def _(): - bot = get_bot() - if bot: - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - for g in gl: - try: - await update_member_info(g) - logger.info(f"更新群组 g:{g} 成功") - except Exception as e: - logger.error(f"更新群组错误 g:{g} e:{e}") - - -# 快速更新群员信息以及管理员权限 -@scheduler.scheduled_job( - "interval", - minutes=5, -) -async def _(): - try: - bot = get_bot() - if bot: - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - all_group = [x.group_id for x in await GroupInfo.get_all_group()] - for g in gl: - if g not in all_group: - await update_member_info(g, False) - logger.info(f"快速更新群信息以及权限:{g}") - except (IndexError, ConnectionDoesNotExistError, UndefinedColumnError): - pass +from utils.utils import scheduler, get_bot +from .data_source import update_member_info +from services.log import logger +from models.group_info import GroupInfo +from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError + + +__zx_plugin_name__ = '管理方面定时任务 [Hidden]' +__plugin_usage__ = '无' +__plugin_des__ = '成员信息和管理权限的定时更新' +__plugin_version__ = 0.1 +__plugin_author__ = 'HibiKier' + + +# 自动更新群员信息 +@scheduler.scheduled_job( + "cron", + hour=2, + minute=1, +) +async def _(): + bot = get_bot() + if bot: + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + for g in gl: + try: + await update_member_info(g) + logger.info(f"更新群组 g:{g} 成功") + except Exception as e: + logger.error(f"更新群组错误 g:{g} e:{e}") + + +# 快速更新群员信息以及管理员权限 +@scheduler.scheduled_job( + "interval", + minutes=5, +) +async def _(): + try: + bot = get_bot() + if bot: + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + all_group = [x.group_id for x in await GroupInfo.get_all_group()] + for g in gl: + if g not in all_group: + await update_member_info(g, False) + logger.info(f"快速更新群信息以及权限:{g}") + except (IndexError, ConnectionDoesNotExistError, UndefinedColumnError): + pass diff --git a/basic_plugins/admin_bot_manage/update_group_member_info.py b/basic_plugins/admin_bot_manage/update_group_member_info.py old mode 100644 new mode 100755 index 9a5cddec..c7258a36 --- a/basic_plugins/admin_bot_manage/update_group_member_info.py +++ b/basic_plugins/admin_bot_manage/update_group_member_info.py @@ -1,41 +1,41 @@ -from nonebot import on_command, on_notice -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent -from .data_source import update_member_info - -__zx_plugin_name__ = "更新群组成员列表 [Admin]" -__plugin_usage__ = """ -usage: - 更新群组成员的基本信息 - 指令: - 更新群组成员列表/更新群组成员信息 -""".strip() -__plugin_des__ = '更新群组成员列表' -__plugin_cmd__ = ['更新群组成员列表'] -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' -__plugin_settings__ = { - "admin_level": 1, -} - - -refresh_member_group = on_command( - "更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True -) - - -@refresh_member_group.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if await update_member_info(event.group_id): - await refresh_member_group.finish("更新群员信息成功!", at_sender=True) - else: - await refresh_member_group.finish("更新群员信息失败!", at_sender=True) - - -group_increase_handle = on_notice(priority=1, block=False) - - -@group_increase_handle.handle() -async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): - if event.user_id == int(bot.self_id): - await update_member_info(event.group_id) +from nonebot import on_command, on_notice +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent +from .data_source import update_member_info + +__zx_plugin_name__ = "更新群组成员列表 [Admin]" +__plugin_usage__ = """ +usage: + 更新群组成员的基本信息 + 指令: + 更新群组成员列表/更新群组成员信息 +""".strip() +__plugin_des__ = '更新群组成员列表' +__plugin_cmd__ = ['更新群组成员列表'] +__plugin_version__ = 0.1 +__plugin_author__ = 'HibiKier' +__plugin_settings__ = { + "admin_level": 1, +} + + +refresh_member_group = on_command( + "更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True +) + + +@refresh_member_group.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if await update_member_info(event.group_id): + await refresh_member_group.finish("更新群员信息成功!", at_sender=True) + else: + await refresh_member_group.finish("更新群员信息失败!", at_sender=True) + + +group_increase_handle = on_notice(priority=1, block=False) + + +@group_increase_handle.handle() +async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): + if event.user_id == int(bot.self_id): + await update_member_info(event.group_id) diff --git a/basic_plugins/admin_help/__init__.py b/basic_plugins/admin_help/__init__.py old mode 100644 new mode 100755 index 1262eb22..0b110162 --- a/basic_plugins/admin_help/__init__.py +++ b/basic_plugins/admin_help/__init__.py @@ -1,27 +1,27 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters import Bot -from nonebot.adapters.cqhttp import GroupMessageEvent -from utils.message_builder import image -from .data_source import create_help_image, admin_help_image - - -__zx_plugin_name__ = '管理帮助 [Admin]' -__plugin_usage__ = '管理员帮助,在群内回复“管理员帮助”' -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' -__plugin_settings__ = { - "admin_level": 1, -} - -admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, block=True) - -if admin_help_image.exists(): - admin_help_image.unlink() - - -@admin_help.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if not admin_help_image.exists(): - await create_help_image() - await admin_help.send(image('admin_help_img.png')) +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters import Bot +from nonebot.adapters.cqhttp import GroupMessageEvent +from utils.message_builder import image +from .data_source import create_help_image, admin_help_image + + +__zx_plugin_name__ = '管理帮助 [Admin]' +__plugin_usage__ = '管理员帮助,在群内回复“管理员帮助”' +__plugin_version__ = 0.1 +__plugin_author__ = 'HibiKier' +__plugin_settings__ = { + "admin_level": 1, +} + +admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, block=True) + +if admin_help_image.exists(): + admin_help_image.unlink() + + +@admin_help.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if not admin_help_image.exists(): + await create_help_image() + await admin_help.send(image('admin_help_img.png')) diff --git a/basic_plugins/admin_help/data_source.py b/basic_plugins/admin_help/data_source.py old mode 100644 new mode 100755 index 9d8959dc..65862fdb --- a/basic_plugins/admin_help/data_source.py +++ b/basic_plugins/admin_help/data_source.py @@ -1,94 +1,94 @@ -from utils.image_utils import CreateImg -from configs.path_config import IMAGE_PATH -from services.log import logger -from utils.utils import get_matchers -from utils.manager import group_manager -from nonebot.adapters.cqhttp import Bot -from pathlib import Path -from nonebot import Driver -import asyncio -import nonebot - - -driver: Driver = nonebot.get_driver() - -background = Path(IMAGE_PATH) / "background" / "0.png" - -admin_help_image = Path(IMAGE_PATH) / 'admin_help_img.png' - - -@driver.on_bot_connect -async def init_task(bot: Bot = None): - if not group_manager.get_task_data(): - await group_manager.init_group_task() - logger.info(f'已成功加载 {len(group_manager.get_task_data())} 个被动技能.') - - -async def create_help_image(): - """ - 创建管理员帮助图片 - """ - await asyncio.get_event_loop().run_in_executor( - None, _create_help_image - ) - - -def _create_help_image(): - """ - 创建管理员帮助图片 - """ - _matchers = get_matchers() - _plugin_name_list = [] - width = 0 - _plugin_level = {} - for matcher in _matchers: - _plugin = nonebot.plugin.get_plugin(matcher.module) - _module = _plugin.module - try: - plugin_name = _module.__getattribute__("__zx_plugin_name__") - except AttributeError: - continue - try: - if ( - "[admin]" in plugin_name.lower() - and plugin_name not in _plugin_name_list - and plugin_name != "管理帮助 [Admin]" - ): - _plugin_name_list.append(plugin_name) - plugin_settings = _module.__getattribute__("__plugin_settings__") - plugin_des = _module.__getattribute__("__plugin_des__") - plugin_cmd = _module.__getattribute__("__plugin_cmd__") - plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x] - admin_level = int(plugin_settings["admin_level"]) - if _plugin_level.get(admin_level): - _plugin_level[admin_level].append( - f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd) - ) - else: - _plugin_level[admin_level] = [ - f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd) - ] - x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23 - width = width if width > x else x - except AttributeError: - logger.warning(f"获取管理插件 {matcher.module}: {plugin_name} 设置失败...") - help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \ - "[权限等级] 管理员帮助:\n\n" - x = list(_plugin_level.keys()) - x.sort() - for level in x: - for help_ in _plugin_level[level]: - help_str += f"\t{help_}\n\n" - help_str += '-----[被动技能开关]-----\n\n' - task_data = group_manager.get_task_data() - for i, x in enumerate(task_data.keys()): - help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n' - height = len(help_str.split("\n")) * 33 - A = CreateImg(width, height, font_size=24) - _background = CreateImg(width, height, background=background) - A.text((150, 110), help_str) - A.paste(_background, alpha=True) - A.save(admin_help_image) - logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令') - - +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH +from services.log import logger +from utils.utils import get_matchers +from utils.manager import group_manager +from nonebot.adapters.cqhttp import Bot +from pathlib import Path +from nonebot import Driver +import asyncio +import nonebot + + +driver: Driver = nonebot.get_driver() + +background = Path(IMAGE_PATH) / "background" / "0.png" + +admin_help_image = Path(IMAGE_PATH) / 'admin_help_img.png' + + +@driver.on_bot_connect +async def init_task(bot: Bot = None): + if not group_manager.get_task_data(): + await group_manager.init_group_task() + logger.info(f'已成功加载 {len(group_manager.get_task_data())} 个被动技能.') + + +async def create_help_image(): + """ + 创建管理员帮助图片 + """ + await asyncio.get_event_loop().run_in_executor( + None, _create_help_image + ) + + +def _create_help_image(): + """ + 创建管理员帮助图片 + """ + _matchers = get_matchers() + _plugin_name_list = [] + width = 0 + _plugin_level = {} + for matcher in _matchers: + _plugin = nonebot.plugin.get_plugin(matcher.module) + _module = _plugin.module + try: + plugin_name = _module.__getattribute__("__zx_plugin_name__") + except AttributeError: + continue + try: + if ( + "[admin]" in plugin_name.lower() + and plugin_name not in _plugin_name_list + and plugin_name != "管理帮助 [Admin]" + ): + _plugin_name_list.append(plugin_name) + plugin_settings = _module.__getattribute__("__plugin_settings__") + plugin_des = _module.__getattribute__("__plugin_des__") + plugin_cmd = _module.__getattribute__("__plugin_cmd__") + plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x] + admin_level = int(plugin_settings["admin_level"]) + if _plugin_level.get(admin_level): + _plugin_level[admin_level].append( + f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd) + ) + else: + _plugin_level[admin_level] = [ + f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd) + ] + x = len(f"[{admin_level}] {plugin_des} -> " + " / ".join(plugin_cmd)) * 23 + width = width if width > x else x + except AttributeError: + logger.warning(f"获取管理插件 {matcher.module}: {plugin_name} 设置失败...") + help_str = "* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" \ + "[权限等级] 管理员帮助:\n\n" + x = list(_plugin_level.keys()) + x.sort() + for level in x: + for help_ in _plugin_level[level]: + help_str += f"\t{help_}\n\n" + help_str += '-----[被动技能开关]-----\n\n' + task_data = group_manager.get_task_data() + for i, x in enumerate(task_data.keys()): + help_str += f'{i+1}.开启/关闭{task_data[x]}\n\n' + height = len(help_str.split("\n")) * 33 + A = CreateImg(width, height, font_size=24) + _background = CreateImg(width, height, background=background) + A.text((150, 110), help_str) + A.paste(_background, alpha=True) + A.save(admin_help_image) + logger.info(f'已成功加载 {len(_plugin_name_list)} 条管理员命令') + + diff --git a/basic_plugins/apscheduler/__init__.py b/basic_plugins/apscheduler/__init__.py old mode 100644 new mode 100755 index 5eb8463b..a12aa80e --- a/basic_plugins/apscheduler/__init__.py +++ b/basic_plugins/apscheduler/__init__.py @@ -1,195 +1,195 @@ -from utils.message_builder import image -from utils.utils import scheduler, get_bot -from nonebot import on_message -from services.log import logger -from models.group_info import GroupInfo -from models.friend_user import FriendUser -from nonebot.adapters.cqhttp.exception import ActionFailed -from configs.config import NICKNAME -from utils.manager import group_manager - -__zx_plugin_name__ = "定时任务相关 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_task__ = {'zwa': '早晚安'} - - -x = on_message(priority=9, block=False) - - -# 早上好 -@scheduler.scheduled_job( - "cron", - hour=6, - minute=1, -) -async def _(): - try: - bot = get_bot() - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - for g in gl: - if await group_manager.check_group_task_status(g, 'zwa'): - result = image("zao.jpg", "zhenxun") - try: - await bot.send_group_msg(group_id=g, message="早上好" + result) - except ActionFailed: - logger.warning(f"{g} 群被禁言中,无法发送早安") - except Exception as e: - logger.error(f"早晚安错误 e:{e}") - - -# 睡觉了 -@scheduler.scheduled_job( - "cron", - hour=23, - minute=59, -) -async def _(): - try: - bot = get_bot() - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - for g in gl: - if await group_manager.check_group_task_status(g, 'zwa'): - result = image("sleep.jpg", "zhenxun") - try: - await bot.send_group_msg( - group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result - ) - except ActionFailed: - logger.warning(f"{g} 群被禁言中,无法发送晚安") - except Exception as e: - logger.error(f"早晚安错误 e:{e}") - - -# 自动更新群组信息 -@scheduler.scheduled_job( - "cron", - hour=3, - minute=1, -) -async def _(): - try: - bot = get_bot() - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - for g in gl: - group_info = await bot.get_group_info(group_id=g) - await GroupInfo.add_group_info( - group_info["group_id"], - group_info["group_name"], - group_info["max_member_count"], - group_info["member_count"], - ) - logger.info(f"自动更新群组 {g} 信息成功") - except Exception as e: - logger.error(f"自动更新群组信息错误 e:{e}") - - -# 自动更新好友信息 -@scheduler.scheduled_job( - "cron", - hour=3, - minute=1, -) -async def _(): - try: - bot = get_bot() - fl = await bot.get_friend_list() - for f in fl: - if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): - logger.info(f'自动更新好友 {f["user_id"]} 信息成功') - else: - logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') - except Exception as e: - logger.error(f"自动更新群组信息错误 e:{e}") - - - # 一次性任务 -# 固定时间触发,仅触发一次: -# -# from datetime import datetime -# -# @nonebot.scheduler.scheduled_job( -# 'date', -# run_date=datetime(2021, 1, 1, 0, 0), -# # timezone=None, -# ) -# async def _(): -# await bot.send_group_msg(group_id=123456, -# message="2021,新年快乐!") - -# 定期任务 -# 从 start_date 开始到 end_date 结束,根据类似 Cron -# -# 的规则触发任务: -# -# @nonebot.scheduler.scheduled_job( -# 'cron', -# # year=None, -# # month=None, -# # day=None, -# # week=None, -# day_of_week="mon,tue,wed,thu,fri", -# hour=7, -# # minute=None, -# # second=None, -# # start_date=None, -# # end_date=None, -# # timezone=None, -# ) -# async def _(): -# await bot.send_group_msg(group_id=123456, -# message="起床啦!") - -# 间隔任务 -# -# interval 触发器 -# -# 从 start_date 开始,每间隔一段时间触发,到 end_date 结束: -# -# @nonebot.scheduler.scheduled_job( -# 'interval', -# # weeks=0, -# # days=0, -# # hours=0, -# minutes=5, -# # seconds=0, -# # start_date=time.now(), -# # end_date=None, -# ) -# async def _(): -# has_new_item = check_new_item() -# if has_new_item: -# await bot.send_group_msg(group_id=123456, -# message="XX有更新啦!") - - -# 动态的计划任务 -# import datetime -# -# from apscheduler.triggers.date import DateTrigger # 一次性触发器 -# # from apscheduler.triggers.cron import CronTrigger # 定期触发器 -# # from apscheduler.triggers.interval import IntervalTrigger # 间隔触发器 -# from nonebot import on_command, scheduler -# -# @on_command('赖床') -# async def _(session: CommandSession): -# await session.send('我会在5分钟后再喊你') -# -# # 制作一个“5分钟后”触发器 -# delta = datetime.timedelta(minutes=5) -# trigger = DateTrigger( -# run_date=datetime.datetime.now() + delta -# ) -# -# # 添加任务 -# scheduler.add_job( -# func=session.send, # 要添加任务的函数,不要带参数 -# trigger=trigger, # 触发器 -# args=('不要再赖床啦!',), # 函数的参数列表,注意:只有一个值时,不能省略末尾的逗号 -# # kwargs=None, -# misfire_grace_time=60, # 允许的误差时间,建议不要省略 -# # jobstore='default', # 任务储存库,在下一小节中说明 -# ) +from utils.message_builder import image +from utils.utils import scheduler, get_bot +from nonebot import on_message +from services.log import logger +from models.group_info import GroupInfo +from models.friend_user import FriendUser +from nonebot.adapters.cqhttp.exception import ActionFailed +from configs.config import NICKNAME +from utils.manager import group_manager + +__zx_plugin_name__ = "定时任务相关 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_task__ = {'zwa': '早晚安'} + + +x = on_message(priority=9, block=False) + + +# 早上好 +@scheduler.scheduled_job( + "cron", + hour=6, + minute=1, +) +async def _(): + try: + bot = get_bot() + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + for g in gl: + if await group_manager.check_group_task_status(g, 'zwa'): + result = image("zao.jpg", "zhenxun") + try: + await bot.send_group_msg(group_id=g, message="早上好" + result) + except ActionFailed: + logger.warning(f"{g} 群被禁言中,无法发送早安") + except Exception as e: + logger.error(f"早晚安错误 e:{e}") + + +# 睡觉了 +@scheduler.scheduled_job( + "cron", + hour=23, + minute=59, +) +async def _(): + try: + bot = get_bot() + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + for g in gl: + if await group_manager.check_group_task_status(g, 'zwa'): + result = image("sleep.jpg", "zhenxun") + try: + await bot.send_group_msg( + group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result + ) + except ActionFailed: + logger.warning(f"{g} 群被禁言中,无法发送晚安") + except Exception as e: + logger.error(f"早晚安错误 e:{e}") + + +# 自动更新群组信息 +@scheduler.scheduled_job( + "cron", + hour=3, + minute=1, +) +async def _(): + try: + bot = get_bot() + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + for g in gl: + group_info = await bot.get_group_info(group_id=g) + await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + ) + logger.info(f"自动更新群组 {g} 信息成功") + except Exception as e: + logger.error(f"自动更新群组信息错误 e:{e}") + + +# 自动更新好友信息 +@scheduler.scheduled_job( + "cron", + hour=3, + minute=1, +) +async def _(): + try: + bot = get_bot() + fl = await bot.get_friend_list() + for f in fl: + if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): + logger.info(f'自动更新好友 {f["user_id"]} 信息成功') + else: + logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') + except Exception as e: + logger.error(f"自动更新群组信息错误 e:{e}") + + + # 一次性任务 +# 固定时间触发,仅触发一次: +# +# from datetime import datetime +# +# @nonebot.scheduler.scheduled_job( +# 'date', +# run_date=datetime(2021, 1, 1, 0, 0), +# # timezone=None, +# ) +# async def _(): +# await bot.send_group_msg(group_id=123456, +# message="2021,新年快乐!") + +# 定期任务 +# 从 start_date 开始到 end_date 结束,根据类似 Cron +# +# 的规则触发任务: +# +# @nonebot.scheduler.scheduled_job( +# 'cron', +# # year=None, +# # month=None, +# # day=None, +# # week=None, +# day_of_week="mon,tue,wed,thu,fri", +# hour=7, +# # minute=None, +# # second=None, +# # start_date=None, +# # end_date=None, +# # timezone=None, +# ) +# async def _(): +# await bot.send_group_msg(group_id=123456, +# message="起床啦!") + +# 间隔任务 +# +# interval 触发器 +# +# 从 start_date 开始,每间隔一段时间触发,到 end_date 结束: +# +# @nonebot.scheduler.scheduled_job( +# 'interval', +# # weeks=0, +# # days=0, +# # hours=0, +# minutes=5, +# # seconds=0, +# # start_date=time.now(), +# # end_date=None, +# ) +# async def _(): +# has_new_item = check_new_item() +# if has_new_item: +# await bot.send_group_msg(group_id=123456, +# message="XX有更新啦!") + + +# 动态的计划任务 +# import datetime +# +# from apscheduler.triggers.date import DateTrigger # 一次性触发器 +# # from apscheduler.triggers.cron import CronTrigger # 定期触发器 +# # from apscheduler.triggers.interval import IntervalTrigger # 间隔触发器 +# from nonebot import on_command, scheduler +# +# @on_command('赖床') +# async def _(session: CommandSession): +# await session.send('我会在5分钟后再喊你') +# +# # 制作一个“5分钟后”触发器 +# delta = datetime.timedelta(minutes=5) +# trigger = DateTrigger( +# run_date=datetime.datetime.now() + delta +# ) +# +# # 添加任务 +# scheduler.add_job( +# func=session.send, # 要添加任务的函数,不要带参数 +# trigger=trigger, # 触发器 +# args=('不要再赖床啦!',), # 函数的参数列表,注意:只有一个值时,不能省略末尾的逗号 +# # kwargs=None, +# misfire_grace_time=60, # 允许的误差时间,建议不要省略 +# # jobstore='default', # 任务储存库,在下一小节中说明 +# ) diff --git a/basic_plugins/ban/__init__.py b/basic_plugins/ban/__init__.py old mode 100644 new mode 100755 index 7592c44a..a9f6f567 --- a/basic_plugins/ban/__init__.py +++ b/basic_plugins/ban/__init__.py @@ -1,197 +1,207 @@ -from nonebot import on_command -from models.ban_user import BanUser -from models.level_user import LevelUser -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot -from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent -from utils.utils import get_message_at, get_message_text, is_number -from configs.config import NICKNAME, Config -from nonebot.permission import SUPERUSER -from services.log import logger - - -__zx_plugin_name__ = "封禁Ban用户 [Admin]" -__plugin_usage__ = """ -usage: - 将用户拉入或拉出黑名单 - 指令: - .ban [at] ?[小时] ?[分钟] - .unban - 示例:.ban @user - 示例:.ban @user 6 - 示例:.ban @user 3 10 - 示例:.unban @user -""".strip() -__plugin_superuser_usage__ = """ -usage: - 屏蔽用户消息,相当于最上级.ban - 指令: - b了 [at] - 示例:b了 @user -""".strip() -__plugin_des__ = '你被逮捕了!丢进小黑屋!' -__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]'] -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' -__plugin_settings__ = { - "admin_level": Config.get_config("ban", "BAN_LEVEL"), - "cmd": ['.ban', '.unban', 'ban', 'unban'] -} -__plugin_configs__ = { - "BAN_LEVEL [LEVEL]": { - "value": 5, - "help": "ban/unban所需要的管理员权限等级", - "default_value": 5 - } -} - - -ban = on_command( - ".ban", - aliases={".unban", "/ban", "/unban"}, - priority=5, - block=True, -) - -super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True) - - -@ban.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - result = "" - qq = get_message_at(event.json()) - if qq: - qq = qq[0] - user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) - user_name = user_name['card'] if user_name['card'] else user_name['nickname'] - msg = get_message_text(event.json()) - if msg: - msg = msg.split() - if len(msg) == 2: - if not is_number(msg[0].strip()) or not is_number(msg[1].strip()): - await ban.finish("参数必须是数字!", at_sender=True) - time = int(msg[0]) * 60 * 60 + int(msg[1]) * 60 - else: - if not is_number(msg[0].strip()): - await ban.finish("参数必须是数字!", at_sender=True) - time = int(msg[0]) * 60 * 60 - else: - time = -1 - if state["_prefix"]["raw_command"] in [".ban", "/ban"]: - if ( - await LevelUser.get_user_level(event.user_id, event.group_id) - <= await LevelUser.get_user_level(qq, event.group_id) - and str(event.user_id) not in bot.config.superusers - ): - await ban.finish( - f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!", - at_sender=True, - ) - if await BanUser.ban( - qq, await LevelUser.get_user_level(event.user_id, event.group_id), time - ): - logger.info( - f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟" - ) - result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!" - if time != -1: - result += f"将在 {time/60} 分钟后解封" - else: - time = await BanUser.check_ban_time(qq) - if is_number(time): - time = abs(int(time)) - if time < 60: - time = str(time) + " 秒" - else: - time = str(int(time / 60)) + " 分钟" - else: - time += " 分钟" - result = f"{user_name} 已在黑名单!预计 {time}后解封" - else: - if ( - await BanUser.check_ban_level( - qq, await LevelUser.get_user_level(event.user_id, event.group_id) - ) - and str(event.user_id) not in bot.config.superusers - ): - await ban.finish( - f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True - ) - if await BanUser.unban(qq): - logger.info( - f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 解禁" - ) - result = f"已经把 {user_name} 从黑名单中删除了!" - else: - result = f"{user_name} 不在黑名单!" - else: - await ban.finish("艾特人了吗??", at_sender=True) - await ban.finish(result, at_sender=True) - - -@ban.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - if str(event.user_id) in bot.config.superusers: - msg = get_message_text(event.json()) - msg = msg.split() - if is_number(msg[0]): - qq = int(msg[0]) - if state["_prefix"]["raw_command"] in [".ban", "/ban"]: - hour = 0 - minute = 0 - if len(msg) > 1 and is_number(msg[1]): - hour = int(msg[1]) - if len(msg) > 2 and is_number(msg[2]): - minute = int(msg[2]) - time = hour * 60 * 60 + minute * 60 - time = time if time else -1 - if await BanUser.ban( - qq, 9, time - ): - logger.info( - f"USER {event.user_id} 将 USER {qq} 封禁 时长 {time/60} 分钟" - ) - result = f"已经将 {qq} 加入{NICKNAME}的黑名单了!" - if time != -1: - result += f"将在 {time/60} 分钟后解封" - else: - result += f"将在 ∞ 分钟后解封" - await ban.send(result) - else: - time = await BanUser.check_ban_time(qq) - if is_number(time): - time = abs(int(time)) - if time < 60: - time = str(time) + " 秒" - else: - time = str(int(time / 60)) + " 分钟" - else: - time += " 分钟" - await ban.send(f"{qq} 已在黑名单!预计 {time}后解封") - else: - if await BanUser.unban(qq): - logger.info( - f"USER {event.user_id} 将 USER {qq} 解禁" - ) - result = f"已经把 {qq} 从黑名单中删除了!" - else: - result = f"{qq} 不在黑名单!" - await ban.send(result) - else: - await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True) - - -@super_ban.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - qq = get_message_at(event.json()) - if qq: - qq = qq[0] - user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) - user_name = user['card'] if user['card'] else user['nickname'] - if not await BanUser.ban(qq, 10, 99999999): - await BanUser.unban(qq) - await BanUser.ban(qq, 10, 99999999) - await ban.send(f"已将 {user_name} 拉入黑名单!") - else: - await super_ban.send('需要艾特被super ban的对象..') - +from nonebot import on_command +from models.ban_user import BanUser +from models.level_user import LevelUser +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot +from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent, MessageEvent +from utils.utils import get_message_at, get_message_text, is_number +from configs.config import NICKNAME, Config +from nonebot.permission import SUPERUSER +from services.log import logger + + +__zx_plugin_name__ = "封禁Ban用户 [Admin]" +__plugin_usage__ = """ +usage: + 将用户拉入或拉出黑名单 + 指令: + .ban [at] ?[小时] ?[分钟] + .unban + 示例:.ban @user + 示例:.ban @user 6 + 示例:.ban @user 3 10 + 示例:.unban @user +""".strip() +__plugin_superuser_usage__ = """ +usage: + b了=屏蔽用户消息,相当于最上级.ban + 跨群ban以及跨群b了 + 指令: + b了 [at/qq] + .ban [user_id] ?[小时] ?[分钟] + 示例:b了 @user + 示例:b了 1234567 + 示例:.ban 12345567 +""".strip() +__plugin_des__ = '你被逮捕了!丢进小黑屋!' +__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]'] +__plugin_version__ = 0.1 +__plugin_author__ = 'HibiKier' +__plugin_settings__ = { + "admin_level": Config.get_config("ban", "BAN_LEVEL"), + "cmd": ['.ban', '.unban', 'ban', 'unban'] +} +__plugin_configs__ = { + "BAN_LEVEL [LEVEL]": { + "value": 5, + "help": "ban/unban所需要的管理员权限等级", + "default_value": 5 + } +} + + +ban = on_command( + ".ban", + aliases={".unban", "/ban", "/unban"}, + priority=5, + block=True, +) + +super_ban = on_command('b了', permission=SUPERUSER, priority=5, block=True) + + +@ban.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + result = "" + qq = get_message_at(event.json()) + if qq: + qq = qq[0] + user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) + user_name = user_name['card'] if user_name['card'] else user_name['nickname'] + msg = get_message_text(event.json()) + if msg: + msg = msg.split() + if len(msg) == 2: + if not is_number(msg[0].strip()) or not is_number(msg[1].strip()): + await ban.finish("参数必须是数字!", at_sender=True) + time = int(msg[0]) * 60 * 60 + int(msg[1]) * 60 + else: + if not is_number(msg[0].strip()): + await ban.finish("参数必须是数字!", at_sender=True) + time = int(msg[0]) * 60 * 60 + else: + time = -1 + if state["_prefix"]["raw_command"] in [".ban", "/ban"]: + if ( + await LevelUser.get_user_level(event.user_id, event.group_id) + <= await LevelUser.get_user_level(qq, event.group_id) + and str(event.user_id) not in bot.config.superusers + ): + await ban.finish( + f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!", + at_sender=True, + ) + if await BanUser.ban( + qq, await LevelUser.get_user_level(event.user_id, event.group_id), time + ): + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟" + ) + result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!" + if time != -1: + result += f"将在 {time/60} 分钟后解封" + else: + time = await BanUser.check_ban_time(qq) + if is_number(time): + time = abs(int(time)) + if time < 60: + time = str(time) + " 秒" + else: + time = str(int(time / 60)) + " 分钟" + else: + time += " 分钟" + result = f"{user_name} 已在黑名单!预计 {time}后解封" + else: + if ( + await BanUser.check_ban_level( + qq, await LevelUser.get_user_level(event.user_id, event.group_id) + ) + and str(event.user_id) not in bot.config.superusers + ): + await ban.finish( + f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True + ) + if await BanUser.unban(qq): + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 解禁" + ) + result = f"已经把 {user_name} 从黑名单中删除了!" + else: + result = f"{user_name} 不在黑名单!" + else: + await ban.finish("艾特人了吗??", at_sender=True) + await ban.finish(result, at_sender=True) + + +@ban.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + if str(event.user_id) in bot.config.superusers: + msg = get_message_text(event.json()) + msg = msg.split() + if is_number(msg[0]): + qq = int(msg[0]) + if state["_prefix"]["raw_command"] in [".ban", "/ban"]: + hour = 0 + minute = 0 + if len(msg) > 1 and is_number(msg[1]): + hour = int(msg[1]) + if len(msg) > 2 and is_number(msg[2]): + minute = int(msg[2]) + time = hour * 60 * 60 + minute * 60 + time = time if time else -1 + if await BanUser.ban( + qq, 9, time + ): + logger.info( + f"USER {event.user_id} 将 USER {qq} 封禁 时长 {time/60} 分钟" + ) + result = f"已经将 {qq} 加入{NICKNAME}的黑名单了!" + if time != -1: + result += f"将在 {time/60} 分钟后解封" + else: + result += f"将在 ∞ 分钟后解封" + await ban.send(result) + else: + time = await BanUser.check_ban_time(qq) + if is_number(time): + time = abs(int(time)) + if time < 60: + time = str(time) + " 秒" + else: + time = str(int(time / 60)) + " 分钟" + else: + time += " 分钟" + await ban.send(f"{qq} 已在黑名单!预计 {time}后解封") + else: + if await BanUser.unban(qq): + logger.info( + f"USER {event.user_id} 将 USER {qq} 解禁" + ) + result = f"已经把 {qq} 从黑名单中删除了!" + else: + result = f"{qq} 不在黑名单!" + await ban.send(result) + else: + await ban.finish('qq号必须是数字!\n格式:.ban [qq] [hour]? [minute]?', at_sender=True) + + +@super_ban.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if isinstance(event, GroupMessageEvent): + qq = get_message_at(event.json()) + else: + qq = get_message_text(event.json()) + if not is_number(qq): + await super_ban.finish("对象qq必须为纯数字...") + qq = [qq] + if qq: + qq = qq[0] + user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) + user_name = user['card'] if user['card'] else user['nickname'] + if not await BanUser.ban(qq, 10, 99999999): + await BanUser.unban(qq) + await BanUser.ban(qq, 10, 99999999) + await ban.send(f"已将 {user_name} 拉入黑名单!") + else: + await super_ban.send('需要艾特被super ban的对象..') + diff --git a/basic_plugins/broadcast/__init__.py b/basic_plugins/broadcast/__init__.py old mode 100644 new mode 100755 index b97d2a6c..328d73c7 --- a/basic_plugins/broadcast/__init__.py +++ b/basic_plugins/broadcast/__init__.py @@ -1,59 +1,59 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters import Bot, Event -from nonebot.permission import SUPERUSER -import asyncio -from utils.utils import get_message_text, get_message_imgs -from services.log import logger -from utils.message_builder import image -from utils.manager import group_manager - -__zx_plugin_name__ = "广播 [Superuser]" -__plugin_usage__ = """ -usage: - 指令: - 广播- ?[消息] ?[图片] - 示例:广播- 你们好! -""".strip() -__plugin_des__ = "昭告天下!" -__plugin_cmd__ = ["广播-"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_task__ = {"broadcast": "广播"} - -broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True) - - -@broadcast.handle() -async def _(bot: Bot, event: Event, state: T_State): - msg = get_message_text(event.json()) - imgs = get_message_imgs(event.json()) - rst = "" - for img in imgs: - rst += image(img) - sid = bot.self_id - gl = await bot.get_group_list(self_id=sid) - gl = [ - g["group_id"] - for g in gl - if await group_manager.check_group_task_status(g["group_id"], "broadcast") - ] - g_cnt = len(gl) - cnt = 0 - error = "" - x = 0.25 - for g in gl: - cnt += 1 - if cnt / g_cnt > x: - await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊") - x += 0.25 - try: - await bot.send_group_msg(self_id=sid, group_id=g, message=msg + rst) - logger.info(f"GROUP {g} 投递广播成功") - except Exception as e: - logger.error(f"GROUP {g} 投递广播失败:{type(e)}") - error += f"GROUP {g} 投递广播失败:{type(e)}\n" - await asyncio.sleep(0.5) - await broadcast.send(f"已播报至 100% 的群聊") - if error: - await broadcast.send(f"播报时错误:{error}") +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters import Bot, Event +from nonebot.permission import SUPERUSER +import asyncio +from utils.utils import get_message_text, get_message_imgs +from services.log import logger +from utils.message_builder import image +from utils.manager import group_manager + +__zx_plugin_name__ = "广播 [Superuser]" +__plugin_usage__ = """ +usage: + 指令: + 广播- ?[消息] ?[图片] + 示例:广播- 你们好! +""".strip() +__plugin_des__ = "昭告天下!" +__plugin_cmd__ = ["广播-"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_task__ = {"broadcast": "广播"} + +broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True) + + +@broadcast.handle() +async def _(bot: Bot, event: Event, state: T_State): + msg = get_message_text(event.json()) + imgs = get_message_imgs(event.json()) + rst = "" + for img in imgs: + rst += image(img) + sid = bot.self_id + gl = await bot.get_group_list(self_id=sid) + gl = [ + g["group_id"] + for g in gl + if await group_manager.check_group_task_status(g["group_id"], "broadcast") + ] + g_cnt = len(gl) + cnt = 0 + error = "" + x = 0.25 + for g in gl: + cnt += 1 + if cnt / g_cnt > x: + await broadcast.send(f"已播报至 {int(cnt / g_cnt * 100)}% 的群聊") + x += 0.25 + try: + await bot.send_group_msg(self_id=sid, group_id=g, message=msg + rst) + logger.info(f"GROUP {g} 投递广播成功") + except Exception as e: + logger.error(f"GROUP {g} 投递广播失败:{type(e)}") + error += f"GROUP {g} 投递广播失败:{type(e)}\n" + await asyncio.sleep(0.5) + await broadcast.send(f"已播报至 100% 的群聊") + if error: + await broadcast.send(f"播报时错误:{error}") diff --git a/basic_plugins/group_handle/__init__.py b/basic_plugins/group_handle/__init__.py old mode 100644 new mode 100755 index d83ae817..ff390d09 --- a/basic_plugins/group_handle/__init__.py +++ b/basic_plugins/group_handle/__init__.py @@ -1,184 +1,184 @@ -from nonebot import on_notice, on_request -from configs.path_config import IMAGE_PATH, DATA_PATH -from utils.message_builder import image -from models.group_member_info import GroupInfoUser -from datetime import datetime -from services.log import logger -from nonebot.adapters.cqhttp import ( - Bot, - GroupIncreaseNoticeEvent, - GroupDecreaseNoticeEvent, -) -from nonebot.adapters.cqhttp.exception import ActionFailed -from utils.manager import group_manager, plugins2settings_manager, requests_manager -from configs.config import NICKNAME -from models.group_info import GroupInfo -from utils.utils import FreqLimiter -from configs.config import Config -from pathlib import Path -import random -import os - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -__zx_plugin_name__ = "群事件处理 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"} -Config.add_plugin_config( - "auto_invite", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.." -) -Config.add_plugin_config( - "auto_invite", "flag", True, help_="被强制拉群后是否直接退出", default_value=True -) -Config.add_plugin_config( - "auto_invite", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5 -) -_flmt = FreqLimiter(Config.get_config("auto_invite", "welcome_msg_cd")) - - -# 群员增加处理 -group_increase_handle = on_notice(priority=1, block=False) -# 群员减少处理 -group_decrease_handle = on_notice(priority=1, block=False) -# (群管理)加群同意请求 -add_group = on_request(priority=1, block=False) - - -@group_increase_handle.handle() -async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): - if event.user_id == int(bot.self_id): - group = await GroupInfo.get_group_info(event.group_id) - # 群聊不存在或被强制拉群,退出该群 - if (not group or group.group_flag == 0) and Config.get_config( - "auto_invite", "flag" - ): - try: - msg = Config.get_config("auto_invite", "message") - if msg: - await bot.send_group_msg(group_id=event.group_id, message=msg) - await bot.set_group_leave(group_id=event.group_id) - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"触发强制入群保护,已成功退出群聊 {event.group_id}..", - ) - logger.info(f"强制拉群或未有群信息,退出群聊 {group} 成功") - requests_manager.remove_request("group", event.group_id) - except Exception as e: - logger.info(f"强制拉群或未有群信息,退出群聊 {group} 失败 e:{e}") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"触发强制入群保护,退出群聊 {event.group_id} 失败..", - ) - # 默认群功能开关 - elif event.group_id not in group_manager["group_manager"].keys(): - data = plugins2settings_manager.get_data() - for plugin in data.keys(): - if not data[plugin]["default_status"]: - group_manager.block_plugin(plugin, event.group_id) - else: - join_time = datetime.now() - user_info = await bot.get_group_member_info( - group_id=event.group_id, user_id=event.user_id - ) - if await GroupInfoUser.add_member_info( - user_info["user_id"], - user_info["group_id"], - user_info["nickname"], - join_time, - ): - logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") - else: - logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") - - # 群欢迎消息 - if await group_manager.check_group_task_status( - event.group_id, "group_welcome" - ) and _flmt.check(event.group_id): - _flmt.start_cd(event.group_id) - msg = "" - img = "" - at_flag = False - custom_welcome_msg_json = ( - Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" - ) - if custom_welcome_msg_json.exists(): - data = json.load(open(custom_welcome_msg_json, "r")) - if data.get(str(event.group_id)): - msg = data[str(event.group_id)] - if msg.find("[at]") != -1: - msg = msg.replace("[at]", "") - at_flag = True - if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"): - img = image( - abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg" - ) - if msg or img: - await group_increase_handle.send( - "\n" + msg.strip() + img, at_sender=at_flag - ) - else: - await group_increase_handle.send( - "新人快跑啊!!本群现状↓(快使用自定义!)" - + image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz") - ) - - -@group_decrease_handle.handle() -async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict): - # 被踢出群 - if event.sub_type == "kick_me": - group_id = event.group_id - operator_id = event.operator_id - try: - operator_name = ( - await GroupInfoUser.get_member_info(event.operator_id, event.group_id) - ).user_name - except AttributeError: - operator_name = "None" - group = await GroupInfo.get_group_info(group_id) - group_name = group.group_name if group else "" - coffee = int(list(bot.config.superusers)[0]) - await bot.send_private_msg( - user_id=coffee, - message=f"****呜..一份踢出报告****\n" - f"我被 {operator_name}({operator_id})\n" - f"踢出了 {group_name}({group_id})\n" - f"日期:{str(datetime.now()).split('.')[0]}", - ) - return - if event.user_id == int(bot.self_id): - group_manager.delete_group(event.group_id) - return - try: - user_name = ( - await GroupInfoUser.get_member_info(event.user_id, event.group_id) - ).user_name - except AttributeError: - user_name = str(event.user_id) - if await GroupInfoUser.delete_member_info(event.user_id, event.group_id): - logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功") - else: - logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败") - if await group_manager.check_group_task_status( - event.group_id, "refund_group_remind" - ): - rst = "" - if event.sub_type == "leave": - rst = f"{user_name}离开了我们..." - if event.sub_type == "kick": - operator = await bot.get_group_member_info( - user_id=event.operator_id, group_id=event.group_id - ) - operator_name = ( - operator["card"] if operator["card"] else operator["nickname"] - ) - rst = f"{user_name} 被 {operator_name} 送走了." - try: - await group_decrease_handle.send(f"{rst}") - except ActionFailed: - return +from nonebot import on_notice, on_request +from configs.path_config import IMAGE_PATH, DATA_PATH +from utils.message_builder import image +from models.group_member_info import GroupInfoUser +from datetime import datetime +from services.log import logger +from nonebot.adapters.cqhttp import ( + Bot, + GroupIncreaseNoticeEvent, + GroupDecreaseNoticeEvent, +) +from nonebot.adapters.cqhttp.exception import ActionFailed +from utils.manager import group_manager, plugins2settings_manager, requests_manager +from configs.config import NICKNAME +from models.group_info import GroupInfo +from utils.utils import FreqLimiter +from configs.config import Config +from pathlib import Path +import random +import os + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +__zx_plugin_name__ = "群事件处理 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"} +Config.add_plugin_config( + "invite_manager", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.." +) +Config.add_plugin_config( + "invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True +) +Config.add_plugin_config( + "invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5 +) +_flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd")) + + +# 群员增加处理 +group_increase_handle = on_notice(priority=1, block=False) +# 群员减少处理 +group_decrease_handle = on_notice(priority=1, block=False) +# (群管理)加群同意请求 +add_group = on_request(priority=1, block=False) + + +@group_increase_handle.handle() +async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): + if event.user_id == int(bot.self_id): + group = await GroupInfo.get_group_info(event.group_id) + # 群聊不存在或被强制拉群,退出该群 + if (not group or group.group_flag == 0) and Config.get_config( + "invite_manager", "flag" + ): + try: + msg = Config.get_config("invite_manager", "message") + if msg: + await bot.send_group_msg(group_id=event.group_id, message=msg) + await bot.set_group_leave(group_id=event.group_id) + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"触发强制入群保护,已成功退出群聊 {event.group_id}..", + ) + logger.info(f"强制拉群或未有群信息,退出群聊 {group} 成功") + requests_manager.remove_request("group", event.group_id) + except Exception as e: + logger.info(f"强制拉群或未有群信息,退出群聊 {group} 失败 e:{e}") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"触发强制入群保护,退出群聊 {event.group_id} 失败..", + ) + # 默认群功能开关 + elif event.group_id not in group_manager["group_manager"].keys(): + data = plugins2settings_manager.get_data() + for plugin in data.keys(): + if not data[plugin]["default_status"]: + group_manager.block_plugin(plugin, event.group_id) + else: + join_time = datetime.now() + user_info = await bot.get_group_member_info( + group_id=event.group_id, user_id=event.user_id + ) + if await GroupInfoUser.add_member_info( + user_info["user_id"], + user_info["group_id"], + user_info["nickname"], + join_time, + ): + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") + else: + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") + + # 群欢迎消息 + if await group_manager.check_group_task_status( + event.group_id, "group_welcome" + ) and _flmt.check(event.group_id): + _flmt.start_cd(event.group_id) + msg = "" + img = "" + at_flag = False + custom_welcome_msg_json = ( + Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" + ) + if custom_welcome_msg_json.exists(): + data = json.load(open(custom_welcome_msg_json, "r")) + if data.get(str(event.group_id)): + msg = data[str(event.group_id)] + if msg.find("[at]") != -1: + msg = msg.replace("[at]", "") + at_flag = True + if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"): + img = image( + abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg" + ) + if msg or img: + await group_increase_handle.send( + "\n" + msg.strip() + img, at_sender=at_flag + ) + else: + await group_increase_handle.send( + "新人快跑啊!!本群现状↓(快使用自定义!)" + + image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz") + ) + + +@group_decrease_handle.handle() +async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict): + # 被踢出群 + if event.sub_type == "kick_me": + group_id = event.group_id + operator_id = event.operator_id + try: + operator_name = ( + await GroupInfoUser.get_member_info(event.operator_id, event.group_id) + ).user_name + except AttributeError: + operator_name = "None" + group = await GroupInfo.get_group_info(group_id) + group_name = group.group_name if group else "" + coffee = int(list(bot.config.superusers)[0]) + await bot.send_private_msg( + user_id=coffee, + message=f"****呜..一份踢出报告****\n" + f"我被 {operator_name}({operator_id})\n" + f"踢出了 {group_name}({group_id})\n" + f"日期:{str(datetime.now()).split('.')[0]}", + ) + return + if event.user_id == int(bot.self_id): + group_manager.delete_group(event.group_id) + return + try: + user_name = ( + await GroupInfoUser.get_member_info(event.user_id, event.group_id) + ).user_name + except AttributeError: + user_name = str(event.user_id) + if await GroupInfoUser.delete_member_info(event.user_id, event.group_id): + logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功") + else: + logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败") + if await group_manager.check_group_task_status( + event.group_id, "refund_group_remind" + ): + rst = "" + if event.sub_type == "leave": + rst = f"{user_name}离开了我们..." + if event.sub_type == "kick": + operator = await bot.get_group_member_info( + user_id=event.operator_id, group_id=event.group_id + ) + operator_name = ( + operator["card"] if operator["card"] else operator["nickname"] + ) + rst = f"{user_name} 被 {operator_name} 送走了." + try: + await group_decrease_handle.send(f"{rst}") + except ActionFailed: + return diff --git a/basic_plugins/help/__init__.py b/basic_plugins/help/__init__.py old mode 100644 new mode 100755 index da1ffd3c..b747cb05 --- a/basic_plugins/help/__init__.py +++ b/basic_plugins/help/__init__.py @@ -1,73 +1,73 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - GroupMessageEvent -) -from nonebot.typing import T_State -from nonebot.rule import to_me -from configs.path_config import IMAGE_PATH, DATA_PATH -from utils.message_builder import image -from .data_source import create_help_img, get_plugin_help -from utils.utils import get_message_text -from pathlib import Path -import os - - -__zx_plugin_name__ = "帮助" - -group_help_path = Path(DATA_PATH) / "group_help" -help_image = Path(IMAGE_PATH) / "help.png" -simple_help_image = Path(IMAGE_PATH) / "simple_help.png" -if help_image.exists(): - help_image.unlink() -if simple_help_image.exists(): - simple_help_image.unlink() -group_help_path.mkdir(exist_ok=True, parents=True) -for x in os.listdir(group_help_path): - group_help_image = group_help_path / x - group_help_image.unlink() - -_help = on_command("详细功能", rule=to_me(), aliases={"详细帮助"}, priority=1, block=True) -simple_help = on_command("功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True) - - -@_help.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - if not help_image.exists(): - if help_image.exists(): - help_image.unlink() - if simple_help_image.exists(): - simple_help_image.unlink() - await create_help_img(None, help_image, simple_help_image) - await _help.finish(image("help.png")) - - -@simple_help.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - is_super = False - if msg: - if '-super' in msg: - if str(event.user_id) in bot.config.superusers: - is_super = True - msg = msg.replace('-super', '').strip() - msg = get_plugin_help(msg, is_super) - if msg: - await _help.send(image(b64=msg)) - else: - await _help.send("没有此功能的帮助信息...") - else: - if isinstance(event, GroupMessageEvent): - _image_path = group_help_path / f"{event.group_id}.png" - if not _image_path.exists(): - await create_help_img(event.group_id, help_image, _image_path) - await simple_help.send(image(_image_path)) - else: - if not simple_help_image.exists(): - if help_image.exists(): - help_image.unlink() - if simple_help_image.exists(): - simple_help_image.unlink() - await create_help_img(None, help_image, simple_help_image) - await _help.finish(image("simple_help.png")) +from nonebot import on_command +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent +) +from nonebot.typing import T_State +from nonebot.rule import to_me +from configs.path_config import IMAGE_PATH, DATA_PATH +from utils.message_builder import image +from .data_source import create_help_img, get_plugin_help +from utils.utils import get_message_text +from pathlib import Path +import os + + +__zx_plugin_name__ = "帮助" + +group_help_path = Path(DATA_PATH) / "group_help" +help_image = Path(IMAGE_PATH) / "help.png" +simple_help_image = Path(IMAGE_PATH) / "simple_help.png" +if help_image.exists(): + help_image.unlink() +if simple_help_image.exists(): + simple_help_image.unlink() +group_help_path.mkdir(exist_ok=True, parents=True) +for x in os.listdir(group_help_path): + group_help_image = group_help_path / x + group_help_image.unlink() + +_help = on_command("详细功能", rule=to_me(), aliases={"详细帮助"}, priority=1, block=True) +simple_help = on_command("功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True) + + +@_help.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if not help_image.exists(): + if help_image.exists(): + help_image.unlink() + if simple_help_image.exists(): + simple_help_image.unlink() + await create_help_img(None, help_image, simple_help_image) + await _help.finish(image("help.png")) + + +@simple_help.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + is_super = False + if msg: + if '-super' in msg: + if str(event.user_id) in bot.config.superusers: + is_super = True + msg = msg.replace('-super', '').strip() + msg = get_plugin_help(msg, is_super) + if msg: + await _help.send(image(b64=msg)) + else: + await _help.send("没有此功能的帮助信息...") + else: + if isinstance(event, GroupMessageEvent): + _image_path = group_help_path / f"{event.group_id}.png" + if not _image_path.exists(): + await create_help_img(event.group_id, help_image, _image_path) + await simple_help.send(image(_image_path)) + else: + if not simple_help_image.exists(): + if help_image.exists(): + help_image.unlink() + if simple_help_image.exists(): + simple_help_image.unlink() + await create_help_img(None, help_image, simple_help_image) + await _help.finish(image("simple_help.png")) diff --git a/basic_plugins/help/data_source.py b/basic_plugins/help/data_source.py old mode 100644 new mode 100755 index 64d0276d..6168180d --- a/basic_plugins/help/data_source.py +++ b/basic_plugins/help/data_source.py @@ -1,367 +1,367 @@ -from utils.image_utils import CreateImg -from configs.path_config import IMAGE_PATH -from utils.manager import ( - plugins2settings_manager, - admin_manager, - plugins_manager, - group_manager, -) -from typing import Optional -from services.log import logger -from pathlib import Path -from utils.utils import get_matchers -import random -import asyncio -import nonebot -import os - - -random_bk_path = Path(IMAGE_PATH) / "background" / "help" / "simple_help" - -background = Path(IMAGE_PATH) / "background" / "0.png" - - -async def create_help_img( - group_id: Optional[int], help_image: Path, simple_help_image: Path -): - """ - 生成帮助图片 - :param group_id: 群号 - :param help_image: 图片路径 - :param simple_help_image: 简易帮助图片路径 - """ - return await asyncio.get_event_loop().run_in_executor( - None, _create_help_img, group_id, help_image, simple_help_image - ) - - -def _create_help_img( - group_id: Optional[int], help_image: Path, simple_help_image: Path -): - """ - 生成帮助图片 - :param group_id: 群号 - :param help_image: 图片路径 - :param simple_help_image: 简易帮助图片路径 - """ - _matchers = get_matchers() - width = 0 - matchers_data = {} - _des_tmp = {} - _plugin_name_tmp = [] - _tmp = [] - tmp_img = CreateImg(0, 0, plain_text="1", font_size=24) - font_height = tmp_img.h - # 插件分类 - for matcher in _matchers: - plugin_name = None - _plugin = nonebot.plugin.get_plugin(matcher.module) - _module = _plugin.module - try: - plugin_name = _module.__getattribute__("__zx_plugin_name__") - try: - plugin_des = _module.__getattribute__("__plugin_des__") - except AttributeError: - plugin_des = "_" - if ( - "[hidden]" in plugin_name.lower() - or "[admin]" in plugin_name.lower() - or "[superuser]" in plugin_name.lower() - or plugin_name in _plugin_name_tmp - or plugin_name == "帮助" - ): - continue - plugin_type = ("normal",) - text_type = 0 - if plugins2settings_manager.get( - matcher.module - ) and plugins2settings_manager[matcher.module].get("plugin_type"): - plugin_type = tuple( - plugins2settings_manager.get_plugin_data(matcher.module)[ - "plugin_type" - ] - ) - else: - try: - plugin_type = _module.__getattribute__("__plugin_type__") - except AttributeError: - pass - if len(plugin_type) > 1: - try: - text_type = int(plugin_type[1]) - except ValueError as e: - logger.warning(f"生成列向帮助排列失败 {plugin_name}: {type(e)}: {e}") - plugin_type = plugin_type[0] - else: - plugin_type = plugin_type[0] - try: - plugin_cmd = _module.__getattribute__("__plugin_cmd__") - plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x] - except AttributeError: - plugin_cmd = [] - if plugin_type not in matchers_data.keys(): - matchers_data[plugin_type] = {} - if plugin_des in _des_tmp.keys(): - try: - matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"] = ( - matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"] - + plugin_cmd - ) - except KeyError as e: - logger.warning(f"{type(e)}: {e}") - else: - matchers_data[plugin_type][plugin_name] = { - "module": matcher.module, - "des": plugin_des, - "cmd": plugin_cmd, - "text_type": text_type, - } - try: - if text_type == 0: - x = tmp_img.getsize( - f'{plugin_name}: {matchers_data[plugin_type][plugin_name]["des"]} ->' - + " / ".join(matchers_data[plugin_type][plugin_name]["cmd"]) - )[0] - width = width if width > x else x - except KeyError: - pass - if plugin_des not in _des_tmp: - _des_tmp[plugin_des] = plugin_name - except AttributeError as e: - if plugin_name not in _plugin_name_tmp: - logger.warning(f"获取功能 {matcher.module}: {plugin_name} 设置失败...e:{e}") - if plugin_name not in _plugin_name_tmp: - _plugin_name_tmp.append(plugin_name) - help_img_list = [] - simple_help_img_list = [] - types = list(matchers_data.keys()) - types.sort() - ix = 0 - # 详细帮助 - for type_ in types: - keys = list(matchers_data[type_].keys()) - keys.sort() - help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n" - simple_help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n" - for i, k in enumerate(keys): - # 禁用flag - flag = True - if plugins_manager.get_plugin_status( - matchers_data[type_][k]["module"], "all" - ): - flag = False - if group_id: - flag = flag and plugins_manager.get_plugin_status( - matchers_data[type_][k]["module"], "group" - ) - simple_help_str += ( - f"{i+1}.{k}<|_|~|>" - f"{group_manager.get_plugin_status(matchers_data[type_][k]['module'], group_id) if group_id else '_'}|" - f"{flag}\n" - ) - if matchers_data[type_][k]["text_type"] == 1: - _x = tmp_img.getsize( - f"{i+1}".rjust(5) - + f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} ' - )[0] - _str = ( - f"{i+1}".rjust(5) - + f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} ' - ) - _str += matchers_data[type_][k]["cmd"][0] + "\n" - for c in matchers_data[type_][k]["cmd"][1:]: - _str += "".rjust(int(_x * 0.125) + 1) + f"{c}\n" - help_str += _str - else: - help_str += ( - f"{i+1}".rjust(5) - + f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} ' - + " / ".join(matchers_data[type_][k]["cmd"]) - + "\n" - ) - height = len(help_str.split("\n")) * (font_height + 5) - simple_height = len(simple_help_str.split("\n")) * (font_height + 5) - A = CreateImg( - width + 150, height, font_size=24, color="white" if not ix % 2 else "black" - ) - A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0)) - # 生成各个分类的插件简易帮助图片 - simple_width = 0 - for x in [ - tmp_img.getsize(x.split("<|_|~|>")[0])[0] - for x in simple_help_str.split("\n") - ]: - simple_width = simple_width if simple_width > x else x - bk = CreateImg(simple_width + 20, simple_height, font_size=24, color="#6495ED") - B = CreateImg( - simple_width + 20, - simple_height, - font_size=24, - color="white" if not ix % 2 else "black", - ) - # 切分,判断插件开关状态 - _s_height = 10 - for _s in simple_help_str.split("\n"): - text_color = (255, 255, 255) if ix % 2 else (0, 0, 0) - _line_flag = False - if "<|_|~|>" in _s: - _x = _s.split("<|_|~|>") - _flag_sp = _x[-1].split("|") - if group_id: - if _flag_sp[0].lower() != "true": - text_color = (252, 75, 13) - if _flag_sp[1].lower() == "true": - _line_flag = True - _s = _x[0] - B.text((10, _s_height), _s, text_color) - if _line_flag: - B.line( - ( - 7, - _s_height + int(B.getsize(_s)[1] / 2) + 2, - B.getsize(_s)[0] + 11, - _s_height + int(B.getsize(_s)[1] / 2) + 2, - ), - (236, 66, 7), - 3, - ) - _s_height += B.getsize("1")[1] + 5 - # B.text((10, 10), simple_help_str, (255, 255, 255) if ix % 2 else (0, 0, 0)) - bk.paste(B, center_type="center") - bk.transparent(2) - ix += 1 - help_img_list.append(A) - simple_help_img_list.append(bk) - height = 0 - for img in help_img_list: - height += img.h - if not group_id: - A = CreateImg(width + 150, height + 50, font_size=24) - A.text( - (10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n' - ) - current_height = 50 - for img in help_img_list: - A.paste(img, (0, current_height)) - current_height += img.h - A.save(help_image) - # 详细帮助生成完毕 - # 简易帮助图片合成 - height = 0 - width = 0 - for img in simple_help_img_list: - if img.h > height: - height = img.h - width += img.w + 10 - B = CreateImg(width + 100, height + 250, font_size=24) - width, _ = get_max_width_or_paste(simple_help_img_list, B) - bk = None - random_bk = os.listdir(random_bk_path) - if random_bk: - bk = random.choice(random_bk) - x = max(width + 50, height + 250) - B = CreateImg( - x, - x, - font_size=24, - color="#FFEFD5", - background=random_bk_path / bk, - ) - B.filter("GaussianBlur", 10) - _, B = get_max_width_or_paste(simple_help_img_list, B, True) - w = 10 - h = 10 - for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]: - text = CreateImg( - 0, - 0, - plain_text=msg, - font_size=24, - font="yuanshen.ttf", - ) - B.paste(text, (w, h), True) - h += 50 - if msg == "目前支持的功能列表:": - w += 50 - B.paste( - CreateImg( - 0, - 0, - plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护", - font_size=24, - font="yuanshen.ttf", - font_color=(231, 74, 57) - ), - (300, 10), - True, - ) - B.save(simple_help_image) - - -def get_max_width_or_paste( - simple_help_img_list: list, B: CreateImg = None, is_paste: bool = False -) -> "int, CreateImg": - """ - 获取最大宽度,或直接贴图 - :param simple_help_img_list: 简单帮助图片列表 - :param B: 背景图 - :param is_paste: 是否直接贴图 - """ - current_width = 50 - current_height = 180 - max_width = simple_help_img_list[0].w - for i in range(len(simple_help_img_list)): - try: - if is_paste and B: - B.paste(simple_help_img_list[i], (current_width, current_height), True) - current_height += simple_help_img_list[i].h + 40 - if current_height + simple_help_img_list[i + 1].h > B.h - 10: - current_height = 180 - current_width += max_width + 30 - max_width = 0 - elif simple_help_img_list[i].w > max_width: - max_width = simple_help_img_list[i].w - except IndexError: - pass - if current_width > simple_help_img_list[0].w + 50: - current_width += simple_help_img_list[-1].w - return current_width, B - - -def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]: - """ - 获取功能的帮助信息 - :param msg: 功能cmd - :param is_super: 是否为超级用户 - """ - module = plugins2settings_manager.get_plugin_module(msg) - if not module: - module = admin_manager.get_plugin_module(msg) - if module: - try: - plugin = nonebot.plugin.get_plugin(module) - if plugin: - if is_super: - result = plugin.module.__getattribute__( - "__plugin_superuser_usage__" - ) - else: - result = plugin.module.__getattribute__("__plugin_usage__") - if result: - width = 0 - for x in result.split("\n"): - _width = len(x) * 24 - width = width if width > _width else _width - height = len(result.split("\n")) * 45 - A = CreateImg(width, height, font_size=24) - bk = CreateImg( - width, - height, - background=Path(IMAGE_PATH) / "background" / "1.png", - ) - A.paste(bk, alpha=True) - A.text((int(width * 0.048), int(height * 0.21)), result) - return A.pic2bs4() - except AttributeError: - pass - return None +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH +from utils.manager import ( + plugins2settings_manager, + admin_manager, + plugins_manager, + group_manager, +) +from typing import Optional +from services.log import logger +from pathlib import Path +from utils.utils import get_matchers +import random +import asyncio +import nonebot +import os + + +random_bk_path = Path(IMAGE_PATH) / "background" / "help" / "simple_help" + +background = Path(IMAGE_PATH) / "background" / "0.png" + + +async def create_help_img( + group_id: Optional[int], help_image: Path, simple_help_image: Path +): + """ + 生成帮助图片 + :param group_id: 群号 + :param help_image: 图片路径 + :param simple_help_image: 简易帮助图片路径 + """ + return await asyncio.get_event_loop().run_in_executor( + None, _create_help_img, group_id, help_image, simple_help_image + ) + + +def _create_help_img( + group_id: Optional[int], help_image: Path, simple_help_image: Path +): + """ + 生成帮助图片 + :param group_id: 群号 + :param help_image: 图片路径 + :param simple_help_image: 简易帮助图片路径 + """ + _matchers = get_matchers() + width = 0 + matchers_data = {} + _des_tmp = {} + _plugin_name_tmp = [] + _tmp = [] + tmp_img = CreateImg(0, 0, plain_text="1", font_size=24) + font_height = tmp_img.h + # 插件分类 + for matcher in _matchers: + plugin_name = None + _plugin = nonebot.plugin.get_plugin(matcher.module) + _module = _plugin.module + try: + plugin_name = _module.__getattribute__("__zx_plugin_name__") + try: + plugin_des = _module.__getattribute__("__plugin_des__") + except AttributeError: + plugin_des = "_" + if ( + "[hidden]" in plugin_name.lower() + or "[admin]" in plugin_name.lower() + or "[superuser]" in plugin_name.lower() + or plugin_name in _plugin_name_tmp + or plugin_name == "帮助" + ): + continue + plugin_type = ("normal",) + text_type = 0 + if plugins2settings_manager.get( + matcher.module + ) and plugins2settings_manager[matcher.module].get("plugin_type"): + plugin_type = tuple( + plugins2settings_manager.get_plugin_data(matcher.module)[ + "plugin_type" + ] + ) + else: + try: + plugin_type = _module.__getattribute__("__plugin_type__") + except AttributeError: + pass + if len(plugin_type) > 1: + try: + text_type = int(plugin_type[1]) + except ValueError as e: + logger.warning(f"生成列向帮助排列失败 {plugin_name}: {type(e)}: {e}") + plugin_type = plugin_type[0] + else: + plugin_type = plugin_type[0] + try: + plugin_cmd = _module.__getattribute__("__plugin_cmd__") + plugin_cmd = [x for x in plugin_cmd if "[_superuser]" not in x] + except AttributeError: + plugin_cmd = [] + if plugin_type not in matchers_data.keys(): + matchers_data[plugin_type] = {} + if plugin_des in _des_tmp.keys(): + try: + matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"] = ( + matchers_data[plugin_type][_des_tmp[plugin_des]]["cmd"] + + plugin_cmd + ) + except KeyError as e: + logger.warning(f"{type(e)}: {e}") + else: + matchers_data[plugin_type][plugin_name] = { + "module": matcher.module, + "des": plugin_des, + "cmd": plugin_cmd, + "text_type": text_type, + } + try: + if text_type == 0: + x = tmp_img.getsize( + f'{plugin_name}: {matchers_data[plugin_type][plugin_name]["des"]} ->' + + " / ".join(matchers_data[plugin_type][plugin_name]["cmd"]) + )[0] + width = width if width > x else x + except KeyError: + pass + if plugin_des not in _des_tmp: + _des_tmp[plugin_des] = plugin_name + except AttributeError as e: + if plugin_name not in _plugin_name_tmp: + logger.warning(f"获取功能 {matcher.module}: {plugin_name} 设置失败...e:{e}") + if plugin_name not in _plugin_name_tmp: + _plugin_name_tmp.append(plugin_name) + help_img_list = [] + simple_help_img_list = [] + types = list(matchers_data.keys()) + types.sort() + ix = 0 + # 详细帮助 + for type_ in types: + keys = list(matchers_data[type_].keys()) + keys.sort() + help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n" + simple_help_str = f"{type_ if type_ != 'normal' else '功能'}:\n\n" + for i, k in enumerate(keys): + # 禁用flag + flag = True + if plugins_manager.get_plugin_status( + matchers_data[type_][k]["module"], "all" + ): + flag = False + if group_id: + flag = flag and plugins_manager.get_plugin_status( + matchers_data[type_][k]["module"], "group" + ) + simple_help_str += ( + f"{i+1}.{k}<|_|~|>" + f"{group_manager.get_plugin_status(matchers_data[type_][k]['module'], group_id) if group_id else '_'}|" + f"{flag}\n" + ) + if matchers_data[type_][k]["text_type"] == 1: + _x = tmp_img.getsize( + f"{i+1}".rjust(5) + + f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} ' + )[0] + _str = ( + f"{i+1}".rjust(5) + + f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} ' + ) + _str += matchers_data[type_][k]["cmd"][0] + "\n" + for c in matchers_data[type_][k]["cmd"][1:]: + _str += "".rjust(int(_x * 0.125) + 1) + f"{c}\n" + help_str += _str + else: + help_str += ( + f"{i+1}".rjust(5) + + f'.{k}: {matchers_data[type_][k]["des"]} {"->" if matchers_data[type_][k]["cmd"] else ""} ' + + " / ".join(matchers_data[type_][k]["cmd"]) + + "\n" + ) + height = len(help_str.split("\n")) * (font_height + 5) + simple_height = len(simple_help_str.split("\n")) * (font_height + 5) + A = CreateImg( + width + 150, height, font_size=24, color="white" if not ix % 2 else "black" + ) + A.text((10, 10), help_str, (255, 255, 255) if ix % 2 else (0, 0, 0)) + # 生成各个分类的插件简易帮助图片 + simple_width = 0 + for x in [ + tmp_img.getsize(x.split("<|_|~|>")[0])[0] + for x in simple_help_str.split("\n") + ]: + simple_width = simple_width if simple_width > x else x + bk = CreateImg(simple_width + 20, simple_height, font_size=24, color="#6495ED") + B = CreateImg( + simple_width + 20, + simple_height, + font_size=24, + color="white" if not ix % 2 else "black", + ) + # 切分,判断插件开关状态 + _s_height = 10 + for _s in simple_help_str.split("\n"): + text_color = (255, 255, 255) if ix % 2 else (0, 0, 0) + _line_flag = False + if "<|_|~|>" in _s: + _x = _s.split("<|_|~|>") + _flag_sp = _x[-1].split("|") + if group_id: + if _flag_sp[0].lower() != "true": + text_color = (252, 75, 13) + if _flag_sp[1].lower() == "true": + _line_flag = True + _s = _x[0] + B.text((10, _s_height), _s, text_color) + if _line_flag: + B.line( + ( + 7, + _s_height + int(B.getsize(_s)[1] / 2) + 2, + B.getsize(_s)[0] + 11, + _s_height + int(B.getsize(_s)[1] / 2) + 2, + ), + (236, 66, 7), + 3, + ) + _s_height += B.getsize("1")[1] + 5 + # B.text((10, 10), simple_help_str, (255, 255, 255) if ix % 2 else (0, 0, 0)) + bk.paste(B, center_type="center") + bk.transparent(2) + ix += 1 + help_img_list.append(A) + simple_help_img_list.append(bk) + height = 0 + for img in help_img_list: + height += img.h + if not group_id: + A = CreateImg(width + 150, height + 50, font_size=24) + A.text( + (10, 10), '* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" "功能名: 功能简介 -> 指令\n\n' + ) + current_height = 50 + for img in help_img_list: + A.paste(img, (0, current_height)) + current_height += img.h + A.save(help_image) + # 详细帮助生成完毕 + # 简易帮助图片合成 + height = 0 + width = 0 + for img in simple_help_img_list: + if img.h > height: + height = img.h + width += img.w + 10 + B = CreateImg(width + 100, height + 250, font_size=24) + width, _ = get_max_width_or_paste(simple_help_img_list, B) + bk = None + random_bk = os.listdir(random_bk_path) + if random_bk: + bk = random.choice(random_bk) + x = max(width + 50, height + 250) + B = CreateImg( + x, + x, + font_size=24, + color="#FFEFD5", + background=random_bk_path / bk, + ) + B.filter("GaussianBlur", 10) + _, B = get_max_width_or_paste(simple_help_img_list, B, True) + w = 10 + h = 10 + for msg in ["目前支持的功能列表:", "可以通过 ‘帮助[功能名称]’ 来获取对应功能的使用方法", "或者使用 ‘详细帮助’ 来获取所有功能方法"]: + text = CreateImg( + 0, + 0, + plain_text=msg, + font_size=24, + font="yuanshen.ttf", + ) + B.paste(text, (w, h), True) + h += 50 + if msg == "目前支持的功能列表:": + w += 50 + B.paste( + CreateImg( + 0, + 0, + plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护", + font_size=24, + font="yuanshen.ttf", + font_color=(231, 74, 57) + ), + (300, 10), + True, + ) + B.save(simple_help_image) + + +def get_max_width_or_paste( + simple_help_img_list: list, B: CreateImg = None, is_paste: bool = False +) -> "int, CreateImg": + """ + 获取最大宽度,或直接贴图 + :param simple_help_img_list: 简单帮助图片列表 + :param B: 背景图 + :param is_paste: 是否直接贴图 + """ + current_width = 50 + current_height = 180 + max_width = simple_help_img_list[0].w + for i in range(len(simple_help_img_list)): + try: + if is_paste and B: + B.paste(simple_help_img_list[i], (current_width, current_height), True) + current_height += simple_help_img_list[i].h + 40 + if current_height + simple_help_img_list[i + 1].h > B.h - 10: + current_height = 180 + current_width += max_width + 30 + max_width = 0 + elif simple_help_img_list[i].w > max_width: + max_width = simple_help_img_list[i].w + except IndexError: + pass + if current_width > simple_help_img_list[0].w + 50: + current_width += simple_help_img_list[-1].w + return current_width, B + + +def get_plugin_help(msg: str, is_super: bool = False) -> Optional[str]: + """ + 获取功能的帮助信息 + :param msg: 功能cmd + :param is_super: 是否为超级用户 + """ + module = plugins2settings_manager.get_plugin_module(msg) + if not module: + module = admin_manager.get_plugin_module(msg) + if module: + try: + plugin = nonebot.plugin.get_plugin(module) + if plugin: + if is_super: + result = plugin.module.__getattribute__( + "__plugin_superuser_usage__" + ) + else: + result = plugin.module.__getattribute__("__plugin_usage__") + if result: + width = 0 + for x in result.split("\n"): + _width = len(x) * 24 + width = width if width > _width else _width + height = len(result.split("\n")) * 45 + A = CreateImg(width, height, font_size=24) + bk = CreateImg( + width, + height, + background=Path(IMAGE_PATH) / "background" / "1.png", + ) + A.paste(bk, alpha=True) + A.text((int(width * 0.048), int(height * 0.21)), result) + return A.pic2bs4() + except AttributeError: + pass + return None diff --git a/basic_plugins/hooks/__init__.py b/basic_plugins/hooks/__init__.py old mode 100644 new mode 100755 index d1c21d41..f33bd813 --- a/basic_plugins/hooks/__init__.py +++ b/basic_plugins/hooks/__init__.py @@ -1,38 +1,38 @@ -from configs.config import Config -import nonebot - - -Config.add_plugin_config( - "hook", - "CHECK_NOTICE_INFO_CD", - 300, - name="基础hook配置", - help_="群检测,个人权限检测等各种检测提示信息cd", - default_value=300 -) - -Config.add_plugin_config( - "hook", - "MALICIOUS_BAN_TIME", - 30, - help_="恶意命令触发检测触发后ban的时长(分钟)", - default_value=30 -) - -Config.add_plugin_config( - "hook", - "MALICIOUS_CHECK_TIME", - 5, - help_="恶意命令触发检测规定时间内(秒)", - default_value=5 -) - -Config.add_plugin_config( - "hook", - "MALICIOUS_BAN_COUNT", - 6, - help_="恶意命令触发检测最大触发次数", - default_value=6 -) - -nonebot.load_plugins("basic_plugins/hooks") +from configs.config import Config +import nonebot + + +Config.add_plugin_config( + "hook", + "CHECK_NOTICE_INFO_CD", + 300, + name="基础hook配置", + help_="群检测,个人权限检测等各种检测提示信息cd", + default_value=300 +) + +Config.add_plugin_config( + "hook", + "MALICIOUS_BAN_TIME", + 30, + help_="恶意命令触发检测触发后ban的时长(分钟)", + default_value=30 +) + +Config.add_plugin_config( + "hook", + "MALICIOUS_CHECK_TIME", + 5, + help_="恶意命令触发检测规定时间内(秒)", + default_value=5 +) + +Config.add_plugin_config( + "hook", + "MALICIOUS_BAN_COUNT", + 6, + help_="恶意命令触发检测最大触发次数", + default_value=6 +) + +nonebot.load_plugins("basic_plugins/hooks") diff --git a/basic_plugins/hooks/auth_hook.py b/basic_plugins/hooks/auth_hook.py old mode 100644 new mode 100755 index 1dadbaa2..aaf782bf --- a/basic_plugins/hooks/auth_hook.py +++ b/basic_plugins/hooks/auth_hook.py @@ -1,210 +1,210 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, IgnoredException -from nonebot.adapters.cqhttp.exception import ActionFailed -from utils.manager import ( - plugins2settings_manager, - admin_manager, - group_manager, - plugins_manager, -) -from .utils import set_block_limit_false, status_message_manager -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - GroupMessageEvent, - PokeNotifyEvent, -) -from configs.config import Config -from models.ban_user import BanUser -from utils.utils import FreqLimiter -from utils.message_builder import at -from models.level_user import LevelUser - - -_flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) -_flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) -_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) -_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) - - -ignore_rst_module = ["ai", "poke", "dialogue"] - - -# 权限检测 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - module = matcher.module - plugins2info_dict = plugins2settings_manager.get_data() - if ( - (not isinstance(event, MessageEvent) and module != "poke") - or await BanUser.is_ban(event.user_id) - and str(event.user_id) not in bot.config.superusers - ) or ( - str(event.user_id) in bot.config.superusers - and plugins2info_dict.get(module) - and not plugins2info_dict[module]["limit_superuser"] - ): - return - # 群黑名单检测 - if isinstance(event, GroupMessageEvent): - if group_manager.get_group_level(event.group_id) < 0: - raise IgnoredException("群黑名单") - if module in admin_manager.keys() and matcher.priority not in [1, 9]: - if isinstance(event, GroupMessageEvent): - # 个人权限 - if ( - not await LevelUser.check_level( - event.user_id, - event.group_id, - admin_manager.get_plugin_level(module), - ) - and admin_manager.get_plugin_level(module) > 0 - ): - try: - if _flmt.check(event.user_id): - _flmt.start_cd(event.user_id) - await bot.send_group_msg( - group_id=event.group_id, - message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" - f"{admin_manager.get_plugin_level(module)}", - ) - except ActionFailed: - pass - set_block_limit_false(event, module) - if event.is_tome(): - status_message_manager.add(event.group_id) - raise IgnoredException("权限不足") - else: - if not await LevelUser.check_level( - event.user_id, 0, admin_manager.get_plugin_level(module) - ): - try: - await bot.send_private_msg( - user_id=event.user_id, - message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(module)}", - ) - except ActionFailed: - pass - set_block_limit_false(event, module) - if event.is_tome(): - status_message_manager.add(event.user_id) - raise IgnoredException("权限不足") - if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]: - # 戳一戳单独判断 - if isinstance(event, GroupMessageEvent) or ( - isinstance(event, PokeNotifyEvent) and event.group_id - ): - if status_message_manager.get(event.group_id) is None: - status_message_manager.delete(event.group_id) - if plugins2info_dict[module]["level"] > group_manager.get_group_level( - event.group_id - ): - try: - if _flmt_g.check(event.user_id) and module not in ignore_rst_module: - _flmt_g.start_cd(event.user_id) - await bot.send_group_msg( - group_id=event.group_id, message="群权限不足..." - ) - except ActionFailed: - pass - if event.is_tome(): - status_message_manager.add(event.group_id) - set_block_limit_false(event, module) - raise IgnoredException("群权限不足") - # 插件状态 - if not group_manager.get_plugin_status(module, event.group_id): - try: - if module not in ignore_rst_module and _flmt_s.check( - event.group_id - ): - _flmt_s.start_cd(event.group_id) - await bot.send_group_msg( - group_id=event.group_id, message="该群未开启此功能.." - ) - except ActionFailed: - pass - if event.is_tome(): - status_message_manager.add(event.group_id) - set_block_limit_false(event, module) - raise IgnoredException("未开启此功能...") - # 管理员禁用 - if not group_manager.get_plugin_status(f"{module}:super", event.group_id): - try: - if ( - _flmt_s.check(event.group_id) - and module not in ignore_rst_module - ): - _flmt_s.start_cd(event.group_id) - await bot.send_group_msg( - group_id=event.group_id, message="管理员禁用了此群该功能..." - ) - except ActionFailed: - pass - if event.is_tome(): - status_message_manager.add(event.group_id) - set_block_limit_false(event, module) - raise IgnoredException("管理员禁用了此群该功能...") - # 群聊禁用 - if not plugins_manager.get_plugin_status(module, block_type="group"): - try: - if ( - _flmt_c.check(event.group_id) - and module not in ignore_rst_module - ): - _flmt_c.start_cd(event.group_id) - await bot.send_group_msg( - group_id=event.group_id, message="该功能在群聊中已被禁用..." - ) - except ActionFailed: - pass - if event.is_tome(): - status_message_manager.add(event.group_id) - set_block_limit_false(event, module) - raise IgnoredException("该插件在群聊中已被禁用...") - else: - # 私聊禁用 - if not plugins_manager.get_plugin_status(module, block_type="private"): - try: - if _flmt_c.check(event.user_id): - _flmt_c.start_cd(event.user_id) - await bot.send_private_msg( - user_id=event.user_id, message="该功能在私聊中已被禁用..." - ) - except ActionFailed: - pass - if event.is_tome(): - status_message_manager.add(event.user_id) - set_block_limit_false(event, module) - raise IgnoredException("该插件在私聊中已被禁用...") - # 维护 - if not plugins_manager.get_plugin_status(module, block_type="all"): - if isinstance( - event, GroupMessageEvent - ) and group_manager.check_group_is_white(event.group_id): - return - try: - if isinstance(event, GroupMessageEvent): - if ( - _flmt_c.check(event.group_id) - and module not in ignore_rst_module - ): - _flmt_c.start_cd(event.group_id) - await bot.send_group_msg( - group_id=event.group_id, message="此功能正在维护..." - ) - else: - await bot.send_private_msg( - user_id=event.user_id, message="此功能正在维护..." - ) - except ActionFailed: - pass - if event.is_tome(): - id_ = ( - event.group_id - if isinstance(event, GroupMessageEvent) - else event.user_id - ) - status_message_manager.add(id_) - set_block_limit_false(event, module) - raise IgnoredException("此功能正在维护...") +from nonebot.matcher import Matcher +from nonebot.message import run_preprocessor, IgnoredException +from nonebot.adapters.cqhttp.exception import ActionFailed +from utils.manager import ( + plugins2settings_manager, + admin_manager, + group_manager, + plugins_manager, +) +from .utils import set_block_limit_false, status_message_manager +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent, + PokeNotifyEvent, +) +from configs.config import Config +from models.ban_user import BanUser +from utils.utils import FreqLimiter +from utils.message_builder import at +from models.level_user import LevelUser + + +_flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) +_flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) +_flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) +_flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) + + +ignore_rst_module = ["ai", "poke", "dialogue"] + + +# 权限检测 +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): + module = matcher.module + plugins2info_dict = plugins2settings_manager.get_data() + if ( + (not isinstance(event, MessageEvent) and module != "poke") + or await BanUser.is_ban(event.user_id) + and str(event.user_id) not in bot.config.superusers + ) or ( + str(event.user_id) in bot.config.superusers + and plugins2info_dict.get(module) + and not plugins2info_dict[module]["limit_superuser"] + ): + return + # 群黑名单检测 + if isinstance(event, GroupMessageEvent): + if group_manager.get_group_level(event.group_id) < 0: + raise IgnoredException("群黑名单") + if module in admin_manager.keys() and matcher.priority not in [1, 9]: + if isinstance(event, GroupMessageEvent): + # 个人权限 + if ( + not await LevelUser.check_level( + event.user_id, + event.group_id, + admin_manager.get_plugin_level(module), + ) + and admin_manager.get_plugin_level(module) > 0 + ): + try: + if _flmt.check(event.user_id): + _flmt.start_cd(event.user_id) + await bot.send_group_msg( + group_id=event.group_id, + message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" + f"{admin_manager.get_plugin_level(module)}", + ) + except ActionFailed: + pass + set_block_limit_false(event, module) + if event.is_tome(): + status_message_manager.add(event.group_id) + raise IgnoredException("权限不足") + else: + if not await LevelUser.check_level( + event.user_id, 0, admin_manager.get_plugin_level(module) + ): + try: + await bot.send_private_msg( + user_id=event.user_id, + message=f"你的权限不足喔,该功能需要的权限等级:{admin_manager.get_plugin_level(module)}", + ) + except ActionFailed: + pass + set_block_limit_false(event, module) + if event.is_tome(): + status_message_manager.add(event.user_id) + raise IgnoredException("权限不足") + if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]: + # 戳一戳单独判断 + if isinstance(event, GroupMessageEvent) or ( + isinstance(event, PokeNotifyEvent) and event.group_id + ): + if status_message_manager.get(event.group_id) is None: + status_message_manager.delete(event.group_id) + if plugins2info_dict[module]["level"] > group_manager.get_group_level( + event.group_id + ): + try: + if _flmt_g.check(event.user_id) and module not in ignore_rst_module: + _flmt_g.start_cd(event.user_id) + await bot.send_group_msg( + group_id=event.group_id, message="群权限不足..." + ) + except ActionFailed: + pass + if event.is_tome(): + status_message_manager.add(event.group_id) + set_block_limit_false(event, module) + raise IgnoredException("群权限不足") + # 插件状态 + if not group_manager.get_plugin_status(module, event.group_id): + try: + if module not in ignore_rst_module and _flmt_s.check( + event.group_id + ): + _flmt_s.start_cd(event.group_id) + await bot.send_group_msg( + group_id=event.group_id, message="该群未开启此功能.." + ) + except ActionFailed: + pass + if event.is_tome(): + status_message_manager.add(event.group_id) + set_block_limit_false(event, module) + raise IgnoredException("未开启此功能...") + # 管理员禁用 + if not group_manager.get_plugin_status(f"{module}:super", event.group_id): + try: + if ( + _flmt_s.check(event.group_id) + and module not in ignore_rst_module + ): + _flmt_s.start_cd(event.group_id) + await bot.send_group_msg( + group_id=event.group_id, message="管理员禁用了此群该功能..." + ) + except ActionFailed: + pass + if event.is_tome(): + status_message_manager.add(event.group_id) + set_block_limit_false(event, module) + raise IgnoredException("管理员禁用了此群该功能...") + # 群聊禁用 + if not plugins_manager.get_plugin_status(module, block_type="group"): + try: + if ( + _flmt_c.check(event.group_id) + and module not in ignore_rst_module + ): + _flmt_c.start_cd(event.group_id) + await bot.send_group_msg( + group_id=event.group_id, message="该功能在群聊中已被禁用..." + ) + except ActionFailed: + pass + if event.is_tome(): + status_message_manager.add(event.group_id) + set_block_limit_false(event, module) + raise IgnoredException("该插件在群聊中已被禁用...") + else: + # 私聊禁用 + if not plugins_manager.get_plugin_status(module, block_type="private"): + try: + if _flmt_c.check(event.user_id): + _flmt_c.start_cd(event.user_id) + await bot.send_private_msg( + user_id=event.user_id, message="该功能在私聊中已被禁用..." + ) + except ActionFailed: + pass + if event.is_tome(): + status_message_manager.add(event.user_id) + set_block_limit_false(event, module) + raise IgnoredException("该插件在私聊中已被禁用...") + # 维护 + if not plugins_manager.get_plugin_status(module, block_type="all"): + if isinstance( + event, GroupMessageEvent + ) and group_manager.check_group_is_white(event.group_id): + return + try: + if isinstance(event, GroupMessageEvent): + if ( + _flmt_c.check(event.group_id) + and module not in ignore_rst_module + ): + _flmt_c.start_cd(event.group_id) + await bot.send_group_msg( + group_id=event.group_id, message="此功能正在维护..." + ) + else: + await bot.send_private_msg( + user_id=event.user_id, message="此功能正在维护..." + ) + except ActionFailed: + pass + if event.is_tome(): + id_ = ( + event.group_id + if isinstance(event, GroupMessageEvent) + else event.user_id + ) + status_message_manager.add(id_) + set_block_limit_false(event, module) + raise IgnoredException("此功能正在维护...") diff --git a/basic_plugins/hooks/ban_hook.py b/basic_plugins/hooks/ban_hook.py old mode 100644 new mode 100755 index e7d131b0..ea59ef0a --- a/basic_plugins/hooks/ban_hook.py +++ b/basic_plugins/hooks/ban_hook.py @@ -1,83 +1,83 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, IgnoredException -from nonebot.adapters.cqhttp.exception import ActionFailed -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - GroupMessageEvent, -) -from configs.config import Config -from models.ban_user import BanUser -from utils.utils import is_number, static_flmt -from utils.message_builder import at - - -Config.add_plugin_config( - "hook", - "BAN_RESULT", - "才不会给你发消息.", - help_="对被ban用户发送的消息", -) - - -# 检查是否被ban -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - try: - if ( - await BanUser.is_super_ban(event.user_id) - and str(event.user_id) not in bot.config.superusers - ): - raise IgnoredException("用户处于超级黑名单中") - except AttributeError: - pass - if not isinstance(event, MessageEvent): - return - if matcher.type == "message" and matcher.priority not in [1, 9]: - if ( - await BanUser.is_ban(event.user_id) - and str(event.user_id) not in bot.config.superusers - ): - time = await BanUser.check_ban_time(event.user_id) - if is_number(time): - time = abs(int(time)) - if time < 60: - time = str(time) + " 秒" - else: - time = str(int(time / 60)) + " 分钟" - else: - time = str(time) + " 分钟" - if isinstance(event, GroupMessageEvent): - if not static_flmt.check(event.user_id): - raise IgnoredException("用户处于黑名单中") - static_flmt.start_cd(event.user_id) - if matcher.priority != 9: - try: - ban_result = Config.get_config("hook", "BAN_RESULT") - if ban_result: - await bot.send_group_msg( - group_id=event.group_id, - message=at(event.user_id) - + ban_result - + f" 在..在 {time} 后才会理你喔", - ) - except ActionFailed: - pass - else: - if not static_flmt.check(event.user_id): - raise IgnoredException("用户处于黑名单中") - static_flmt.start_cd(event.user_id) - if matcher.priority != 9: - try: - ban_result = Config.get_config("hook", "BAN_RESULT") - if ban_result: - await bot.send_private_msg( - user_id=event.user_id, - message=at(event.user_id) - + ban_result - + f" 在..在 {time}后才会理你喔", - ) - except ActionFailed: - pass +from nonebot.matcher import Matcher +from nonebot.message import run_preprocessor, IgnoredException +from nonebot.adapters.cqhttp.exception import ActionFailed +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent, +) +from configs.config import Config +from models.ban_user import BanUser +from utils.utils import is_number, static_flmt +from utils.message_builder import at + + +Config.add_plugin_config( + "hook", + "BAN_RESULT", + "才不会给你发消息.", + help_="对被ban用户发送的消息", +) + + +# 检查是否被ban +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): + try: + if ( + await BanUser.is_super_ban(event.user_id) + and str(event.user_id) not in bot.config.superusers + ): + raise IgnoredException("用户处于超级黑名单中") + except AttributeError: + pass + if not isinstance(event, MessageEvent): + return + if matcher.type == "message" and matcher.priority not in [1, 9]: + if ( + await BanUser.is_ban(event.user_id) + and str(event.user_id) not in bot.config.superusers + ): + time = await BanUser.check_ban_time(event.user_id) + if is_number(time): + time = abs(int(time)) + if time < 60: + time = str(time) + " 秒" + else: + time = str(int(time / 60)) + " 分钟" + else: + time = str(time) + " 分钟" + if isinstance(event, GroupMessageEvent): + if not static_flmt.check(event.user_id): + raise IgnoredException("用户处于黑名单中") + static_flmt.start_cd(event.user_id) + if matcher.priority != 9: + try: + ban_result = Config.get_config("hook", "BAN_RESULT") + if ban_result: + await bot.send_group_msg( + group_id=event.group_id, + message=at(event.user_id) + + ban_result + + f" 在..在 {time} 后才会理你喔", + ) + except ActionFailed: + pass + else: + if not static_flmt.check(event.user_id): + raise IgnoredException("用户处于黑名单中") + static_flmt.start_cd(event.user_id) + if matcher.priority != 9: + try: + ban_result = Config.get_config("hook", "BAN_RESULT") + if ban_result: + await bot.send_private_msg( + user_id=event.user_id, + message=at(event.user_id) + + ban_result + + f" 在..在 {time}后才会理你喔", + ) + except ActionFailed: + pass raise IgnoredException("用户处于黑名单中") \ No newline at end of file diff --git a/basic_plugins/hooks/chkdsk_hook.py b/basic_plugins/hooks/chkdsk_hook.py old mode 100644 new mode 100755 index 3fa53285..e04d20c4 --- a/basic_plugins/hooks/chkdsk_hook.py +++ b/basic_plugins/hooks/chkdsk_hook.py @@ -1,54 +1,54 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, IgnoredException -from nonebot.adapters.cqhttp.exception import ActionFailed -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - GroupMessageEvent, -) -from configs.config import Config -from models.ban_user import BanUser -from utils.utils import BanCheckLimiter -from utils.message_builder import at -from services.log import logger - - -_blmt = BanCheckLimiter( - Config.get_config("hook", "MALICIOUS_CHECK_TIME"), - Config.get_config("hook", "MALICIOUS_BAN_COUNT"), -) - - -# 恶意触发命令检测 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State): - if not isinstance(event, MessageEvent): - return - if matcher.type == "message" and matcher.priority not in [1, 9]: - if state["_prefix"]["raw_command"]: - if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'): - if await BanUser.ban( - event.user_id, - 9, - Config.get_config("hook", "MALICIOUS_BAN_TIME") * 60, - ): - logger.info(f"USER {event.user_id} 触发了恶意触发检测") - if isinstance(event, GroupMessageEvent): - try: - await bot.send_group_msg( - group_id=event.group_id, - message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", - ) - except ActionFailed: - pass - else: - try: - await bot.send_private_msg( - user_id=event.user_id, - message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", - ) - except ActionFailed: - pass - raise IgnoredException("检测到恶意触发命令") - _blmt.add(f'{event.user_id}{state["_prefix"]["raw_command"]}') +from nonebot.matcher import Matcher +from nonebot.message import run_preprocessor, IgnoredException +from nonebot.adapters.cqhttp.exception import ActionFailed +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent, +) +from configs.config import Config +from models.ban_user import BanUser +from utils.utils import BanCheckLimiter +from utils.message_builder import at +from services.log import logger + + +_blmt = BanCheckLimiter( + Config.get_config("hook", "MALICIOUS_CHECK_TIME"), + Config.get_config("hook", "MALICIOUS_BAN_COUNT"), +) + + +# 恶意触发命令检测 +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State): + if not isinstance(event, MessageEvent): + return + if matcher.type == "message" and matcher.priority not in [1, 9]: + if state["_prefix"]["raw_command"]: + if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'): + if await BanUser.ban( + event.user_id, + 9, + Config.get_config("hook", "MALICIOUS_BAN_TIME") * 60, + ): + logger.info(f"USER {event.user_id} 触发了恶意触发检测") + if isinstance(event, GroupMessageEvent): + try: + await bot.send_group_msg( + group_id=event.group_id, + message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", + ) + except ActionFailed: + pass + else: + try: + await bot.send_private_msg( + user_id=event.user_id, + message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", + ) + except ActionFailed: + pass + raise IgnoredException("检测到恶意触发命令") + _blmt.add(f'{event.user_id}{state["_prefix"]["raw_command"]}') diff --git a/basic_plugins/hooks/limit_hook.py b/basic_plugins/hooks/limit_hook.py old mode 100644 new mode 100755 index eff6760c..8e747316 --- a/basic_plugins/hooks/limit_hook.py +++ b/basic_plugins/hooks/limit_hook.py @@ -1,146 +1,146 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException -from nonebot.adapters.cqhttp.exception import ActionFailed -from models.friend_user import FriendUser -from models.group_member_info import GroupInfoUser -from utils.message_builder import at -from .utils import status_message_manager, set_block_limit_false -from utils.manager import ( - plugins2cd_manager, - plugins2block_manager, - plugins2count_manager, -) -from typing import Optional -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - Event, - MessageEvent, - PrivateMessageEvent, - GroupMessageEvent, - Message, -) - - -# 命令cd | 命令阻塞 | 命令次数 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - if not isinstance(event, MessageEvent) and matcher.module != "poke": - return - module = matcher.module - if ( - isinstance(event, GroupMessageEvent) - and status_message_manager.get(event.group_id) is None - ): - status_message_manager.delete(event.group_id) - # Count - if ( - plugins2count_manager.check_plugin_count_status(module) - and event.user_id not in bot.config.superusers - ): - plugin_count_data = plugins2count_manager.get_plugin_count_data(module) - limit_type = plugin_count_data["limit_type"] - rst = plugin_count_data["rst"] - count_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - count_type_ = event.group_id - if not plugins2count_manager.check(module, count_type_): - if rst: - rst = await init_rst(rst, event) - await send_msg(rst, bot, event) - raise IgnoredException(f"{module} count次数限制...") - else: - plugins2count_manager.increase(module, count_type_) - # Cd - if plugins2cd_manager.check_plugin_cd_status(module): - plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module) - check_type = plugin_cd_data["check_type"] - limit_type = plugin_cd_data["limit_type"] - rst = plugin_cd_data["rst"] - if ( - (isinstance(event, PrivateMessageEvent) and check_type == "private") - or (isinstance(event, GroupMessageEvent) and check_type == "group") - or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all" - ): - cd_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - cd_type_ = event.group_id - if not plugins2cd_manager.check(module, cd_type_): - if rst: - rst = await init_rst(rst, event) - await send_msg(rst, bot, event) - raise IgnoredException(f"{module} 正在cd中...") - else: - plugins2cd_manager.start_cd(module, cd_type_) - # Block - if plugins2block_manager.check_plugin_block_status(module): - plugin_block_data = plugins2block_manager.get_plugin_block_data(module) - check_type = plugin_block_data["check_type"] - limit_type = plugin_block_data["limit_type"] - rst = plugin_block_data["rst"] - if ( - (isinstance(event, PrivateMessageEvent) and check_type == "private") - or (isinstance(event, GroupMessageEvent) and check_type == "group") - or check_type == "all" - ): - block_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - block_type_ = event.group_id - if plugins2block_manager.check(block_type_, module): - if rst: - rst = await init_rst(rst, event) - await send_msg(rst, bot, event) - raise IgnoredException(f"{event.user_id}正在调用{module}....") - else: - plugins2block_manager.set_true(block_type_, module) - - -async def send_msg(rst: str, bot: Bot, event: MessageEvent): - """ - 发送信息 - :param rst: pass - :param bot: pass - :param event: pass - """ - rst = await init_rst(rst, event) - try: - if isinstance(event, GroupMessageEvent): - status_message_manager.add(event.group_id) - await bot.send_group_msg(group_id=event.group_id, message=Message(rst)) - else: - status_message_manager.add(event.user_id) - await bot.send_private_msg(user_id=event.user_id, message=Message(rst)) - except ActionFailed: - pass - - -# 解除命令block阻塞 -@run_postprocessor -async def _( - matcher: Matcher, - exception: Optional[Exception], - bot: Bot, - event: Event, - state: T_State, -): - if not isinstance(event, MessageEvent) and matcher.module != "poke": - return - module = matcher.module - set_block_limit_false(event, module) - - -async def init_rst(rst: str, event: MessageEvent): - if "[uname]" in rst: - uname = event.sender.card if event.sender.card else event.sender.nickname - rst = rst.replace("[uname]", uname) - if "[nickname]" in rst: - if isinstance(event, GroupMessageEvent): - nickname = await GroupInfoUser.get_group_member_nickname( - event.user_id, event.group_id - ) - else: - nickname = await FriendUser.get_friend_nickname(event.user_id) - rst = rst.replace("[nickname]", nickname) - if "[at]" in rst and isinstance(event, GroupMessageEvent): - rst = rst.replace("[at]", str(at(event.user_id))) - return rst +from nonebot.matcher import Matcher +from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException +from nonebot.adapters.cqhttp.exception import ActionFailed +from models.friend_user import FriendUser +from models.group_member_info import GroupInfoUser +from utils.message_builder import at +from .utils import status_message_manager, set_block_limit_false +from utils.manager import ( + plugins2cd_manager, + plugins2block_manager, + plugins2count_manager, +) +from typing import Optional +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import ( + Bot, + Event, + MessageEvent, + PrivateMessageEvent, + GroupMessageEvent, + Message, +) + + +# 命令cd | 命令阻塞 | 命令次数 +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): + if not isinstance(event, MessageEvent) and matcher.module != "poke": + return + module = matcher.module + if ( + isinstance(event, GroupMessageEvent) + and status_message_manager.get(event.group_id) is None + ): + status_message_manager.delete(event.group_id) + # Count + if ( + plugins2count_manager.check_plugin_count_status(module) + and event.user_id not in bot.config.superusers + ): + plugin_count_data = plugins2count_manager.get_plugin_count_data(module) + limit_type = plugin_count_data["limit_type"] + rst = plugin_count_data["rst"] + count_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + count_type_ = event.group_id + if not plugins2count_manager.check(module, count_type_): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{module} count次数限制...") + else: + plugins2count_manager.increase(module, count_type_) + # Cd + if plugins2cd_manager.check_plugin_cd_status(module): + plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module) + check_type = plugin_cd_data["check_type"] + limit_type = plugin_cd_data["limit_type"] + rst = plugin_cd_data["rst"] + if ( + (isinstance(event, PrivateMessageEvent) and check_type == "private") + or (isinstance(event, GroupMessageEvent) and check_type == "group") + or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all" + ): + cd_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + cd_type_ = event.group_id + if not plugins2cd_manager.check(module, cd_type_): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{module} 正在cd中...") + else: + plugins2cd_manager.start_cd(module, cd_type_) + # Block + if plugins2block_manager.check_plugin_block_status(module): + plugin_block_data = plugins2block_manager.get_plugin_block_data(module) + check_type = plugin_block_data["check_type"] + limit_type = plugin_block_data["limit_type"] + rst = plugin_block_data["rst"] + if ( + (isinstance(event, PrivateMessageEvent) and check_type == "private") + or (isinstance(event, GroupMessageEvent) and check_type == "group") + or check_type == "all" + ): + block_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + block_type_ = event.group_id + if plugins2block_manager.check(block_type_, module): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{event.user_id}正在调用{module}....") + else: + plugins2block_manager.set_true(block_type_, module) + + +async def send_msg(rst: str, bot: Bot, event: MessageEvent): + """ + 发送信息 + :param rst: pass + :param bot: pass + :param event: pass + """ + rst = await init_rst(rst, event) + try: + if isinstance(event, GroupMessageEvent): + status_message_manager.add(event.group_id) + await bot.send_group_msg(group_id=event.group_id, message=Message(rst)) + else: + status_message_manager.add(event.user_id) + await bot.send_private_msg(user_id=event.user_id, message=Message(rst)) + except ActionFailed: + pass + + +# 解除命令block阻塞 +@run_postprocessor +async def _( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: Event, + state: T_State, +): + if not isinstance(event, MessageEvent) and matcher.module != "poke": + return + module = matcher.module + set_block_limit_false(event, module) + + +async def init_rst(rst: str, event: MessageEvent): + if "[uname]" in rst: + uname = event.sender.card if event.sender.card else event.sender.nickname + rst = rst.replace("[uname]", uname) + if "[nickname]" in rst: + if isinstance(event, GroupMessageEvent): + nickname = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) + else: + nickname = await FriendUser.get_friend_nickname(event.user_id) + rst = rst.replace("[nickname]", nickname) + if "[at]" in rst and isinstance(event, GroupMessageEvent): + rst = rst.replace("[at]", str(at(event.user_id))) + return rst diff --git a/basic_plugins/hooks/other_hook.py b/basic_plugins/hooks/other_hook.py old mode 100644 new mode 100755 index b2a85919..b2d617f5 --- a/basic_plugins/hooks/other_hook.py +++ b/basic_plugins/hooks/other_hook.py @@ -1,41 +1,41 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, IgnoredException -from nonebot.typing import T_State -from .utils import status_message_manager -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - PrivateMessageEvent, - GroupMessageEvent, -) - - -# 为什么AI会自己和自己聊天 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_State): - if not isinstance(event, MessageEvent): - return - if event.user_id == int(bot.self_id): - raise IgnoredException("为什么AI会自己和自己聊天") - - -# 有命令就别说话了 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - if not isinstance(event, MessageEvent): - return - if matcher.type == "message" and matcher.module == "ai": - if ( - isinstance(event, GroupMessageEvent) - and not status_message_manager.check(event.group_id) - ): - status_message_manager.delete(event.group_id) - raise IgnoredException("有命令就别说话了") - elif ( - isinstance(event, PrivateMessageEvent) - and not status_message_manager.check(event.user_id) - ): - print(status_message_manager) - status_message_manager.delete(event.user_id) - raise IgnoredException("有命令就别说话了") - +from nonebot.matcher import Matcher +from nonebot.message import run_preprocessor, IgnoredException +from nonebot.typing import T_State +from .utils import status_message_manager +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + PrivateMessageEvent, + GroupMessageEvent, +) + + +# 为什么AI会自己和自己聊天 +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_State): + if not isinstance(event, MessageEvent): + return + if event.user_id == int(bot.self_id): + raise IgnoredException("为什么AI会自己和自己聊天") + + +# 有命令就别说话了 +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): + if not isinstance(event, MessageEvent): + return + if matcher.type == "message" and matcher.module == "ai": + if ( + isinstance(event, GroupMessageEvent) + and not status_message_manager.check(event.group_id) + ): + status_message_manager.delete(event.group_id) + raise IgnoredException("有命令就别说话了") + elif ( + isinstance(event, PrivateMessageEvent) + and not status_message_manager.check(event.user_id) + ): + print(status_message_manager) + status_message_manager.delete(event.user_id) + raise IgnoredException("有命令就别说话了") + diff --git a/basic_plugins/hooks/utils.py b/basic_plugins/hooks/utils.py old mode 100644 new mode 100755 index 5231b8f9..7860b42e --- a/basic_plugins/hooks/utils.py +++ b/basic_plugins/hooks/utils.py @@ -1,48 +1,48 @@ -from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent -from utils.manager import plugins2block_manager, StaticData -import time - - -class StatusMessageManager(StaticData): - - def __init__(self): - super().__init__(None) - - def add(self, id_: int): - self._data[id_] = time.time() - - def delete(self, id_: int): - if self._data.get(id_): - del self._data[id_] - - def check(self, id_: int, t: int = 30) -> bool: - if self._data.get(id_): - if time.time() - self._data[id_] > t: - del self._data[id_] - return True - return False - return True - - -status_message_manager = StatusMessageManager() - - -def set_block_limit_false(event, module): - """ - 设置用户block为false - :param event: event - :param module: 插件模块 - """ - if plugins2block_manager.check_plugin_block_status(module): - plugin_block_data = plugins2block_manager.get_plugin_block_data(module) - check_type = plugin_block_data["check_type"] - limit_type = plugin_block_data["limit_type"] - if not ( - (isinstance(event, GroupMessageEvent) and check_type == "private") - or (isinstance(event, PrivateMessageEvent) and check_type == "group") - ): - block_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - block_type_ = event.group_id - plugins2block_manager.set_false(block_type_, module) - +from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent +from utils.manager import plugins2block_manager, StaticData +import time + + +class StatusMessageManager(StaticData): + + def __init__(self): + super().__init__(None) + + def add(self, id_: int): + self._data[id_] = time.time() + + def delete(self, id_: int): + if self._data.get(id_): + del self._data[id_] + + def check(self, id_: int, t: int = 30) -> bool: + if self._data.get(id_): + if time.time() - self._data[id_] > t: + del self._data[id_] + return True + return False + return True + + +status_message_manager = StatusMessageManager() + + +def set_block_limit_false(event, module): + """ + 设置用户block为false + :param event: event + :param module: 插件模块 + """ + if plugins2block_manager.check_plugin_block_status(module): + plugin_block_data = plugins2block_manager.get_plugin_block_data(module) + check_type = plugin_block_data["check_type"] + limit_type = plugin_block_data["limit_type"] + if not ( + (isinstance(event, GroupMessageEvent) and check_type == "private") + or (isinstance(event, PrivateMessageEvent) and check_type == "group") + ): + block_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + block_type_ = event.group_id + plugins2block_manager.set_false(block_type_, module) + diff --git a/basic_plugins/hooks/withdraw_message_hook.py b/basic_plugins/hooks/withdraw_message_hook.py old mode 100644 new mode 100755 index 76356542..d91ef8d3 --- a/basic_plugins/hooks/withdraw_message_hook.py +++ b/basic_plugins/hooks/withdraw_message_hook.py @@ -1,28 +1,28 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_postprocessor -from typing import Optional -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, Event -from utils.manager import withdraw_message_manager -import asyncio - - -# 消息撤回 -@run_postprocessor -async def _( - matcher: Matcher, - exception: Optional[Exception], - bot: Bot, - event: Event, - state: T_State, -): - tasks = [] - for id_, time in withdraw_message_manager.data: - tasks.append(asyncio.ensure_future(_withdraw_message(bot, id_, time))) - withdraw_message_manager.remove((id_, time)) - await asyncio.gather(*tasks) - - -async def _withdraw_message(bot: Bot, id_: int, time: int): - await asyncio.sleep(time) - await bot.delete_msg(message_id=id_, self_id=int(bot.self_id)) +from nonebot.matcher import Matcher +from nonebot.message import run_postprocessor +from typing import Optional +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, Event +from utils.manager import withdraw_message_manager +import asyncio + + +# 消息撤回 +@run_postprocessor +async def _( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: Event, + state: T_State, +): + tasks = [] + for id_, time in withdraw_message_manager.data: + tasks.append(asyncio.ensure_future(_withdraw_message(bot, id_, time))) + withdraw_message_manager.remove((id_, time)) + await asyncio.gather(*tasks) + + +async def _withdraw_message(bot: Bot, id_: int, time: int): + await asyncio.sleep(time) + await bot.delete_msg(message_id=id_, self_id=int(bot.self_id)) diff --git a/basic_plugins/init_plugin_config/__init__.py b/basic_plugins/init_plugin_config/__init__.py old mode 100644 new mode 100755 index 0b4efb6c..65156ada --- a/basic_plugins/init_plugin_config/__init__.py +++ b/basic_plugins/init_plugin_config/__init__.py @@ -9,10 +9,12 @@ from .init_plugins_limit import ( init_plugins_count_limit, init_plugins_cd_limit, ) +from .init import init from .check_plugin_status import check_plugin_status from nonebot.adapters.cqhttp import Bot from configs.path_config import DATA_PATH from services.log import logger +from pathlib import Path from nonebot import Driver import nonebot @@ -30,6 +32,11 @@ def _(): """ 初始化数据 """ + _flag = False + config_file = Path(DATA_PATH) / "configs" / "plugins2config.yaml" + if not config_file.exists(): + _flag = True + init() init_plugins_settings(DATA_PATH) init_plugins_cd_limit(DATA_PATH) init_plugins_block_limit(DATA_PATH) @@ -42,6 +49,8 @@ def _(): if x: for key in x.keys(): plugins_manager.block_plugin(key, block_type=x[key]) + if _flag: + raise Exception("首次运行,已在configs目录下生成配置文件config.yaml,修改后重启即可...") logger.info("初始化数据完成...") diff --git a/basic_plugins/init_plugin_config/check_plugin_status.py b/basic_plugins/init_plugin_config/check_plugin_status.py old mode 100644 new mode 100755 index ad2bd049..c8cbee77 --- a/basic_plugins/init_plugin_config/check_plugin_status.py +++ b/basic_plugins/init_plugin_config/check_plugin_status.py @@ -1,18 +1,18 @@ -from utils.manager import plugins_manager -from nonebot.adapters.cqhttp import Bot - - -async def check_plugin_status(bot: Bot): - """ - 遍历查看插件加载情况 - """ - rst = "" - for plugin in plugins_manager.keys(): - data = plugins_manager.get(plugin) - if data.get("error") or data.get("error") is None: - rst += f'{plugin}:{data["plugin_name"]}\n' - if rst: - rst = "以下插件加载失败..\n" + rst - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), message=rst[:-1] - ) +from utils.manager import plugins_manager +from nonebot.adapters.cqhttp import Bot + + +async def check_plugin_status(bot: Bot): + """ + 遍历查看插件加载情况 + """ + rst = "" + for plugin in plugins_manager.keys(): + data = plugins_manager.get(plugin) + if data.get("error") or data.get("error") is None: + rst += f'{plugin}:{data["plugin_name"]}\n' + if rst: + rst = "以下插件加载失败..\n" + rst + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), message=rst[:-1] + ) diff --git a/basic_plugins/init_plugin_config/init.py b/basic_plugins/init_plugin_config/init.py new file mode 100644 index 00000000..d8640828 --- /dev/null +++ b/basic_plugins/init_plugin_config/init.py @@ -0,0 +1,14 @@ +from utils.manager import plugins2settings_manager + + +def init(): + if plugins2settings_manager.get("update_pic"): + plugins2settings_manager["update_picture"] = plugins2settings_manager["update_pic"] + plugins2settings_manager.delete("update_pic") + if plugins2settings_manager.get("white2black_img"): + plugins2settings_manager["white2black_image"] = plugins2settings_manager["white2black_img"] + plugins2settings_manager.delete("white2black_img") + if plugins2settings_manager.get("send_img"): + plugins2settings_manager["send_image"] = plugins2settings_manager["send_img"] + plugins2settings_manager.delete("send_img") + diff --git a/basic_plugins/init_plugin_config/init_group_manager.py b/basic_plugins/init_plugin_config/init_group_manager.py old mode 100644 new mode 100755 index 3b91ae03..58a1a744 --- a/basic_plugins/init_plugin_config/init_group_manager.py +++ b/basic_plugins/init_plugin_config/init_group_manager.py @@ -1,73 +1,73 @@ -from pathlib import Path -from utils.manager import group_manager -from services.db_context import db -from asyncpg.exceptions import DuplicateColumnError -from services.log import logger - -try: - import ujson as json -except ModuleNotFoundError: - import json -try: - from models.group_remind import GroupRemind -except ModuleNotFoundError: - pass - - -async def init_group_manager(): - """ - 旧数据格式替换为新格式 - 初始化数据 - """ - old_group_level_file = Path() / "data" / "manager" / "group_level.json" - old_plugin_list_file = Path() / "data" / "manager" / "plugin_list.json" - if old_group_level_file.exists(): - data = json.load(open(old_group_level_file, "r", encoding="utf8")) - for key in data.keys(): - group = key - level = data[key] - group_manager.set_group_level(group, level) - old_group_level_file.unlink() - group_manager.save() - - if old_plugin_list_file.exists(): - data = json.load(open(old_plugin_list_file, "r", encoding="utf8")) - for plugin in data.keys(): - for group in data[plugin].keys(): - if group == "default" and not data[plugin]["default"]: - group_manager.block_plugin(plugin) - elif not data[plugin][group]: - group_manager.block_plugin(plugin, group) - old_plugin_list_file.unlink() - old_data_table = Path() / "models" / "group_remind.py" - try: - if old_data_table.exists(): - b = { - "hy": "group_welcome", - "kxcz": "open_case_reset_remind", - "zwa": "zwa", - "blpar": "bilibili_parse", - "epic": "epic_free_game", - "pa": "pa", - "almanac": "genshin_alc", - } - for group in group_manager.get_data()["group_manager"]: - for remind in b: - try: - status = await GroupRemind.get_status(int(group), remind) - if status is not None: - if status: - await group_manager.open_group_task(group, b[remind]) - logger.info(f"读取旧数据-->{group} 开启 {b[remind]}") - else: - await group_manager.close_group_task(group, b[remind]) - logger.info(f"读取旧数据-->{group} 关闭 {b[remind]}") - except Exception as e: - pass - query = db.text("DROP TABLE group_reminds;") - await db.first(query) - old_data_table.unlink() - logger.info("旧数据读取完毕,删除了舍弃表 group_reminds...") - except (ModuleNotFoundError, DuplicateColumnError): - pass - group_manager.save() +from pathlib import Path +from utils.manager import group_manager +from services.db_context import db +from asyncpg.exceptions import DuplicateColumnError +from services.log import logger + +try: + import ujson as json +except ModuleNotFoundError: + import json +try: + from models.group_remind import GroupRemind +except ModuleNotFoundError: + pass + + +async def init_group_manager(): + """ + 旧数据格式替换为新格式 + 初始化数据 + """ + old_group_level_file = Path() / "data" / "manager" / "group_level.json" + old_plugin_list_file = Path() / "data" / "manager" / "plugin_list.json" + if old_group_level_file.exists(): + data = json.load(open(old_group_level_file, "r", encoding="utf8")) + for key in data.keys(): + group = key + level = data[key] + group_manager.set_group_level(group, level) + old_group_level_file.unlink() + group_manager.save() + + if old_plugin_list_file.exists(): + data = json.load(open(old_plugin_list_file, "r", encoding="utf8")) + for plugin in data.keys(): + for group in data[plugin].keys(): + if group == "default" and not data[plugin]["default"]: + group_manager.block_plugin(plugin) + elif not data[plugin][group]: + group_manager.block_plugin(plugin, group) + old_plugin_list_file.unlink() + old_data_table = Path() / "models" / "group_remind.py" + try: + if old_data_table.exists(): + b = { + "hy": "group_welcome", + "kxcz": "open_case_reset_remind", + "zwa": "zwa", + "blpar": "bilibili_parse", + "epic": "epic_free_game", + "pa": "pa", + "almanac": "genshin_alc", + } + for group in group_manager.get_data()["group_manager"]: + for remind in b: + try: + status = await GroupRemind.get_status(int(group), remind) + if status is not None: + if status: + await group_manager.open_group_task(group, b[remind]) + logger.info(f"读取旧数据-->{group} 开启 {b[remind]}") + else: + await group_manager.close_group_task(group, b[remind]) + logger.info(f"读取旧数据-->{group} 关闭 {b[remind]}") + except Exception as e: + pass + query = db.text("DROP TABLE group_reminds;") + await db.first(query) + old_data_table.unlink() + logger.info("旧数据读取完毕,删除了舍弃表 group_reminds...") + except (ModuleNotFoundError, DuplicateColumnError): + pass + group_manager.save() diff --git a/basic_plugins/init_plugin_config/init_none_plugin_count_manager.py b/basic_plugins/init_plugin_config/init_none_plugin_count_manager.py old mode 100644 new mode 100755 index b89dd422..918ed137 --- a/basic_plugins/init_plugin_config/init_none_plugin_count_manager.py +++ b/basic_plugins/init_plugin_config/init_none_plugin_count_manager.py @@ -1,45 +1,45 @@ -from utils.manager import ( - none_plugin_count_manager, - plugins2count_manager, - plugins2cd_manager, - plugins2settings_manager, - plugins2block_manager, - plugins_manager, - resources_manager -) -from services.log import logger -from utils.utils import get_matchers - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -def init_none_plugin_count_manager(): - """ - 清除已删除插件数据 - """ - modules = [x.module for x in get_matchers()] - for module in none_plugin_count_manager.keys(): - if module not in modules: - none_plugin_count_manager.add_count(module) - else: - none_plugin_count_manager.reset(module) - if none_plugin_count_manager.check(module): - try: - plugin_name = plugins_manager.get(module)["plugin_name"] - except (AttributeError, KeyError): - plugin_name = "" - try: - plugins2settings_manager.delete(module) - plugins2count_manager.delete(module) - plugins2cd_manager.delete(module) - plugins2block_manager.delete(module) - plugins_manager.delete(module) - resources_manager.remove_resource(module) - logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...") - except Exception as e: - logger.error( - f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}" - ) +from utils.manager import ( + none_plugin_count_manager, + plugins2count_manager, + plugins2cd_manager, + plugins2settings_manager, + plugins2block_manager, + plugins_manager, + resources_manager +) +from services.log import logger +from utils.utils import get_matchers + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +def init_none_plugin_count_manager(): + """ + 清除已删除插件数据 + """ + modules = [x.module for x in get_matchers()] + for module in none_plugin_count_manager.keys(): + if module not in modules: + none_plugin_count_manager.add_count(module) + else: + none_plugin_count_manager.reset(module) + if none_plugin_count_manager.check(module): + try: + plugin_name = plugins_manager.get(module)["plugin_name"] + except (AttributeError, KeyError): + plugin_name = "" + try: + plugins2settings_manager.delete(module) + plugins2count_manager.delete(module) + plugins2cd_manager.delete(module) + plugins2block_manager.delete(module) + plugins_manager.delete(module) + resources_manager.remove_resource(module) + logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...") + except Exception as e: + logger.error( + f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}" + ) diff --git a/basic_plugins/init_plugin_config/init_plugins_config.py b/basic_plugins/init_plugin_config/init_plugins_config.py old mode 100644 new mode 100755 index addd8602..c4346075 --- a/basic_plugins/init_plugin_config/init_plugins_config.py +++ b/basic_plugins/init_plugin_config/init_plugins_config.py @@ -1,147 +1,147 @@ -from pathlib import Path -from ruamel.yaml import round_trip_load, round_trip_dump, YAML -from utils.manager import admin_manager, plugins_manager -from configs.config import Config -from services.log import logger -from utils.utils import get_matchers -from ruamel import yaml -import nonebot - - -_yaml = YAML(typ="safe") - - -def init_plugins_config(data_path): - """ - 初始化插件数据配置 - """ - plugins2config_file = Path(data_path) / "configs" / "plugins2config.yaml" - plugins2config_file.parent.mkdir(parents=True, exist_ok=True) - _data = {} - if plugins2config_file.exists(): - _data = _yaml.load(open(plugins2config_file, "r", encoding="utf8")) - _matchers = get_matchers() - for matcher in _matchers: - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - except AttributeError: - continue - try: - plugin_version = _module.__getattribute__("__plugin_version__") - except AttributeError: - plugin_version = None - try: - plugin_configs = _module.__getattribute__("__plugin_configs__") - except AttributeError: - continue - # 插件配置版本更新或为Version为None或不在存储配置内 - if ( - plugin_version is None - or ( - _data.get(matcher.module) - and _data[matcher.module].keys() != plugin_configs.keys() - ) - or plugin_version > plugins_manager.get(matcher.module)["version"] - or matcher.module not in _data.keys() - ): - for key in plugin_configs: - if isinstance(plugin_configs[key], dict): - Config.add_plugin_config( - matcher.module, - key, - plugin_configs[key].get("value"), - help_=plugin_configs[key].get("help"), - default_value=plugin_configs[key].get("default_value"), - _override=True, - ) - else: - Config.add_plugin_config(matcher.module, key, plugin_configs[key]) - else: - plugin_configs = _data[matcher.module] - for key in plugin_configs: - Config.add_plugin_config( - matcher.module, - key, - plugin_configs[key]["value"], - help_=plugin_configs[key]["help"], - default_value=plugin_configs[key]["default_value"], - _override=True, - ) - if not Config.is_empty(): - Config.save() - _data = round_trip_load(open(plugins2config_file, encoding="utf8")) - for plugin in _data.keys(): - try: - plugin_name = plugins_manager.get(plugin)["plugin_name"] - except (AttributeError, TypeError): - plugin_name = plugin - _data[plugin].yaml_set_start_comment(plugin_name, indent=2) - # 初始化未设置的管理员权限等级 - for k, v in Config.get_admin_level_data(): - admin_manager.set_admin_level(k, v) - # 存完插件基本设置 - with open(plugins2config_file, "w", encoding="utf8") as wf: - round_trip_dump( - _data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True - ) - # 再开始读取用户配置 - user_config_file = Path() / "configs" / "config.yaml" - _data = {} - _tmp_data = {} - if user_config_file.exists(): - with open(user_config_file, "r", encoding="utf8") as f: - _data = _yaml.load(f) - for plugin in Config.keys(): - _tmp_data[plugin] = {} - for k in Config[plugin].keys(): - if _data.get(plugin) and k in _data[plugin].keys(): - Config.set_config(plugin, k, _data[plugin][k]) - _tmp_data[plugin][k] = Config.get_config(plugin, k) - Config.save() - temp_file = Path() / "configs" / "temp_config.yaml" - try: - with open(temp_file, "w", encoding="utf8") as wf: - yaml.dump( - _tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True - ) - with open(temp_file, "r", encoding="utf8") as rf: - _data = round_trip_load(rf) - # 添加注释 - for plugin in _data.keys(): - rst = "" - plugin_name = None - try: - plugin_data = Config.get(plugin) - for x in list(Config.get(plugin).keys()): - try: - _x = plugin_data[x].get("name") - if _x: - plugin_name = _x - except AttributeError: - pass - except (KeyError, AttributeError): - plugin_name = None - if not plugin_name: - try: - plugin_name = plugins_manager.get(plugin)["plugin_name"] - except (AttributeError, TypeError): - plugin_name = plugin - plugin_name = ( - plugin_name.replace("[Hidden]", "") - .replace("[Superuser]", "") - .replace("[Admin]", "") - .strip() - ) - rst += plugin_name + "\n" - for k in _data[plugin].keys(): - rst += f'{k}: {Config[plugin][k]["help"]}' + "\n" - _data[plugin].yaml_set_start_comment(rst[:-1], indent=2) - with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf: - round_trip_dump( - _data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True - ) - except Exception as e: - logger.error(f"生成简易配置注释错误 {type(e)}:{e}") - if temp_file.exists(): - temp_file.unlink() +from pathlib import Path +from ruamel.yaml import round_trip_load, round_trip_dump, YAML +from utils.manager import admin_manager, plugins_manager +from configs.config import Config +from services.log import logger +from utils.utils import get_matchers +from ruamel import yaml +import nonebot + + +_yaml = YAML(typ="safe") + + +def init_plugins_config(data_path): + """ + 初始化插件数据配置 + """ + plugins2config_file = Path(data_path) / "configs" / "plugins2config.yaml" + plugins2config_file.parent.mkdir(parents=True, exist_ok=True) + _data = {} + if plugins2config_file.exists(): + _data = _yaml.load(open(plugins2config_file, "r", encoding="utf8")) + _matchers = get_matchers() + for matcher in _matchers: + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + except AttributeError: + continue + try: + plugin_version = _module.__getattribute__("__plugin_version__") + except AttributeError: + plugin_version = None + try: + plugin_configs = _module.__getattribute__("__plugin_configs__") + except AttributeError: + continue + # 插件配置版本更新或为Version为None或不在存储配置内 + if ( + plugin_version is None + or ( + _data.get(matcher.module) + and _data[matcher.module].keys() != plugin_configs.keys() + ) + or plugin_version > plugins_manager.get(matcher.module)["version"] + or matcher.module not in _data.keys() + ): + for key in plugin_configs: + if isinstance(plugin_configs[key], dict): + Config.add_plugin_config( + matcher.module, + key, + plugin_configs[key].get("value"), + help_=plugin_configs[key].get("help"), + default_value=plugin_configs[key].get("default_value"), + _override=True, + ) + else: + Config.add_plugin_config(matcher.module, key, plugin_configs[key]) + else: + plugin_configs = _data[matcher.module] + for key in plugin_configs: + Config.add_plugin_config( + matcher.module, + key, + plugin_configs[key]["value"], + help_=plugin_configs[key]["help"], + default_value=plugin_configs[key]["default_value"], + _override=True, + ) + if not Config.is_empty(): + Config.save() + _data = round_trip_load(open(plugins2config_file, encoding="utf8")) + for plugin in _data.keys(): + try: + plugin_name = plugins_manager.get(plugin)["plugin_name"] + except (AttributeError, TypeError): + plugin_name = plugin + _data[plugin].yaml_set_start_comment(plugin_name, indent=2) + # 初始化未设置的管理员权限等级 + for k, v in Config.get_admin_level_data(): + admin_manager.set_admin_level(k, v) + # 存完插件基本设置 + with open(plugins2config_file, "w", encoding="utf8") as wf: + round_trip_dump( + _data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True + ) + # 再开始读取用户配置 + user_config_file = Path() / "configs" / "config.yaml" + _data = {} + _tmp_data = {} + if user_config_file.exists(): + with open(user_config_file, "r", encoding="utf8") as f: + _data = _yaml.load(f) + for plugin in Config.keys(): + _tmp_data[plugin] = {} + for k in Config[plugin].keys(): + if _data.get(plugin) and k in _data[plugin].keys(): + Config.set_config(plugin, k, _data[plugin][k]) + _tmp_data[plugin][k] = Config.get_config(plugin, k) + Config.save() + temp_file = Path() / "configs" / "temp_config.yaml" + try: + with open(temp_file, "w", encoding="utf8") as wf: + yaml.dump( + _tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True + ) + with open(temp_file, "r", encoding="utf8") as rf: + _data = round_trip_load(rf) + # 添加注释 + for plugin in _data.keys(): + rst = "" + plugin_name = None + try: + plugin_data = Config.get(plugin) + for x in list(Config.get(plugin).keys()): + try: + _x = plugin_data[x].get("name") + if _x: + plugin_name = _x + except AttributeError: + pass + except (KeyError, AttributeError): + plugin_name = None + if not plugin_name: + try: + plugin_name = plugins_manager.get(plugin)["plugin_name"] + except (AttributeError, TypeError): + plugin_name = plugin + plugin_name = ( + plugin_name.replace("[Hidden]", "") + .replace("[Superuser]", "") + .replace("[Admin]", "") + .strip() + ) + rst += plugin_name + "\n" + for k in _data[plugin].keys(): + rst += f'{k}: {Config[plugin][k]["help"]}' + "\n" + _data[plugin].yaml_set_start_comment(rst[:-1], indent=2) + with open(Path() / "configs" / "config.yaml", "w", encoding="utf8") as wf: + round_trip_dump( + _data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True + ) + except Exception as e: + logger.error(f"生成简易配置注释错误 {type(e)}:{e}") + if temp_file.exists(): + temp_file.unlink() diff --git a/basic_plugins/init_plugin_config/init_plugins_data.py b/basic_plugins/init_plugin_config/init_plugins_data.py old mode 100644 new mode 100755 index e90d5238..ca17eaa0 --- a/basic_plugins/init_plugin_config/init_plugins_data.py +++ b/basic_plugins/init_plugin_config/init_plugins_data.py @@ -1,82 +1,82 @@ -from pathlib import Path -from ruamel.yaml import YAML -from utils.manager import plugins_manager -from utils.utils import get_matchers -import nonebot - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -_yaml = YAML(typ="safe") - - -def init_plugins_data(data_path): - """ - 初始化插件数据信息 - """ - plugin2data_file = Path(data_path) / "manager" / "plugin_manager.json" - plugin2data_file.parent.mkdir(parents=True, exist_ok=True) - _data = {} - if plugin2data_file.exists(): - _data = json.load(open(plugin2data_file, "r", encoding="utf8")) - _matchers = get_matchers() - for matcher in _matchers: - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - except AttributeError: - if matcher.module not in _data.keys(): - plugins_manager.add_plugin_data( - matcher.module, matcher.module, error=True - ) - else: - plugins_manager.set_module_data(matcher.module, "error", True) - plugin_data = plugins_manager.get(matcher.module) - if plugin_data: - plugins_manager.set_module_data( - matcher.module, "version", plugin_data.get("version") - ) - else: - try: - plugin_version = _module.__getattribute__("__plugin_version__") - except AttributeError: - plugin_version = None - try: - plugin_name = _module.__getattribute__("__zx_plugin_name__") - except AttributeError: - plugin_name = matcher.module - try: - plugin_author = _module.__getattribute__("__plugin_author__") - except AttributeError: - plugin_author = None - if matcher.module in plugins_manager.keys(): - plugins_manager.set_module_data(matcher.module, "error", False) - if matcher.module not in plugins_manager.keys(): - plugins_manager.add_plugin_data( - matcher.module, - plugin_name=plugin_name, - author=plugin_author, - version=plugin_version, - ) - elif plugins_manager[matcher.module]["version"] is None or ( - plugin_version is not None - and plugin_version > plugins_manager[matcher.module]["version"] - ): - plugins_manager.set_module_data( - matcher.module, "plugin_name", plugin_name - ) - plugins_manager.set_module_data(matcher.module, "author", plugin_author) - plugins_manager.set_module_data( - matcher.module, "version", plugin_version - ) - if matcher.module in _data.keys(): - plugins_manager.set_module_data( - matcher.module, "error", _data[matcher.module]["error"] - ) - plugins_manager.set_module_data( - matcher.module, "plugin_name", _data[matcher.module]["plugin_name"] - ) - plugins_manager.save() +from pathlib import Path +from ruamel.yaml import YAML +from utils.manager import plugins_manager +from utils.utils import get_matchers +import nonebot + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +_yaml = YAML(typ="safe") + + +def init_plugins_data(data_path): + """ + 初始化插件数据信息 + """ + plugin2data_file = Path(data_path) / "manager" / "plugin_manager.json" + plugin2data_file.parent.mkdir(parents=True, exist_ok=True) + _data = {} + if plugin2data_file.exists(): + _data = json.load(open(plugin2data_file, "r", encoding="utf8")) + _matchers = get_matchers() + for matcher in _matchers: + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + except AttributeError: + if matcher.module not in _data.keys(): + plugins_manager.add_plugin_data( + matcher.module, matcher.module, error=True + ) + else: + plugins_manager.set_module_data(matcher.module, "error", True) + plugin_data = plugins_manager.get(matcher.module) + if plugin_data: + plugins_manager.set_module_data( + matcher.module, "version", plugin_data.get("version") + ) + else: + try: + plugin_version = _module.__getattribute__("__plugin_version__") + except AttributeError: + plugin_version = None + try: + plugin_name = _module.__getattribute__("__zx_plugin_name__") + except AttributeError: + plugin_name = matcher.module + try: + plugin_author = _module.__getattribute__("__plugin_author__") + except AttributeError: + plugin_author = None + if matcher.module in plugins_manager.keys(): + plugins_manager.set_module_data(matcher.module, "error", False) + if matcher.module not in plugins_manager.keys(): + plugins_manager.add_plugin_data( + matcher.module, + plugin_name=plugin_name, + author=plugin_author, + version=plugin_version, + ) + elif plugins_manager[matcher.module]["version"] is None or ( + plugin_version is not None + and plugin_version > plugins_manager[matcher.module]["version"] + ): + plugins_manager.set_module_data( + matcher.module, "plugin_name", plugin_name + ) + plugins_manager.set_module_data(matcher.module, "author", plugin_author) + plugins_manager.set_module_data( + matcher.module, "version", plugin_version + ) + if matcher.module in _data.keys(): + plugins_manager.set_module_data( + matcher.module, "error", _data[matcher.module]["error"] + ) + plugins_manager.set_module_data( + matcher.module, "plugin_name", _data[matcher.module]["plugin_name"] + ) + plugins_manager.save() diff --git a/basic_plugins/init_plugin_config/init_plugins_limit.py b/basic_plugins/init_plugin_config/init_plugins_limit.py old mode 100644 new mode 100755 index 459f747c..f507b4c5 --- a/basic_plugins/init_plugin_config/init_plugins_limit.py +++ b/basic_plugins/init_plugin_config/init_plugins_limit.py @@ -1,157 +1,157 @@ -from pathlib import Path -from ruamel.yaml import round_trip_load, round_trip_dump, YAML -from utils.manager import ( - plugins2cd_manager, - plugins2block_manager, - plugins2count_manager, -) -from utils.utils import get_matchers -from ruamel import yaml -import nonebot - - -_yaml = YAML(typ="safe") - - -def init_plugins_cd_limit(data_path): - """ - 加载 cd 限制 - """ - plugins2cd_file = Path(data_path) / "configs" / "plugins2cd.yaml" - plugins2cd_file.parent.mkdir(exist_ok=True, parents=True) - _data = {} - _matchers = get_matchers() - for matcher in _matchers: - if not plugins2cd_manager.get_plugin_cd_data(matcher.module): - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__") - plugins2cd_manager.add_cd_limit( - matcher.module, data_dict=plugin_cd_limit - ) - except AttributeError: - pass - if not plugins2cd_manager.keys(): - plugins2cd_manager.add_cd_limit( - "这是一个示例" - ) - _tmp_data = {"PluginCdLimit": plugins2cd_manager.get_data()} - with open(plugins2cd_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - _data = round_trip_load(open(plugins2cd_file, encoding="utf8")) - _data["PluginCdLimit"].yaml_set_start_comment( - """# 需要cd的功能 -# 自定义的功能需要cd也可以在此配置 -# key:模块名称 -# cd:cd 时长(秒) -# status:此限制的开关状态 -# check_type:'private'/'group'/'all',限制私聊/群聊/全部 -# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id -# 示例:'user':用户N秒内触发1次,'group':群N秒内触发1次 -# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称 -# rst 为 "" 或 None 时则不回复 -# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]" -# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批" -# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""", - indent=2, - ) - with open(plugins2cd_file, "w", encoding="utf8") as wf: - round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - plugins2cd_manager.reload_cd_limit() - - -def init_plugins_block_limit(data_path): - """ - 加载阻塞限制 - """ - plugins2block_file = Path(data_path) / "configs" / "plugins2block.yaml" - plugins2block_file.parent.mkdir(exist_ok=True, parents=True) - _data = {} - _matchers = get_matchers() - for matcher in _matchers: - if not plugins2block_manager.get_plugin_block_data(matcher.module): - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - plugin_block_limit = _module.__getattribute__("__plugin_block_limit__") - plugins2block_manager.add_block_limit( - matcher.module, data_dict=plugin_block_limit - ) - except AttributeError: - pass - if not plugins2block_manager.keys(): - plugins2block_manager.add_block_limit( - "这是一个示例" - ) - _tmp_data = {"PluginBlockLimit": plugins2block_manager.get_data()} - with open(plugins2block_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - _data = round_trip_load(open(plugins2block_file, encoding="utf8")) - _data["PluginBlockLimit"].yaml_set_start_comment( - """# 用户调用阻塞 -# 即 当用户调用此功能还未结束时 -# 用发送消息阻止用户重复调用此命令直到该命令结束 -# key:模块名称 -# status:此限制的开关状态 -# check_type:'private'/'group'/'all',限制私聊/群聊/全部 -# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id -# 示例:'user':阻塞用户,'group':阻塞群聊 -# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称 -# rst 为 "" 或 None 时则不回复 -# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]" -# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批" -# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""", - indent=2, - ) - with open(plugins2block_file, "w", encoding="utf8") as wf: - round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - plugins2block_manager.reload_block_limit() - - -def init_plugins_count_limit(data_path): - """ - 加载次数限制 - """ - plugins2count_file = Path(data_path) / "configs" / "plugins2count.yaml" - plugins2count_file.parent.mkdir(exist_ok=True, parents=True) - _data = {} - _matchers = get_matchers() - for matcher in _matchers: - if not plugins2count_manager.get_plugin_count_data(matcher.module): - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - plugin_count_limit = _module.__getattribute__("__plugin_count_limit__") - plugins2count_manager.add_count_limit( - matcher.module, data_dict=plugin_count_limit - ) - except AttributeError: - pass - if not plugins2count_manager.keys(): - plugins2count_manager.add_count_limit( - "这是一个示例" - ) - _tmp_data = {"PluginCountLimit": plugins2count_manager.get_data()} - with open(plugins2count_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - _data = round_trip_load(open(plugins2count_file, encoding="utf8")) - _data["PluginCountLimit"].yaml_set_start_comment( - """# 命令每日次数限制 -# 即 用户/群聊 每日可调用命令的次数 [数据内存存储,重启将会重置] -# 每日调用直到 00:00 刷新 -# key:模块名称 -# max_count: 每日调用上限 -# status:此限制的开关状态 -# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id -# 示例:'user':用户上限,'group':群聊上限 -# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称 -# rst 为 "" 或 None 时则不回复 -# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]" -# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批" -# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""", - indent=2, - ) - with open(plugins2count_file, "w", encoding="utf8") as wf: - round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - plugins2count_manager.reload_count_limit() +from pathlib import Path +from ruamel.yaml import round_trip_load, round_trip_dump, YAML +from utils.manager import ( + plugins2cd_manager, + plugins2block_manager, + plugins2count_manager, +) +from utils.utils import get_matchers +from ruamel import yaml +import nonebot + + +_yaml = YAML(typ="safe") + + +def init_plugins_cd_limit(data_path): + """ + 加载 cd 限制 + """ + plugins2cd_file = Path(data_path) / "configs" / "plugins2cd.yaml" + plugins2cd_file.parent.mkdir(exist_ok=True, parents=True) + _data = {} + _matchers = get_matchers() + for matcher in _matchers: + if not plugins2cd_manager.get_plugin_cd_data(matcher.module): + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__") + plugins2cd_manager.add_cd_limit( + matcher.module, data_dict=plugin_cd_limit + ) + except AttributeError: + pass + if not plugins2cd_manager.keys(): + plugins2cd_manager.add_cd_limit( + "这是一个示例" + ) + _tmp_data = {"PluginCdLimit": plugins2cd_manager.get_data()} + with open(plugins2cd_file, "w", encoding="utf8") as wf: + yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + _data = round_trip_load(open(plugins2cd_file, encoding="utf8")) + _data["PluginCdLimit"].yaml_set_start_comment( + """# 需要cd的功能 +# 自定义的功能需要cd也可以在此配置 +# key:模块名称 +# cd:cd 时长(秒) +# status:此限制的开关状态 +# check_type:'private'/'group'/'all',限制私聊/群聊/全部 +# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id +# 示例:'user':用户N秒内触发1次,'group':群N秒内触发1次 +# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称 +# rst 为 "" 或 None 时则不回复 +# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]" +# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批" +# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""", + indent=2, + ) + with open(plugins2cd_file, "w", encoding="utf8") as wf: + round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + plugins2cd_manager.reload_cd_limit() + + +def init_plugins_block_limit(data_path): + """ + 加载阻塞限制 + """ + plugins2block_file = Path(data_path) / "configs" / "plugins2block.yaml" + plugins2block_file.parent.mkdir(exist_ok=True, parents=True) + _data = {} + _matchers = get_matchers() + for matcher in _matchers: + if not plugins2block_manager.get_plugin_block_data(matcher.module): + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + plugin_block_limit = _module.__getattribute__("__plugin_block_limit__") + plugins2block_manager.add_block_limit( + matcher.module, data_dict=plugin_block_limit + ) + except AttributeError: + pass + if not plugins2block_manager.keys(): + plugins2block_manager.add_block_limit( + "这是一个示例" + ) + _tmp_data = {"PluginBlockLimit": plugins2block_manager.get_data()} + with open(plugins2block_file, "w", encoding="utf8") as wf: + yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + _data = round_trip_load(open(plugins2block_file, encoding="utf8")) + _data["PluginBlockLimit"].yaml_set_start_comment( + """# 用户调用阻塞 +# 即 当用户调用此功能还未结束时 +# 用发送消息阻止用户重复调用此命令直到该命令结束 +# key:模块名称 +# status:此限制的开关状态 +# check_type:'private'/'group'/'all',限制私聊/群聊/全部 +# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id +# 示例:'user':阻塞用户,'group':阻塞群聊 +# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称 +# rst 为 "" 或 None 时则不回复 +# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]" +# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批" +# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""", + indent=2, + ) + with open(plugins2block_file, "w", encoding="utf8") as wf: + round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + plugins2block_manager.reload_block_limit() + + +def init_plugins_count_limit(data_path): + """ + 加载次数限制 + """ + plugins2count_file = Path(data_path) / "configs" / "plugins2count.yaml" + plugins2count_file.parent.mkdir(exist_ok=True, parents=True) + _data = {} + _matchers = get_matchers() + for matcher in _matchers: + if not plugins2count_manager.get_plugin_count_data(matcher.module): + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + plugin_count_limit = _module.__getattribute__("__plugin_count_limit__") + plugins2count_manager.add_count_limit( + matcher.module, data_dict=plugin_count_limit + ) + except AttributeError: + pass + if not plugins2count_manager.keys(): + plugins2count_manager.add_count_limit( + "这是一个示例" + ) + _tmp_data = {"PluginCountLimit": plugins2count_manager.get_data()} + with open(plugins2count_file, "w", encoding="utf8") as wf: + yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + _data = round_trip_load(open(plugins2count_file, encoding="utf8")) + _data["PluginCountLimit"].yaml_set_start_comment( + """# 命令每日次数限制 +# 即 用户/群聊 每日可调用命令的次数 [数据内存存储,重启将会重置] +# 每日调用直到 00:00 刷新 +# key:模块名称 +# max_count: 每日调用上限 +# status:此限制的开关状态 +# limit_type:监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id +# 示例:'user':用户上限,'group':群聊上限 +# rst:回复的话,可以添加[at],[uname],[nickname]来对应艾特,用户群名称,昵称系统昵称 +# rst 为 "" 或 None 时则不回复 +# rst示例:"[uname]你冲的太快了,[nickname]先生,请稍后再冲[at]" +# rst回复:"老色批你冲的太快了,欧尼酱先生,请稍后再冲@老色批" +# 用户昵称↑ 昵称系统的昵称↑ 艾特用户↑""", + indent=2, + ) + with open(plugins2count_file, "w", encoding="utf8") as wf: + round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + plugins2count_manager.reload_count_limit() diff --git a/basic_plugins/init_plugin_config/init_plugins_resources.py b/basic_plugins/init_plugin_config/init_plugins_resources.py old mode 100644 new mode 100755 index 36e2f47b..1fdfa4b3 --- a/basic_plugins/init_plugin_config/init_plugins_resources.py +++ b/basic_plugins/init_plugin_config/init_plugins_resources.py @@ -1,43 +1,43 @@ -from utils.manager import resources_manager -from utils.utils import get_matchers -from services.log import logger -from pathlib import Path -import nonebot - - -def init_plugins_resources(): - """ - 资源文件路径的移动 - """ - _tmp = [] - for matcher in get_matchers(): - if matcher.module not in _tmp: - _tmp.append(matcher.module) - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - except AttributeError: - logger.warning(f"插件 {matcher.module} 加载失败...,资源控制未加载...") - else: - try: - resources = _module.__getattribute__("__plugin_resources__") - except AttributeError: - pass - else: - path = Path(_module.__getattribute__("__file__")).parent - for resource in resources.keys(): - resources_manager.add_resource(matcher.module, (path / resource).absolute(), resources[resource]) - resources_manager.save() - resources_manager.start_move() - - - - - - - - - - - - +from utils.manager import resources_manager +from utils.utils import get_matchers +from services.log import logger +from pathlib import Path +import nonebot + + +def init_plugins_resources(): + """ + 资源文件路径的移动 + """ + _tmp = [] + for matcher in get_matchers(): + if matcher.module not in _tmp: + _tmp.append(matcher.module) + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + except AttributeError: + logger.warning(f"插件 {matcher.module} 加载失败...,资源控制未加载...") + else: + try: + resources = _module.__getattribute__("__plugin_resources__") + except AttributeError: + pass + else: + path = Path(_module.__getattribute__("__file__")).parent + for resource in resources.keys(): + resources_manager.add_resource(matcher.module, (path / resource).absolute(), resources[resource]) + resources_manager.save() + resources_manager.start_move() + + + + + + + + + + + + diff --git a/basic_plugins/init_plugin_config/init_plugins_settings.py b/basic_plugins/init_plugin_config/init_plugins_settings.py old mode 100644 new mode 100755 index c1ec6c11..fff027d9 --- a/basic_plugins/init_plugin_config/init_plugins_settings.py +++ b/basic_plugins/init_plugin_config/init_plugins_settings.py @@ -1,130 +1,126 @@ -from pathlib import Path -from ruamel.yaml import round_trip_load, round_trip_dump, YAML -from utils.manager import plugins2settings_manager, admin_manager -from services.log import logger -from utils.utils import get_matchers -from ruamel import yaml -import nonebot - - -_yaml = YAML(typ="safe") - - -def init_plugins_settings(data_path: str): - """ - 初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__ - """ - plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml" - plugins2settings_file.parent.mkdir(exist_ok=True, parents=True) - _matchers = get_matchers() - _data = {} - if plugins2settings_file.exists(): - with open(plugins2settings_file, "r", encoding="utf8") as f: - _data = _yaml.load(f) - _data = _data["PluginSettings"] if _data else {} - _tmp_module = {} - _tmp = [] - for matcher in _matchers: - if matcher.module in _data.keys(): - plugins2settings_manager.add_plugin_settings( - matcher.module, - plugin_type=_data[matcher.module]["plugin_type"], - data_dict=_data[matcher.module], - ) - if _data[matcher.module]["cmd"]: - _tmp_module[matcher.module] = _data[matcher.module]["cmd"][0] - else: - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - except AttributeError: - logger.warning(f"插件 {matcher.module} 加载失败...,插件控制未加载.") - else: - try: - plugin_name = _module.__getattribute__("__zx_plugin_name__") - if "[admin]" in plugin_name.lower(): - try: - admin_settings = _module.__getattribute__( - "__plugin_settings__" - ) - level = admin_settings["admin_level"] - cmd = admin_settings.get("cmd") - except (AttributeError, KeyError): - level = 5 - cmd = None - if not level: - level = 5 - admin_manager.add_admin_plugin_settings( - matcher.module, cmd, level - ) - if ( - "[hidden]" in plugin_name.lower() - or "[admin]" in plugin_name.lower() - or "[superuser]" in plugin_name.lower() - or matcher.module in plugins2settings_manager.keys() - ): - continue - except AttributeError: - if matcher.module not in _tmp: - logger.warning( - f"获取插件 {matcher.module} __zx_plugin_name__ 失败...,插件控制未加载." - ) - else: - try: - _tmp_module[matcher.module] = plugin_name - plugin_settings = _module.__getattribute__( - "__plugin_settings__" - ) - if ( - plugin_settings["cmd"] - and plugin_name not in plugin_settings["cmd"] - ): - plugin_settings["cmd"].append(plugin_name) - if plugins2settings_manager.get( - matcher.module - ) and plugins2settings_manager[matcher.module].get( - "plugin_type" - ): - plugin_type = tuple( - plugins2settings_manager.get_plugin_data( - matcher.module - )["plugin_type"] - ) - else: - try: - plugin_type = _module.__getattribute__( - "__plugin_type__" - ) - except AttributeError: - plugin_type = ("normal",) - if plugin_settings and matcher.module: - plugins2settings_manager.add_plugin_settings( - matcher.module, - plugin_type=plugin_type, - data_dict=plugin_settings, - ) - except AttributeError: - pass - _tmp.append(matcher.module) - _tmp_data = {"PluginSettings": plugins2settings_manager.get_data()} - with open(plugins2settings_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - _data = round_trip_load(open(plugins2settings_file, encoding="utf8")) - _data["PluginSettings"].yaml_set_start_comment( - """# 模块与对应命令和对应群权限 -# 用于生成帮助图片 和 开关功能 -# key:模块名称 -# level:需要的群等级 -# default_status:加入群时功能的默认开关状态 -# limit_superuser: 功能状态是否限制超级用户 -# cmd: 关闭[cmd] 都会触发命令 关闭对应功能,cmd列表第一个词为统计的功能名称 -# plugin_type: 帮助类别 示例:('原神相关',) 或 ('原神相关', 1),1代表帮助命令列向排列,否则为横向排列""", - indent=2, - ) - for plugin in _data["PluginSettings"].keys(): - _data["PluginSettings"][plugin].yaml_set_start_comment( - f"{plugin}:{_tmp_module[plugin]}", indent=2 - ) - with open(plugins2settings_file, "w", encoding="utf8") as wf: - round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) - logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.") +from pathlib import Path +from ruamel.yaml import round_trip_load, round_trip_dump, YAML +from utils.manager import plugins2settings_manager, admin_manager +from services.log import logger +from utils.utils import get_matchers +from ruamel import yaml +import nonebot + + +_yaml = YAML(typ="safe") + + +def init_plugins_settings(data_path: str): + """ + 初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__ + """ + plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml" + plugins2settings_file.parent.mkdir(exist_ok=True, parents=True) + _matchers = get_matchers() + _tmp_module = {} + _tmp = [] + for x in plugins2settings_manager.keys(): + try: + _plugin = nonebot.plugin.get_plugin(x) + _module = _plugin.module + plugin_name = _module.__getattribute__("__zx_plugin_name__") + _tmp_module[x] = plugin_name + except (KeyError, AttributeError) as e: + logger.error(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}") + _tmp_module[x] = "" + for matcher in _matchers: + if matcher.module not in plugins2settings_manager.keys(): + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + except AttributeError: + logger.warning(f"插件 {matcher.module} 加载失败...,插件控制未加载.") + else: + try: + plugin_name = _module.__getattribute__("__zx_plugin_name__") + if "[admin]" in plugin_name.lower(): + try: + admin_settings = _module.__getattribute__( + "__plugin_settings__" + ) + level = admin_settings["admin_level"] + cmd = admin_settings.get("cmd") + except (AttributeError, KeyError): + level = 5 + cmd = None + if level is None: + level = 5 + admin_manager.add_admin_plugin_settings( + matcher.module, cmd, level + ) + if ( + "[hidden]" in plugin_name.lower() + or "[admin]" in plugin_name.lower() + or "[superuser]" in plugin_name.lower() + or matcher.module in plugins2settings_manager.keys() + ): + continue + except AttributeError: + if matcher.module not in _tmp: + logger.warning( + f"获取插件 {matcher.module} __zx_plugin_name__ 失败...,插件控制未加载." + ) + else: + try: + _tmp_module[matcher.module] = plugin_name + plugin_settings = _module.__getattribute__( + "__plugin_settings__" + ) + if ( + plugin_settings["cmd"] is not None + and plugin_name not in plugin_settings["cmd"] + ): + plugin_settings["cmd"].append(plugin_name) + if plugins2settings_manager.get( + matcher.module + ) and plugins2settings_manager[matcher.module].get( + "plugin_type" + ): + plugin_type = tuple( + plugins2settings_manager.get_plugin_data( + matcher.module + )["plugin_type"] + ) + else: + try: + plugin_type = _module.__getattribute__( + "__plugin_type__" + ) + except AttributeError: + plugin_type = ("normal",) + if plugin_settings and matcher.module: + plugins2settings_manager.add_plugin_settings( + matcher.module, + plugin_type=plugin_type, + data_dict=plugin_settings, + ) + except AttributeError: + pass + _tmp.append(matcher.module) + _tmp_data = {"PluginSettings": plugins2settings_manager.get_data()} + with open(plugins2settings_file, "w", encoding="utf8") as wf: + yaml.dump(_tmp_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + _data = round_trip_load(open(plugins2settings_file, encoding="utf8")) + _data["PluginSettings"].yaml_set_start_comment( + """# 模块与对应命令和对应群权限 +# 用于生成帮助图片 和 开关功能 +# key:模块名称 +# level:需要的群等级 +# default_status:加入群时功能的默认开关状态 +# limit_superuser: 功能状态是否限制超级用户 +# cmd: 关闭[cmd] 都会触发命令 关闭对应功能,cmd列表第一个词为统计的功能名称 +# plugin_type: 帮助类别 示例:('原神相关',) 或 ('原神相关', 1),1代表帮助命令列向排列,否则为横向排列""", + indent=2, + ) + for plugin in _data["PluginSettings"].keys(): + _data["PluginSettings"][plugin].yaml_set_start_comment( + f"{plugin}:{_tmp_module[plugin]}", indent=2 + ) + with open(plugins2settings_file, "w", encoding="utf8") as wf: + round_trip_dump(_data, wf, Dumper=yaml.RoundTripDumper, allow_unicode=True) + logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.") diff --git a/basic_plugins/auto_invite/__init__.py b/basic_plugins/invite_manager/__init__.py old mode 100644 new mode 100755 similarity index 94% rename from basic_plugins/auto_invite/__init__.py rename to basic_plugins/invite_manager/__init__.py index d0d91701..857cac4a --- a/basic_plugins/auto_invite/__init__.py +++ b/basic_plugins/invite_manager/__init__.py @@ -1,149 +1,149 @@ -from nonebot import on_request, on_message -from nonebot.adapters.cqhttp import ( - Bot, - FriendRequestEvent, - GroupRequestEvent, - MessageEvent, -) -from models.friend_user import FriendUser -from datetime import datetime -from configs.config import NICKNAME, Config -from nonebot.adapters.cqhttp.exception import ActionFailed -from utils.manager import requests_manager -from models.group_info import GroupInfo -from utils.utils import scheduler -import asyncio -import time -import re - -__zx_plugin_name__ = "好友群聊处理请求 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_configs__ = { - "AUTO_ADD_FRIEND": {"value": False, "help": "是否自动同意好友添加", "default_value": False} -} - -friend_req = on_request(priority=5, block=True) -group_req = on_request(priority=5, block=True) -x = on_message(priority=9, block=False) - -exists_data = {"private": {}, "group": {}} - - -@friend_req.handle() -async def _(bot: Bot, event: FriendRequestEvent, state: dict): - global exists_data - if exists_data["private"].get(event.user_id): - if time.time() - exists_data["private"][event.user_id] < 60 * 5: - return - exists_data["private"][event.user_id] = time.time() - user = await bot.get_stranger_info(user_id=event.user_id) - nickname = user["nickname"] - sex = user["sex"] - age = str(user["age"]) - comment = event.comment - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"*****一份好友申请*****\n" - f"昵称:{nickname}({event.user_id})\n" - f"自动同意:{'√' if Config.get_config('auto_invite', 'AUTO_ADD_FRIEND') else '×'}\n" - f"日期:{str(datetime.now()).split('.')[0]}\n" - f"备注:{event.comment}", - ) - if Config.get_config("auto_invite", "AUTO_ADD_FRIEND"): - await bot.set_friend_add_request(flag=event.flag, approve=True) - await FriendUser.add_friend_info(user["user_id"], user["nickname"]) - else: - requests_manager.add_request( - event.user_id, - "private", - event.flag, - nickname=nickname, - sex=sex, - age=age, - comment=comment, - ) - - -@group_req.handle() -async def _(bot: Bot, event: GroupRequestEvent, state: dict): - global exists_data - if event.sub_type == "invite": - if str(event.user_id) in bot.config.superusers: - try: - if await GroupInfo.get_group_info(event.group_id): - await GroupInfo.set_group_flag(event.group_id, 1) - else: - group_info = await bot.get_group_info(group_id=event.group_id) - await GroupInfo.add_group_info( - group_info["group_id"], - group_info["group_name"], - group_info["max_member_count"], - group_info["member_count"], - 1, - ) - await bot.set_group_add_request( - flag=event.flag, sub_type="invite", approve=True - ) - except ActionFailed: - pass - else: - user = await bot.get_stranger_info(user_id=event.user_id) - sex = user["sex"] - age = str(user["age"]) - if exists_data["group"].get(f"{event.user_id}:{event.group_id}"): - if ( - time.time() - - exists_data["group"][f"{event.user_id}:{event.group_id}"] - < 60 * 5 - ): - return - exists_data["group"][f"{event.user_id}:{event.group_id}"] = time.time() - nickname = await FriendUser.get_user_name(event.user_id) - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"*****一份入群申请*****\n" - f"申请人:{nickname}({event.user_id})\n" - f"群聊:{event.group_id}\n" - f"邀请日期:{str(datetime.now()).split('.')[0]}", - ) - await bot.send_private_msg( - user_id=event.user_id, - message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n" - "请确保已经群主或群管理沟通过!\n" - "等待管理员处理吧!", - ) - requests_manager.add_request( - event.user_id, - "group", - event.flag, - invite_group=event.group_id, - nickname=nickname, - sex=sex, - age=age, - ) - - -@x.handle() -async def _(bot: Bot, event: MessageEvent, state: dict): - await asyncio.sleep(0.1) - r = re.search(r'groupcode="(.*?)"', str(event.get_message())) - if r: - group_id = int(r.group(1)) - else: - return - r = re.search(r'groupname="(.*?)"', str(event.get_message())) - if r: - group_name = r.group(1) - else: - group_name = "None" - requests_manager.set_group_name(group_name, group_id) - - -@scheduler.scheduled_job( - "interval", - minutes=5, -) -async def _(): - global exists_data - exists_data = {"private": {}, "group": {}} +from nonebot import on_request, on_message +from nonebot.adapters.cqhttp import ( + Bot, + FriendRequestEvent, + GroupRequestEvent, + MessageEvent, +) +from models.friend_user import FriendUser +from datetime import datetime +from configs.config import NICKNAME, Config +from nonebot.adapters.cqhttp.exception import ActionFailed +from utils.manager import requests_manager +from models.group_info import GroupInfo +from utils.utils import scheduler +import asyncio +import time +import re + +__zx_plugin_name__ = "好友群聊处理请求 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_configs__ = { + "AUTO_ADD_FRIEND": {"value": False, "help": "是否自动同意好友添加", "default_value": False} +} + +friend_req = on_request(priority=5, block=True) +group_req = on_request(priority=5, block=True) +x = on_message(priority=9, block=False) + +exists_data = {"private": {}, "group": {}} + + +@friend_req.handle() +async def _(bot: Bot, event: FriendRequestEvent, state: dict): + global exists_data + if exists_data["private"].get(event.user_id): + if time.time() - exists_data["private"][event.user_id] < 60 * 5: + return + exists_data["private"][event.user_id] = time.time() + user = await bot.get_stranger_info(user_id=event.user_id) + nickname = user["nickname"] + sex = user["sex"] + age = str(user["age"]) + comment = event.comment + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"*****一份好友申请*****\n" + f"昵称:{nickname}({event.user_id})\n" + f"自动同意:{'√' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n" + f"日期:{str(datetime.now()).split('.')[0]}\n" + f"备注:{event.comment}", + ) + if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"): + await bot.set_friend_add_request(flag=event.flag, approve=True) + await FriendUser.add_friend_info(user["user_id"], user["nickname"]) + else: + requests_manager.add_request( + event.user_id, + "private", + event.flag, + nickname=nickname, + sex=sex, + age=age, + comment=comment, + ) + + +@group_req.handle() +async def _(bot: Bot, event: GroupRequestEvent, state: dict): + global exists_data + if event.sub_type == "invite": + if str(event.user_id) in bot.config.superusers: + try: + if await GroupInfo.get_group_info(event.group_id): + await GroupInfo.set_group_flag(event.group_id, 1) + else: + group_info = await bot.get_group_info(group_id=event.group_id) + await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + 1, + ) + await bot.set_group_add_request( + flag=event.flag, sub_type="invite", approve=True + ) + except ActionFailed: + pass + else: + user = await bot.get_stranger_info(user_id=event.user_id) + sex = user["sex"] + age = str(user["age"]) + if exists_data["group"].get(f"{event.user_id}:{event.group_id}"): + if ( + time.time() + - exists_data["group"][f"{event.user_id}:{event.group_id}"] + < 60 * 5 + ): + return + exists_data["group"][f"{event.user_id}:{event.group_id}"] = time.time() + nickname = await FriendUser.get_user_name(event.user_id) + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"*****一份入群申请*****\n" + f"申请人:{nickname}({event.user_id})\n" + f"群聊:{event.group_id}\n" + f"邀请日期:{str(datetime.now()).split('.')[0]}", + ) + await bot.send_private_msg( + user_id=event.user_id, + message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n" + "请确保已经群主或群管理沟通过!\n" + "等待管理员处理吧!", + ) + requests_manager.add_request( + event.user_id, + "group", + event.flag, + invite_group=event.group_id, + nickname=nickname, + sex=sex, + age=age, + ) + + +@x.handle() +async def _(bot: Bot, event: MessageEvent, state: dict): + await asyncio.sleep(0.1) + r = re.search(r'groupcode="(.*?)"', str(event.get_message())) + if r: + group_id = int(r.group(1)) + else: + return + r = re.search(r'groupname="(.*?)"', str(event.get_message())) + if r: + group_name = r.group(1) + else: + group_name = "None" + requests_manager.set_group_name(group_name, group_id) + + +@scheduler.scheduled_job( + "interval", + minutes=5, +) +async def _(): + global exists_data + exists_data = {"private": {}, "group": {}} diff --git a/basic_plugins/nickname.py b/basic_plugins/nickname.py old mode 100644 new mode 100755 index 75e497d4..f06018b8 --- a/basic_plugins/nickname.py +++ b/basic_plugins/nickname.py @@ -1,194 +1,194 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent -from nonebot.rule import to_me -from utils.utils import get_message_text -from models.group_member_info import GroupInfoUser -from models.friend_user import FriendUser -import random -from models.ban_user import BanUser -from services.log import logger -from configs.config import NICKNAME - - -__zx_plugin_name__ = "昵称系统" -__plugin_usage__ = f""" -usage: - 个人昵称系统,群聊 与 私聊 昵称相互独立 - 指令: - 以后叫我 [昵称] - {NICKNAME}我是谁 -""".strip() -__plugin_des__ = "区区昵称,才不想叫呢!" -__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - -nickname = on_command( - "nickname", - aliases={"以后叫我", "以后请叫我", "称呼我", "以后请称呼我", "以后称呼我", "叫我", "请叫我"}, - rule=to_me(), - priority=5, - block=True, -) - -my_nickname = on_command( - "my_name", aliases={"我叫什么", "我是谁", "我的名字"}, rule=to_me(), priority=5, block=True -) - - -cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=True) - - -@nickname.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - msg = get_message_text(event.json()) - if not msg: - await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) - if len(msg) > 10: - await nickname.finish("昵称可不能超过10个字!", at_sender=True) - if await GroupInfoUser.set_group_member_nickname( - event.user_id, event.group_id, msg - ): - if len(msg) < 5: - if random.random() < 0.5: - msg = "~".join(msg) - await nickname.send( - random.choice( - [ - f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", - f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", - f"好突然,突然要叫你昵称什么的...{msg}..", - f"{NICKNAME}会好好记住的{msg}的,放心吧", - f"好..好.,那窝以后就叫你{msg}了.", - ] - ) - ) - logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}") - else: - await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True) - logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败") - - -@nickname.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - msg = get_message_text(event.json()) - if not msg: - await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) - if len(msg) > 10: - await nickname.finish("不要超过10个字!", at_sender=True) - if await FriendUser.set_friend_nickname(event.user_id, msg): - await nickname.send( - random.choice( - [ - f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", - f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", - f"好突然,突然要叫你昵称什么的...{msg}..", - f"{NICKNAME}会好好记住的{msg}的,放心吧", - f"好..好.,那窝以后就叫你{msg}了.", - ] - ) - ) - logger.info(f"USER {event.user_id} 设置昵称 {msg}") - else: - await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True) - logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败") - - -@my_nickname.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - try: - nickname_ = await GroupInfoUser.get_group_member_nickname( - event.user_id, event.group_id - ) - except AttributeError: - nickname_ = "" - if nickname_: - await my_nickname.send( - random.choice( - [ - f"我肯定记得你啊,你是{nickname_}啊", - f"我不会忘记你的,你也不要忘记我!{nickname_}", - f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}", - f"嗯?你是失忆了嘛...{nickname_}..", - f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ", - f"哎?{nickname_}..怎么了吗..突然这样问..", - ] - ) - ) - else: - nickname_ = event.sender.card if event.sender.card else event.sender.nickname - await my_nickname.send( - random.choice( - ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] - ).format(nickname_) - ) - - -@my_nickname.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - nickname_ = await FriendUser.get_friend_nickname(event.user_id) - if nickname_: - await my_nickname.send( - random.choice( - [ - f"我肯定记得你啊,你是{nickname_}啊", - f"我不会忘记你的,你也不要忘记我!{nickname_}", - f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}", - f"嗯?你是失忆了嘛...{nickname_}..", - f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ", - f"哎?{nickname_}..怎么了吗..突然这样问..", - ] - ) - ) - else: - nickname_ = (await bot.get_stranger_info(user_id=event.user_id))["nickname"] - await my_nickname.send( - random.choice( - ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] - ).format(nickname_) - ) - - -@cancel_nickname.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - nickname_ = await GroupInfoUser.get_group_member_nickname( - event.user_id, event.group_id - ) - if nickname_: - await cancel_nickname.send( - random.choice( - [ - f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}", - f"窝知道了..{nickname_}..", - f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}", - f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!", - f"可..可恶!{nickname_}!太可恶了!呜", - ] - ) - ) - await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, "") - await BanUser.ban(event.user_id, 9, 60) - else: - await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True) - - -@cancel_nickname.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - nickname_ = await FriendUser.get_friend_nickname(event.user_id) - if nickname_: - await cancel_nickname.send( - random.choice( - [ - f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}", - f"窝知道了..{nickname_}..", - f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}", - f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!", - f"可..可恶!{nickname_}!太可恶了!呜", - ] - ) - ) - await FriendUser.get_user_name(event.user_id) - await BanUser.ban(event.user_id, 9, 60) - else: - await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True) +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent +from nonebot.rule import to_me +from utils.utils import get_message_text +from models.group_member_info import GroupInfoUser +from models.friend_user import FriendUser +import random +from models.ban_user import BanUser +from services.log import logger +from configs.config import NICKNAME + + +__zx_plugin_name__ = "昵称系统" +__plugin_usage__ = f""" +usage: + 个人昵称系统,群聊 与 私聊 昵称相互独立 + 指令: + 以后叫我 [昵称] + {NICKNAME}我是谁 +""".strip() +__plugin_des__ = "区区昵称,才不想叫呢!" +__plugin_cmd__ = ["以后叫我 [昵称]", f"{NICKNAME}我是谁"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + +nickname = on_command( + "nickname", + aliases={"以后叫我", "以后请叫我", "称呼我", "以后请称呼我", "以后称呼我", "叫我", "请叫我"}, + rule=to_me(), + priority=5, + block=True, +) + +my_nickname = on_command( + "my_name", aliases={"我叫什么", "我是谁", "我的名字"}, rule=to_me(), priority=5, block=True +) + + +cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=True) + + +@nickname.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) + if len(msg) > 10: + await nickname.finish("昵称可不能超过10个字!", at_sender=True) + if await GroupInfoUser.set_group_member_nickname( + event.user_id, event.group_id, msg + ): + if len(msg) < 5: + if random.random() < 0.5: + msg = "~".join(msg) + await nickname.send( + random.choice( + [ + f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", + f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", + f"好突然,突然要叫你昵称什么的...{msg}..", + f"{NICKNAME}会好好记住的{msg}的,放心吧", + f"好..好.,那窝以后就叫你{msg}了.", + ] + ) + ) + logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}") + else: + await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True) + logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败") + + +@nickname.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) + if len(msg) > 10: + await nickname.finish("不要超过10个字!", at_sender=True) + if await FriendUser.set_friend_nickname(event.user_id, msg): + await nickname.send( + random.choice( + [ + f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", + f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", + f"好突然,突然要叫你昵称什么的...{msg}..", + f"{NICKNAME}会好好记住的{msg}的,放心吧", + f"好..好.,那窝以后就叫你{msg}了.", + ] + ) + ) + logger.info(f"USER {event.user_id} 设置昵称 {msg}") + else: + await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True) + logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败") + + +@my_nickname.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + try: + nickname_ = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) + except AttributeError: + nickname_ = "" + if nickname_: + await my_nickname.send( + random.choice( + [ + f"我肯定记得你啊,你是{nickname_}啊", + f"我不会忘记你的,你也不要忘记我!{nickname_}", + f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}", + f"嗯?你是失忆了嘛...{nickname_}..", + f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ", + f"哎?{nickname_}..怎么了吗..突然这样问..", + ] + ) + ) + else: + nickname_ = event.sender.card if event.sender.card else event.sender.nickname + await my_nickname.send( + random.choice( + ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] + ).format(nickname_) + ) + + +@my_nickname.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + nickname_ = await FriendUser.get_friend_nickname(event.user_id) + if nickname_: + await my_nickname.send( + random.choice( + [ + f"我肯定记得你啊,你是{nickname_}啊", + f"我不会忘记你的,你也不要忘记我!{nickname_}", + f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}", + f"嗯?你是失忆了嘛...{nickname_}..", + f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ", + f"哎?{nickname_}..怎么了吗..突然这样问..", + ] + ) + ) + else: + nickname_ = (await bot.get_stranger_info(user_id=event.user_id))["nickname"] + await my_nickname.send( + random.choice( + ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] + ).format(nickname_) + ) + + +@cancel_nickname.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + nickname_ = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) + if nickname_: + await cancel_nickname.send( + random.choice( + [ + f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}", + f"窝知道了..{nickname_}..", + f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}", + f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!", + f"可..可恶!{nickname_}!太可恶了!呜", + ] + ) + ) + await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, "") + await BanUser.ban(event.user_id, 9, 60) + else: + await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True) + + +@cancel_nickname.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + nickname_ = await FriendUser.get_friend_nickname(event.user_id) + if nickname_: + await cancel_nickname.send( + random.choice( + [ + f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}", + f"窝知道了..{nickname_}..", + f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}", + f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!", + f"可..可恶!{nickname_}!太可恶了!呜", + ] + ) + ) + await FriendUser.get_user_name(event.user_id) + await BanUser.ban(event.user_id, 9, 60) + else: + await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True) diff --git a/basic_plugins/scripts.py b/basic_plugins/scripts.py old mode 100644 new mode 100755 index 2a8879e6..11abd3c3 --- a/basic_plugins/scripts.py +++ b/basic_plugins/scripts.py @@ -1,111 +1,103 @@ -from nonebot import Driver -from services.db_context import db -from asyncpg.exceptions import DuplicateColumnError -from models.group_info import GroupInfo -from nonebot.adapters.cqhttp import Bot -from utils.user_agent import get_user_agent -from services.log import logger -from configs.path_config import TEXT_PATH -from asyncio.exceptions import TimeoutError -from pathlib import Path -import aiohttp -import nonebot - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -driver: Driver = nonebot.get_driver() - - -@driver.on_startup -async def update_city(): - """ - 部分插件需要中国省份城市 - 这里直接更新,避免插件内代码重复 - :return: - """ - china_city = Path(TEXT_PATH) / "china_city.json" - data = {} - try: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get( - "http://www.weather.com.cn/data/city3jdata/china.html", timeout=5 - ) as res: - provinces_data = json.loads(await res.text(encoding="utf8")) - for province in provinces_data.keys(): - data[provinces_data[province]] = [] - async with session.get( - f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html", - timeout=5, - ) as res: - city_data = json.loads(await res.text(encoding="utf8")) - for city in city_data.keys(): - data[provinces_data[province]].append(city_data[city]) - with open(china_city, "w", encoding="utf8") as f: - json.dump(data, f, indent=4, ensure_ascii=False) - logger.info("自动更新城市列表完成.....") - except TimeoutError: - logger.info("自动更新城市列表超时.....") - - -@driver.on_startup -async def _(): - """ - 数据库表结构变换 - """ - sql_str = [ - "ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag - ] - for sql in sql_str: - try: - query = db.text(sql) - await db.first(query) - except DuplicateColumnError: - pass - - -@driver.on_bot_connect -async def _(bot: Bot): - """ - 版本某些需要的变换 - """ - # 清空不存在的群聊信息,并将已所有已存在的群聊group_flag设置为1(认证所有已存在的群) - if not await GroupInfo.get_group_info(114514): - # 标识符,该功能只需执行一次 - await GroupInfo.add_group_info( - 114514, - "114514", - 114514, - 114514, - 1 - ) - group_list = await bot.get_group_list() - group_list = [g["group_id"] for g in group_list] - _gl = [x.group_id for x in await GroupInfo.get_all_group()] - if 114514 in _gl: - _gl.remove(114514) - for group_id in _gl: - if group_id in group_list: - if await GroupInfo.get_group_info(group_id): - await GroupInfo.set_group_flag(group_id, 1) - else: - group_info = await bot.get_group_info(group_id=group_id) - await GroupInfo.add_group_info( - group_info["group_id"], - group_info["group_name"], - group_info["max_member_count"], - group_info["member_count"], - 1 - ) - logger.info(f"已将群聊 {group_id} 添加认证...") - else: - await GroupInfo.delete_group_info(group_id) - logger.info(f"移除不存在的群聊信息:{group_id}") - - - - - +from nonebot import Driver +from services.db_context import db +from asyncpg.exceptions import DuplicateColumnError +from models.group_info import GroupInfo +from nonebot.adapters.cqhttp import Bot +from services.log import logger +from configs.path_config import TEXT_PATH +from asyncio.exceptions import TimeoutError +from utils.http_utils import AsyncHttpx +from pathlib import Path +import nonebot + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +driver: Driver = nonebot.get_driver() + + +@driver.on_startup +async def update_city(): + """ + 部分插件需要中国省份城市 + 这里直接更新,避免插件内代码重复 + """ + china_city = Path(TEXT_PATH) / "china_city.json" + data = {} + try: + res = await AsyncHttpx.get( + "http://www.weather.com.cn/data/city3jdata/china.html", timeout=5 + ) + res.encoding = "utf8" + provinces_data = json.loads(res.text) + for province in provinces_data.keys(): + data[provinces_data[province]] = [] + res = await AsyncHttpx.get( + f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html", + timeout=5, + ) + res.encoding = "utf8" + city_data = json.loads(res.text) + for city in city_data.keys(): + data[provinces_data[province]].append(city_data[city]) + with open(china_city, "w", encoding="utf8") as f: + json.dump(data, f, indent=4, ensure_ascii=False) + logger.info("自动更新城市列表完成.....") + except TimeoutError: + logger.warning("自动更新城市列表超时.....") + except ValueError: + logger.warning("自动城市列表失败.....") + except Exception as e: + logger.error(f"自动城市列表未知错误 {type(e)}:{e}") + + +@driver.on_startup +async def _(): + """ + 数据库表结构变换 + """ + sql_str = [ + "ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag + ] + for sql in sql_str: + try: + query = db.text(sql) + await db.first(query) + except DuplicateColumnError: + pass + + +@driver.on_bot_connect +async def _(bot: Bot): + """ + 版本某些需要的变换 + """ + # 清空不存在的群聊信息,并将已所有已存在的群聊group_flag设置为1(认证所有已存在的群) + if not await GroupInfo.get_group_info(114514): + # 标识符,该功能只需执行一次 + await GroupInfo.add_group_info(114514, "114514", 114514, 114514, 1) + group_list = await bot.get_group_list() + group_list = [g["group_id"] for g in group_list] + _gl = [x.group_id for x in await GroupInfo.get_all_group()] + if 114514 in _gl: + _gl.remove(114514) + for group_id in _gl: + if group_id in group_list: + if await GroupInfo.get_group_info(group_id): + await GroupInfo.set_group_flag(group_id, 1) + else: + group_info = await bot.get_group_info(group_id=group_id) + await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + 1, + ) + logger.info(f"已将群聊 {group_id} 添加认证...") + else: + await GroupInfo.delete_group_info(group_id) + logger.info(f"移除不存在的群聊信息:{group_id}") diff --git a/basic_plugins/super_cmd/__init__.py b/basic_plugins/super_cmd/__init__.py old mode 100644 new mode 100755 index b4997936..47831269 --- a/basic_plugins/super_cmd/__init__.py +++ b/basic_plugins/super_cmd/__init__.py @@ -1,10 +1,10 @@ -import nonebot - - -nonebot.load_plugins('basic_plugins/super_cmd') - - - - - - +import nonebot + + +nonebot.load_plugins('basic_plugins/super_cmd') + + + + + + diff --git a/basic_plugins/super_cmd/bot_friend_group.py b/basic_plugins/super_cmd/bot_friend_group.py old mode 100644 new mode 100755 index def933c8..ff4b16ab --- a/basic_plugins/super_cmd/bot_friend_group.py +++ b/basic_plugins/super_cmd/bot_friend_group.py @@ -1,152 +1,152 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message -from nonebot.rule import to_me -from utils.utils import get_message_text, is_number -from utils.manager import requests_manager -from utils.message_builder import image -from models.group_info import GroupInfo - - -__zx_plugin_name__ = "显示所有好友群组 [Superuser]" -__plugin_usage__ = """ -usage: - 显示所有好友群组 - 指令: - 查看所有好友/查看所有群组 - 同意好友请求 [id] - 拒绝好友请求 [id] - 同意群聊请求 [id] - 拒绝群聊请求 [id] - 查看所有请求 - 清空所有请求 -""".strip() -__plugin_des__ = "显示所有好友群组" -__plugin_cmd__ = [ - "查看所有好友/查看所有群组", - "同意好友请求 [id]", - "拒绝好友请求 [id]", - "同意群聊请求 [id]", - "拒绝群聊请求 [id]", - "查看所有请求", - "清空所有请求", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -cls_group = on_command( - "查看所有群组", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) -cls_friend = on_command( - "查看所有好友", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) - -friend_handle = on_command( - "同意好友请求", aliases={"拒绝好友请求"}, permission=SUPERUSER, priority=1, block=True -) - -group_handle = on_command( - "同意群聊请求", aliases={"拒绝群聊请求"}, permission=SUPERUSER, priority=1, block=True -) - -clear_request = on_command("清空所有请求", permission=SUPERUSER, priority=1, block=True) - -cls_request = on_command("查看所有请求", permission=SUPERUSER, priority=1, block=True) - - -@cls_group.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - gl = await bot.get_group_list(self_id=int(bot.self_id)) - msg = ["{group_id} {group_name}".format_map(g) for g in gl] - msg = "\n".join(msg) - msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg - await bot.send_private_msg( - self_id=int(bot.self_id), - user_id=int(list(bot.config.superusers)[0]), - message=msg, - ) - - -@cls_friend.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - gl = await bot.get_friend_list(self_id=int(bot.self_id)) - msg = ["{user_id} {nickname}".format_map(g) for g in gl] - msg = "\n".join(msg) - msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg - await bot.send_private_msg( - self_id=int(bot.self_id), - user_id=int(list(bot.config.superusers)[0]), - message=msg, - ) - - -@friend_handle.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - id_ = get_message_text(event.json()) - if is_number(id_): - id_ = int(id_) - if state["_prefix"]["raw_command"][:2] == "同意": - if await requests_manager.approve(bot, id_, "private"): - await friend_handle.send("同意好友请求成功..") - else: - await friend_handle.send("同意好友请求失败,可能是未找到此id的请求..") - else: - if await requests_manager.refused(bot, id_, "private"): - await friend_handle.send("拒绝好友请求成功..") - else: - await friend_handle.send("拒绝好友请求失败,可能是未找到此id的请求..") - else: - await friend_handle.send("id必须为纯数字!") - - -@group_handle.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - id_ = get_message_text(event.json()) - if is_number(id_): - id_ = int(id_) - if state["_prefix"]["raw_command"][:2] == "同意": - rid = await requests_manager.approve(bot, id_, "group") - if rid: - await friend_handle.send("同意群聊请求成功..") - if await GroupInfo.get_group_info(rid): - await GroupInfo.set_group_flag(rid, 1) - else: - group_info = await bot.get_group_info(group_id=rid) - await GroupInfo.add_group_info( - rid, - group_info["group_name"], - group_info["max_member_count"], - group_info["member_count"], - 1 - ) - else: - await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..") - else: - if await requests_manager.refused(bot, id_, "group"): - await friend_handle.send("拒绝群聊请求成功..") - else: - await friend_handle.send("拒绝群聊请求失败,可能是未找到此id的请求..") - else: - await friend_handle.send("id必须为纯数字!") - - -@cls_request.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - _str = "" - for type_ in ["private", "group"]: - msg = await requests_manager.show(type_) - if msg: - _str += image(b64=msg) - else: - _str += "没有任何好友请求.." if type_ == "private" else "没有任何群聊请求.." - if type_ == "private": - _str += '\n--------------------\n' - await cls_request.send(Message(_str)) - - -@clear_request.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - requests_manager.clear() - await cls_request.send("已清空所有好友/群聊请求..") +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message +from nonebot.rule import to_me +from utils.utils import get_message_text, is_number +from utils.manager import requests_manager +from utils.message_builder import image +from models.group_info import GroupInfo + + +__zx_plugin_name__ = "显示所有好友群组 [Superuser]" +__plugin_usage__ = """ +usage: + 显示所有好友群组 + 指令: + 查看所有好友/查看所有群组 + 同意好友请求 [id] + 拒绝好友请求 [id] + 同意群聊请求 [id] + 拒绝群聊请求 [id] + 查看所有请求 + 清空所有请求 +""".strip() +__plugin_des__ = "显示所有好友群组" +__plugin_cmd__ = [ + "查看所有好友/查看所有群组", + "同意好友请求 [id]", + "拒绝好友请求 [id]", + "同意群聊请求 [id]", + "拒绝群聊请求 [id]", + "查看所有请求", + "清空所有请求", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +cls_group = on_command( + "查看所有群组", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) +cls_friend = on_command( + "查看所有好友", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) + +friend_handle = on_command( + "同意好友请求", aliases={"拒绝好友请求"}, permission=SUPERUSER, priority=1, block=True +) + +group_handle = on_command( + "同意群聊请求", aliases={"拒绝群聊请求"}, permission=SUPERUSER, priority=1, block=True +) + +clear_request = on_command("清空所有请求", permission=SUPERUSER, priority=1, block=True) + +cls_request = on_command("查看所有请求", permission=SUPERUSER, priority=1, block=True) + + +@cls_group.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + gl = await bot.get_group_list(self_id=int(bot.self_id)) + msg = ["{group_id} {group_name}".format_map(g) for g in gl] + msg = "\n".join(msg) + msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg + await bot.send_private_msg( + self_id=int(bot.self_id), + user_id=int(list(bot.config.superusers)[0]), + message=msg, + ) + + +@cls_friend.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + gl = await bot.get_friend_list(self_id=int(bot.self_id)) + msg = ["{user_id} {nickname}".format_map(g) for g in gl] + msg = "\n".join(msg) + msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg + await bot.send_private_msg( + self_id=int(bot.self_id), + user_id=int(list(bot.config.superusers)[0]), + message=msg, + ) + + +@friend_handle.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + id_ = get_message_text(event.json()) + if is_number(id_): + id_ = int(id_) + if state["_prefix"]["raw_command"][:2] == "同意": + if await requests_manager.approve(bot, id_, "private"): + await friend_handle.send("同意好友请求成功..") + else: + await friend_handle.send("同意好友请求失败,可能是未找到此id的请求..") + else: + if await requests_manager.refused(bot, id_, "private"): + await friend_handle.send("拒绝好友请求成功..") + else: + await friend_handle.send("拒绝好友请求失败,可能是未找到此id的请求..") + else: + await friend_handle.send("id必须为纯数字!") + + +@group_handle.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + id_ = get_message_text(event.json()) + if is_number(id_): + id_ = int(id_) + if state["_prefix"]["raw_command"][:2] == "同意": + rid = await requests_manager.approve(bot, id_, "group") + if rid: + await friend_handle.send("同意群聊请求成功..") + if await GroupInfo.get_group_info(rid): + await GroupInfo.set_group_flag(rid, 1) + else: + group_info = await bot.get_group_info(group_id=rid) + await GroupInfo.add_group_info( + rid, + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + 1 + ) + else: + await friend_handle.send("同意群聊请求失败,可能是未找到此id的请求..") + else: + if await requests_manager.refused(bot, id_, "group"): + await friend_handle.send("拒绝群聊请求成功..") + else: + await friend_handle.send("拒绝群聊请求失败,可能是未找到此id的请求..") + else: + await friend_handle.send("id必须为纯数字!") + + +@cls_request.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + _str = "" + for type_ in ["private", "group"]: + msg = await requests_manager.show(type_) + if msg: + _str += image(b64=msg) + else: + _str += "没有任何好友请求.." if type_ == "private" else "没有任何群聊请求.." + if type_ == "private": + _str += '\n--------------------\n' + await cls_request.send(Message(_str)) + + +@clear_request.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + requests_manager.clear() + await cls_request.send("已清空所有好友/群聊请求..") diff --git a/basic_plugins/super_cmd/clear_data.py b/basic_plugins/super_cmd/clear_data.py old mode 100644 new mode 100755 index b77237ad..d7571954 --- a/basic_plugins/super_cmd/clear_data.py +++ b/basic_plugins/super_cmd/clear_data.py @@ -1,67 +1,67 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent -from configs.path_config import TEMP_PATH -from nonebot.rule import to_me -from utils.utils import scheduler -from services.log import logger -from utils.manager import resources_manager -import asyncio -import time -import os - -__zx_plugin_name__ = "清理临时数据 [Superuser]" -__plugin_usage__ = """ -usage: - 清理临时数据 - 指令: - 清理临时数据 -""".strip() -__plugin_des__ = "清理临时数据" -__plugin_cmd__ = [ - "清理临时数据", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -clear_data = on_command( - "清理临时数据", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) - - -resources_manager.add_temp_dir(TEMP_PATH) - - -@clear_data.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - await clear_data.send("开始清理临时数据....") - size = await asyncio.get_event_loop().run_in_executor(None, _clear_data) - await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024)) - - -def _clear_data() -> float: - size = 0 - for dir_ in resources_manager.get_temp_data_dir(): - if dir_.exists(): - for file in os.listdir(dir_): - file = dir_ / file - try: - if time.time() - os.path.getatime(file) > 300: - file_size = os.path.getsize(file) - file.unlink() - size += file_size - except Exception as e: - logger.error(f"清理临时数据错误...{type(e)}:{e}") - return float(size) - - -@scheduler.scheduled_job( - "cron", - hour=1, - minute=1, -) -async def _(): - size = await asyncio.get_event_loop().run_in_executor(None, _clear_data) - logger.info("自动清理临时数据完成," + "共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024)) +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from configs.path_config import TEMP_PATH +from nonebot.rule import to_me +from utils.utils import scheduler +from services.log import logger +from utils.manager import resources_manager +import asyncio +import time +import os + +__zx_plugin_name__ = "清理临时数据 [Superuser]" +__plugin_usage__ = """ +usage: + 清理临时数据 + 指令: + 清理临时数据 +""".strip() +__plugin_des__ = "清理临时数据" +__plugin_cmd__ = [ + "清理临时数据", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +clear_data = on_command( + "清理临时数据", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) + + +resources_manager.add_temp_dir(TEMP_PATH) + + +@clear_data.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await clear_data.send("开始清理临时数据....") + size = await asyncio.get_event_loop().run_in_executor(None, _clear_data) + await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024)) + + +def _clear_data() -> float: + size = 0 + for dir_ in resources_manager.get_temp_data_dir(): + if dir_.exists(): + for file in os.listdir(dir_): + file = dir_ / file + try: + if time.time() - os.path.getatime(file) > 300: + file_size = os.path.getsize(file) + file.unlink() + size += file_size + except Exception as e: + logger.error(f"清理临时数据错误...{type(e)}:{e}") + return float(size) + + +@scheduler.scheduled_job( + "cron", + hour=1, + minute=1, +) +async def _(): + size = await asyncio.get_event_loop().run_in_executor(None, _clear_data) + logger.info("自动清理临时数据完成," + "共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024)) diff --git a/basic_plugins/super_cmd/data_source.py b/basic_plugins/super_cmd/data_source.py old mode 100644 new mode 100755 index 86c06db0..53244823 --- a/basic_plugins/super_cmd/data_source.py +++ b/basic_plugins/super_cmd/data_source.py @@ -1,74 +1,74 @@ - - -# async def open_remind(group: int, name: str) -> str: -# _name = "" -# if name == "zwa": -# _name = "早晚安" -# if name == "dz": -# _name = "地震播报" -# if name == "hy": -# _name = "群欢迎" -# if name == "kxcz": -# _name = "开箱重置提醒" -# if name == "gb": -# _name = "广播" -# if await GroupRemind.get_status(group, name): -# return f"该群已经开启过 {_name} 通知,请勿重复开启!" -# if await GroupRemind.set_status(group, name, True): -# return f"成功开启 {_name} 通知!0v0" -# else: -# return f"开启 {_name} 通知失败了..." -# -# -# async def close_remind(group: int, name: str) -> str: -# _name = "" -# if name == "zwa": -# _name = "早晚安" -# if name == "dz": -# _name = "地震播报" -# if name == "hy": -# _name = "群欢迎" -# if name == "kxcz": -# _name = "开箱重置提醒" -# if name == "gb": -# _name = "广播" -# if not await GroupRemind.get_status(group, name): -# return f"该群已经取消过 {_name} 通知,请勿重复取消!" -# if await GroupRemind.set_status(group, name, False): -# return f"成功关闭 {_name} 通知!0v0" -# else: -# return f"关闭 {_name} 通知失败了..." - - -# cmd_list = ['总开关', '签到', '发送图片', '色图', '黑白草图', 'coser', '鸡汤/语录', '骂我', '开箱', '鲁迅说', '假消息', '商店系统', -# '操作图片', '查询皮肤', '天气', '疫情', '识番', '搜番', '点歌', 'pixiv', 'rss', '方舟一井', '查干员', '骰子娘', '原神一井'] -# -# -# def check_group_switch_json(group_id): -# if not os.path.exists(DATA_PATH + f'rule/group_switch/'): -# os.mkdir(DATA_PATH + f'rule/group_switch/') -# if not os.path.exists(DATA_PATH + f'rule/group_switch/{group_id}.json'): -# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as f: -# data = {} -# for cmd in cmd_list: -# data[cmd] = True -# f.write(json.dumps(data, ensure_ascii=False)) -# else: -# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'r', encoding='utf8') as f: -# try: -# data = json.load(f) -# except ValueError: -# data = {} -# if len(data.keys()) - 1 != len(cmd_list): -# for cmd in cmd_list: -# if cmd not in data.keys(): -# data[cmd] = True -# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as wf: -# wf.write(json.dumps(data, ensure_ascii=False)) -# reload(data) -# for file in os.listdir(DATA_PATH + 'group_help'): -# os.remove(DATA_PATH + f'group_help/{file}') - - -def reload(data): - static_group_dict = data + + +# async def open_remind(group: int, name: str) -> str: +# _name = "" +# if name == "zwa": +# _name = "早晚安" +# if name == "dz": +# _name = "地震播报" +# if name == "hy": +# _name = "群欢迎" +# if name == "kxcz": +# _name = "开箱重置提醒" +# if name == "gb": +# _name = "广播" +# if await GroupRemind.get_status(group, name): +# return f"该群已经开启过 {_name} 通知,请勿重复开启!" +# if await GroupRemind.set_status(group, name, True): +# return f"成功开启 {_name} 通知!0v0" +# else: +# return f"开启 {_name} 通知失败了..." +# +# +# async def close_remind(group: int, name: str) -> str: +# _name = "" +# if name == "zwa": +# _name = "早晚安" +# if name == "dz": +# _name = "地震播报" +# if name == "hy": +# _name = "群欢迎" +# if name == "kxcz": +# _name = "开箱重置提醒" +# if name == "gb": +# _name = "广播" +# if not await GroupRemind.get_status(group, name): +# return f"该群已经取消过 {_name} 通知,请勿重复取消!" +# if await GroupRemind.set_status(group, name, False): +# return f"成功关闭 {_name} 通知!0v0" +# else: +# return f"关闭 {_name} 通知失败了..." + + +# cmd_list = ['总开关', '签到', '发送图片', '色图', '黑白草图', 'coser', '鸡汤/语录', '骂我', '开箱', '鲁迅说', '假消息', '商店系统', +# '操作图片', '查询皮肤', '天气', '疫情', '识番', '搜番', '点歌', 'pixiv', 'rss', '方舟一井', '查干员', '骰子娘', '原神一井'] +# +# +# def check_group_switch_json(group_id): +# if not os.path.exists(DATA_PATH + f'rule/group_switch/'): +# os.mkdir(DATA_PATH + f'rule/group_switch/') +# if not os.path.exists(DATA_PATH + f'rule/group_switch/{group_id}.json'): +# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as f: +# data = {} +# for cmd in cmd_list: +# data[cmd] = True +# f.write(json.dumps(data, ensure_ascii=False)) +# else: +# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'r', encoding='utf8') as f: +# try: +# data = json.load(f) +# except ValueError: +# data = {} +# if len(data.keys()) - 1 != len(cmd_list): +# for cmd in cmd_list: +# if cmd not in data.keys(): +# data[cmd] = True +# with open(DATA_PATH + f'rule/group_switch/{group_id}.json', 'w', encoding='utf8') as wf: +# wf.write(json.dumps(data, ensure_ascii=False)) +# reload(data) +# for file in os.listdir(DATA_PATH + 'group_help'): +# os.remove(DATA_PATH + f'group_help/{file}') + + +def reload(data): + static_group_dict = data diff --git a/basic_plugins/super_cmd/manager_group.py b/basic_plugins/super_cmd/manager_group.py old mode 100644 new mode 100755 index 36d4fb56..e6af40c8 --- a/basic_plugins/super_cmd/manager_group.py +++ b/basic_plugins/super_cmd/manager_group.py @@ -1,200 +1,200 @@ -from nonebot import on_command, on_regex -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GROUP, GroupMessageEvent -from nonebot.rule import to_me -from utils.utils import get_message_text, is_number -from utils.manager import group_manager, plugins2settings_manager -from models.group_info import GroupInfo -from services.log import logger -from configs.config import NICKNAME -from nonebot.adapters.cqhttp.exception import ActionFailed - - -__zx_plugin_name__ = "管理群操作 [Superuser]" -__plugin_usage__ = """ -usage: - 群权限 | 群白名单 | 退出群 操作 - 指令: - 退群 [group_id] - 修改群权限 [group_id] [等级] - 添加群白名单 *[group_id] - 删除群白名单 *[group_id] - 添加群认证 *[group_id] - 删除群认证 *[group_id] - 查看群白名单 -""".strip() -__plugin_des__ = "管理群操作" -__plugin_cmd__ = [ - "退群 [group_id]", - "修改群权限 [group_id] [等级]", - "添加群白名单 *[group_id]", - "删除群白名单 *[group_id]", - "添加群认证 *[group_id]", - "删除群认证 *[group_id]", - "查看群白名单", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -del_group = on_command("退群", rule=to_me(), permission=SUPERUSER, priority=1, block=True) - -add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True) -my_group_level = on_command( - "查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True -) -what_up_group_level = on_regex( - ".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?", - rule=to_me(), - priority=5, - permission=GROUP, - block=True, -) -manager_group_whitelist = on_command( - "添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True -) - -show_group_whitelist = on_command( - "查看群白名单", priority=1, permission=SUPERUSER, block=True -) - -group_auth = on_command( - "添加群认证", aliases={"删除群认证"}, priority=1, permission=SUPERUSER, block=True -) - - -@del_group.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - group_id = get_message_text(event.json()) - if group_id: - if is_number(group_id): - try: - await bot.set_group_leave(group_id=int(group_id)) - logger.info(f"退出群聊 {group_id} 成功") - await del_group.send(f"退出群聊 {group_id} 成功", at_sender=True) - group_manager.delete_group(int(group_id)) - await GroupInfo.delete_group_info(int(group_id)) - except Exception as e: - logger.info(f"退出群聊 {group_id} 失败 e:{e}") - else: - await del_group.finish(f"请输入正确的群号", at_sender=True) - else: - await del_group.finish(f"请输入群号", at_sender=True) - - -@add_group_level.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - group_id = 0 - level = 0 - if not msg: - await add_group_level.finish("用法:修改群权限 [group] [level]") - msg = msg.split(" ") - if len(msg) < 2: - await add_group_level.finish("参数不完全..[group] [level]") - if is_number(msg[0]) and is_number(msg[1]): - group_id = msg[0] - level = int(msg[1]) - else: - await add_group_level.finish("参数错误...group和level必须是数字..") - old_level = group_manager.get_group_level(group_id) - group_manager.set_group_level(group_id, level) - await add_group_level.send("修改成功...", at_sender=True) - if level > -1: - await bot.send_group_msg( - group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}" - ) - logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}") - - -@my_group_level.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - level = group_manager.get_group_level(event.group_id) - tmp = "" - data = plugins2settings_manager.get_data() - for module in data: - if data[module]["level"] > level: - plugin_name = data[module]["cmd"][0] - if plugin_name == "pixiv": - plugin_name = "搜图 p站排行" - tmp += f"{plugin_name}\n" - if tmp: - tmp = "\n目前无法使用的功能:\n" + tmp - await my_group_level.finish(f"当前群权限:{level}{tmp}") - - -@what_up_group_level.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await what_up_group_level.finish( - f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限" - ) - - -@manager_group_whitelist.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()).split() - all_group = [ - g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id)) - ] - group_list = [] - for group in msg: - if is_number(group) and int(group) in all_group: - group_list.append(int(group)) - if group_list: - for group in group_list: - if state["_prefix"]["raw_command"] in ["添加群白名单"]: - group_manager.add_group_white_list(group) - else: - group_manager.delete_group_white_list(group) - group_list = [str(x) for x in group_list] - await manager_group_whitelist.send( - "已成功将 " + "\n".join(group_list) + " " + state["_prefix"]["raw_command"] - ) - else: - await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名") - - -@show_group_whitelist.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - x = group_manager.get_group_white_list() - x = [str(g) for g in x] - if x: - await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(x)) - else: - await show_group_whitelist.send("没有任何群在群白名单...") - - -@group_auth.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()).split() - for group_id in msg: - if not is_number(group_id): - await group_auth.send(f"{group_id}非纯数字,已跳过该项..") - group_id = int(group_id) - if state["_prefix"]["raw_command"][:2] == "添加": - if await GroupInfo.get_group_info(group_id): - await GroupInfo.set_group_flag(group_id, 1) - else: - try: - group_info = await bot.get_group_info(group_id=group_id) - except ActionFailed: - group_info = { - "group_id": group_id, - "group_name": "_", - "max_member_count": -1, - "member_count": -1, - } - await GroupInfo.add_group_info( - group_info["group_id"], - group_info["group_name"], - group_info["max_member_count"], - group_info["member_count"], - 1, - ) - else: - if await GroupInfo.get_group_info(group_id): - await GroupInfo.set_group_flag(group_id, 0) - await group_auth.send( - f'已为 {group_id} {state["_prefix"]["raw_command"][:2]}群认证..' - ) +from nonebot import on_command, on_regex +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GROUP, GroupMessageEvent +from nonebot.rule import to_me +from utils.utils import get_message_text, is_number +from utils.manager import group_manager, plugins2settings_manager +from models.group_info import GroupInfo +from services.log import logger +from configs.config import NICKNAME +from nonebot.adapters.cqhttp.exception import ActionFailed + + +__zx_plugin_name__ = "管理群操作 [Superuser]" +__plugin_usage__ = """ +usage: + 群权限 | 群白名单 | 退出群 操作 + 指令: + 退群 [group_id] + 修改群权限 [group_id] [等级] + 添加群白名单 *[group_id] + 删除群白名单 *[group_id] + 添加群认证 *[group_id] + 删除群认证 *[group_id] + 查看群白名单 +""".strip() +__plugin_des__ = "管理群操作" +__plugin_cmd__ = [ + "退群 [group_id]", + "修改群权限 [group_id] [等级]", + "添加群白名单 *[group_id]", + "删除群白名单 *[group_id]", + "添加群认证 *[group_id]", + "删除群认证 *[group_id]", + "查看群白名单", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +del_group = on_command("退群", rule=to_me(), permission=SUPERUSER, priority=1, block=True) + +add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True) +my_group_level = on_command( + "查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True +) +what_up_group_level = on_regex( + ".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?", + rule=to_me(), + priority=5, + permission=GROUP, + block=True, +) +manager_group_whitelist = on_command( + "添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True +) + +show_group_whitelist = on_command( + "查看群白名单", priority=1, permission=SUPERUSER, block=True +) + +group_auth = on_command( + "添加群认证", aliases={"删除群认证"}, priority=1, permission=SUPERUSER, block=True +) + + +@del_group.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + group_id = get_message_text(event.json()) + if group_id: + if is_number(group_id): + try: + await bot.set_group_leave(group_id=int(group_id)) + logger.info(f"退出群聊 {group_id} 成功") + await del_group.send(f"退出群聊 {group_id} 成功", at_sender=True) + group_manager.delete_group(int(group_id)) + await GroupInfo.delete_group_info(int(group_id)) + except Exception as e: + logger.info(f"退出群聊 {group_id} 失败 e:{e}") + else: + await del_group.finish(f"请输入正确的群号", at_sender=True) + else: + await del_group.finish(f"请输入群号", at_sender=True) + + +@add_group_level.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + group_id = 0 + level = 0 + if not msg: + await add_group_level.finish("用法:修改群权限 [group] [level]") + msg = msg.split(" ") + if len(msg) < 2: + await add_group_level.finish("参数不完全..[group] [level]") + if is_number(msg[0]) and is_number(msg[1]): + group_id = msg[0] + level = int(msg[1]) + else: + await add_group_level.finish("参数错误...group和level必须是数字..") + old_level = group_manager.get_group_level(group_id) + group_manager.set_group_level(group_id, level) + await add_group_level.send("修改成功...", at_sender=True) + if level > -1: + await bot.send_group_msg( + group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}" + ) + logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}") + + +@my_group_level.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + level = group_manager.get_group_level(event.group_id) + tmp = "" + data = plugins2settings_manager.get_data() + for module in data: + if data[module]["level"] > level: + plugin_name = data[module]["cmd"][0] + if plugin_name == "pixiv": + plugin_name = "搜图 p站排行" + tmp += f"{plugin_name}\n" + if tmp: + tmp = "\n目前无法使用的功能:\n" + tmp + await my_group_level.finish(f"当前群权限:{level}{tmp}") + + +@what_up_group_level.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await what_up_group_level.finish( + f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限" + ) + + +@manager_group_whitelist.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).split() + all_group = [ + g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id)) + ] + group_list = [] + for group in msg: + if is_number(group) and int(group) in all_group: + group_list.append(int(group)) + if group_list: + for group in group_list: + if state["_prefix"]["raw_command"] in ["添加群白名单"]: + group_manager.add_group_white_list(group) + else: + group_manager.delete_group_white_list(group) + group_list = [str(x) for x in group_list] + await manager_group_whitelist.send( + "已成功将 " + "\n".join(group_list) + " " + state["_prefix"]["raw_command"] + ) + else: + await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名") + + +@show_group_whitelist.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + x = group_manager.get_group_white_list() + x = [str(g) for g in x] + if x: + await show_group_whitelist.send("目前的群白名单:\n" + "\n".join(x)) + else: + await show_group_whitelist.send("没有任何群在群白名单...") + + +@group_auth.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).split() + for group_id in msg: + if not is_number(group_id): + await group_auth.send(f"{group_id}非纯数字,已跳过该项..") + group_id = int(group_id) + if state["_prefix"]["raw_command"][:2] == "添加": + if await GroupInfo.get_group_info(group_id): + await GroupInfo.set_group_flag(group_id, 1) + else: + try: + group_info = await bot.get_group_info(group_id=group_id) + except ActionFailed: + group_info = { + "group_id": group_id, + "group_name": "_", + "max_member_count": -1, + "member_count": -1, + } + await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + 1, + ) + else: + if await GroupInfo.get_group_info(group_id): + await GroupInfo.set_group_flag(group_id, 0) + await group_auth.send( + f'已为 {group_id} {state["_prefix"]["raw_command"][:2]}群认证..' + ) diff --git a/basic_plugins/super_cmd/reload_setting.py b/basic_plugins/super_cmd/reload_setting.py old mode 100644 new mode 100755 index b392f913..494ee819 --- a/basic_plugins/super_cmd/reload_setting.py +++ b/basic_plugins/super_cmd/reload_setting.py @@ -1,40 +1,40 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.rule import to_me -from utils.manager import ( - plugins2cd_manager, - plugins2settings_manager, - plugins2block_manager, - group_manager, -) -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent - - -__zx_plugin_name__ = "重载插件配置 [Superuser]" -__plugin_usage__ = """ -usage: - 重载插件配置 - 指令: - 重载插件配置 -""".strip() -__plugin_des__ = "重载插件配置" -__plugin_cmd__ = [ - "重载插件配置", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -reload_plugins_manager = on_command( - "重载插件限制", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) - - -@reload_plugins_manager.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - plugins2settings_manager.reload() - plugins2cd_manager.reload() - plugins2block_manager.reload() - group_manager.reload() - await reload_plugins_manager.send("重载完成...") +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.rule import to_me +from utils.manager import ( + plugins2cd_manager, + plugins2settings_manager, + plugins2block_manager, + group_manager, +) +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent + + +__zx_plugin_name__ = "重载插件配置 [Superuser]" +__plugin_usage__ = """ +usage: + 重载插件配置 + 指令: + 重载插件配置 +""".strip() +__plugin_des__ = "重载插件配置" +__plugin_cmd__ = [ + "重载插件配置", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +reload_plugins_manager = on_command( + "重载插件限制", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) + + +@reload_plugins_manager.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + plugins2settings_manager.reload() + plugins2cd_manager.reload() + plugins2block_manager.reload() + group_manager.reload() + await reload_plugins_manager.send("重载完成...") diff --git a/basic_plugins/super_cmd/set_admin_permissions.py b/basic_plugins/super_cmd/set_admin_permissions.py old mode 100644 new mode 100755 index f075dd94..a375af6e --- a/basic_plugins/super_cmd/set_admin_permissions.py +++ b/basic_plugins/super_cmd/set_admin_permissions.py @@ -1,89 +1,89 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from models.level_user import LevelUser -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, Message -from utils.utils import get_message_at, get_message_text, is_number -from services.log import logger -from utils.message_builder import at - -__zx_plugin_name__ = "用户权限管理 [Superuser]" -__plugin_usage__ = """ -usage: - 增删改用户的权限 - 指令: - 添加权限 [at] [权限] - 添加权限 [qq] [group_id] [权限] - 删除权限 [at] -""".strip() -__plugin_des__ = "增删改用户的权限" -__plugin_cmd__ = [ - "添加权限 [at] [权限]", - "添加权限 [qq] [group_id] [权限]", - "删除权限 [at]", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -super_cmd = on_command( - "添加管理", - aliases={"删除管理", "添加权限", "删除权限"}, - priority=1, - permission=SUPERUSER, - block=True, -) - - -@super_cmd.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - group_id = -1 - level = 0 - try: - args = get_message_text(event.json()).strip().split() - qq = get_message_at(event.json()) - flag = -1 - if not qq: - if len(args) > 2: - if is_number(args[0]) and is_number(args[1]) and is_number(args[2]): - qq = int(args[0]) - group_id = int(args[1]) - level = int(args[2]) - flag = 1 - else: - await super_cmd.finish("所有参数必须是数字!", at_sender=True) - else: - await super_cmd.finish( - "权限参数不完全\n\t格式:添加/删除权限 [at] [level]" - "\n\t格式:添加/删除权限 [qq] [group] [level]", - at_sender=True, - ) - else: - qq = qq[0] - group_id = event.group_id - flag = 2 - if state["_prefix"]["raw_command"][:2] == "添加": - if is_number(args[0]): - level = int(args[0]) - else: - await super_cmd.finish("权限等级必须是数字!", at_sender=True) - if await LevelUser.set_level(qq, group_id, level, 1): - result = "添加管理成功, 权限: " + str(level) - else: - result = "管理已存在, 更新权限: " + str(level) - else: - if await LevelUser.delete_level(qq, event.group_id): - result = "删除管理成功!" - else: - result = "该账号无管理权限!" - if flag == 2: - await super_cmd.send(result) - elif flag == 1: - await bot.send_group_msg( - group_id=group_id, - message=Message(f"{at(qq)}管理员修改了你的权限" f"\n--------\n你当前的权限等级:{level}"), - ) - await super_cmd.send("修改成功") - except Exception as e: - await super_cmd.send("执行指令失败!") - logger.error(f"执行指令失败 e:{e}") +from nonebot import on_command +from nonebot.permission import SUPERUSER +from models.level_user import LevelUser +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, Message +from utils.utils import get_message_at, get_message_text, is_number +from services.log import logger +from utils.message_builder import at + +__zx_plugin_name__ = "用户权限管理 [Superuser]" +__plugin_usage__ = """ +usage: + 增删改用户的权限 + 指令: + 添加权限 [at] [权限] + 添加权限 [qq] [group_id] [权限] + 删除权限 [at] +""".strip() +__plugin_des__ = "增删改用户的权限" +__plugin_cmd__ = [ + "添加权限 [at] [权限]", + "添加权限 [qq] [group_id] [权限]", + "删除权限 [at]", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +super_cmd = on_command( + "添加管理", + aliases={"删除管理", "添加权限", "删除权限"}, + priority=1, + permission=SUPERUSER, + block=True, +) + + +@super_cmd.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + group_id = -1 + level = 0 + try: + args = get_message_text(event.json()).strip().split() + qq = get_message_at(event.json()) + flag = -1 + if not qq: + if len(args) > 2: + if is_number(args[0]) and is_number(args[1]) and is_number(args[2]): + qq = int(args[0]) + group_id = int(args[1]) + level = int(args[2]) + flag = 1 + else: + await super_cmd.finish("所有参数必须是数字!", at_sender=True) + else: + await super_cmd.finish( + "权限参数不完全\n\t格式:添加/删除权限 [at] [level]" + "\n\t格式:添加/删除权限 [qq] [group] [level]", + at_sender=True, + ) + else: + qq = qq[0] + group_id = event.group_id + flag = 2 + if state["_prefix"]["raw_command"][:2] == "添加": + if is_number(args[0]): + level = int(args[0]) + else: + await super_cmd.finish("权限等级必须是数字!", at_sender=True) + if await LevelUser.set_level(qq, group_id, level, 1): + result = "添加管理成功, 权限: " + str(level) + else: + result = "管理已存在, 更新权限: " + str(level) + else: + if await LevelUser.delete_level(qq, event.group_id): + result = "删除管理成功!" + else: + result = "该账号无管理权限!" + if flag == 2: + await super_cmd.send(result) + elif flag == 1: + await bot.send_group_msg( + group_id=group_id, + message=Message(f"{at(qq)}管理员修改了你的权限" f"\n--------\n你当前的权限等级:{level}"), + ) + await super_cmd.send("修改成功") + except Exception as e: + await super_cmd.send("执行指令失败!") + logger.error(f"执行指令失败 e:{e}") diff --git a/basic_plugins/super_cmd/super_task_switch.py b/basic_plugins/super_cmd/super_task_switch.py old mode 100644 new mode 100755 index 391d5778..01fb0eca --- a/basic_plugins/super_cmd/super_task_switch.py +++ b/basic_plugins/super_cmd/super_task_switch.py @@ -1,56 +1,56 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent -from nonebot.rule import to_me -from utils.utils import get_message_text, is_number -from services.log import logger -from utils.manager import group_manager - - -__zx_plugin_name__ = "超级用户被动开关 [Superuser]" -__plugin_usage__ = """ -usage: - 超级用户被动开关 - 指令: - 开启/关闭广播通知 -""".strip() -__plugin_des__ = "超级用户被动开关" -__plugin_cmd__ = [ - "开启/关闭广播通知", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -oc_gb = on_command( - "开启广播通知", - aliases={"关闭广播通知"}, - rule=to_me(), - permission=SUPERUSER, - priority=1, - block=True, -) - - -@oc_gb.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - group = get_message_text(event.json()) - if group: - if is_number(group): - group = int(group) - for g in await bot.get_group_list(): - if g["group_id"] == group: - break - else: - await oc_gb.finish("没有加入这个群...", at_sender=True) - if state["_prefix"]["raw_command"] == "开启广播通知": - logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播") - await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True) - else: - logger.info(f"USER {event.user_id} 关闭了 GROUP {group} 的广播") - await oc_gb.finish(await group_manager.close_group_task(group, "broadcast"), at_sender=True) - else: - await oc_gb.finish("请输入正确的群号", at_sender=True) - else: +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.rule import to_me +from utils.utils import get_message_text, is_number +from services.log import logger +from utils.manager import group_manager + + +__zx_plugin_name__ = "超级用户被动开关 [Superuser]" +__plugin_usage__ = """ +usage: + 超级用户被动开关 + 指令: + 开启/关闭广播通知 +""".strip() +__plugin_des__ = "超级用户被动开关" +__plugin_cmd__ = [ + "开启/关闭广播通知", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +oc_gb = on_command( + "开启广播通知", + aliases={"关闭广播通知"}, + rule=to_me(), + permission=SUPERUSER, + priority=1, + block=True, +) + + +@oc_gb.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + group = get_message_text(event.json()) + if group: + if is_number(group): + group = int(group) + for g in await bot.get_group_list(): + if g["group_id"] == group: + break + else: + await oc_gb.finish("没有加入这个群...", at_sender=True) + if state["_prefix"]["raw_command"] == "开启广播通知": + logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播") + await oc_gb.finish(await group_manager.open_group_task(group, "broadcast",), at_sender=True) + else: + logger.info(f"USER {event.user_id} 关闭了 GROUP {group} 的广播") + await oc_gb.finish(await group_manager.close_group_task(group, "broadcast"), at_sender=True) + else: + await oc_gb.finish("请输入正确的群号", at_sender=True) + else: await oc_gb.finish("请输入要关闭广播的群号", at_sender=True) \ No newline at end of file diff --git a/basic_plugins/super_cmd/update_friend_group_info.py b/basic_plugins/super_cmd/update_friend_group_info.py old mode 100644 new mode 100755 index 091e89f8..273bdfe8 --- a/basic_plugins/super_cmd/update_friend_group_info.py +++ b/basic_plugins/super_cmd/update_friend_group_info.py @@ -1,71 +1,71 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent -from nonebot.rule import to_me -from utils.utils import get_bot -from services.log import logger -from models.group_info import GroupInfo -from models.friend_user import FriendUser - - -__zx_plugin_name__ = "更新群/好友信息 [Superuser]" -__plugin_usage__ = """ -usage: - 更新群/好友信息 - 指令: - 更新群信息 - 更新好友信息 -""".strip() -__plugin_des__ = "更新群/好友信息" -__plugin_cmd__ = [ - "更新群信息", - "更新好友信息", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - -update_group_info = on_command( - "更新群信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) -update_friend_info = on_command( - "更新好友信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) - - -@update_group_info.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - bot = get_bot() - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - num = 0 - rst = "" - for g in gl: - group_info = await bot.get_group_info(group_id=g) - if await GroupInfo.add_group_info( - group_info["group_id"], - group_info["group_name"], - group_info["max_member_count"], - group_info["member_count"], - ): - num += 1 - logger.info(f"自动更新群组 {g} 信息成功") - else: - logger.info(f"自动更新群组 {g} 信息失败") - rst += f"{g} 更新失败\n" - await update_group_info.send(f"成功更新了 {num} 个群的信息\n{rst[:-1]}") - - -@update_friend_info.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - num = 0 - rst = "" - fl = await get_bot().get_friend_list() - for f in fl: - if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): - logger.info(f'自动更新好友 {f["user_id"]} 信息成功') - num += 1 - else: - logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') - rst += f'{f["user_id"]} 更新失败\n' - await update_friend_info.send(f"成功更新了 {num} 个好友的信息\n{rst[:-1]}") +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.rule import to_me +from utils.utils import get_bot +from services.log import logger +from models.group_info import GroupInfo +from models.friend_user import FriendUser + + +__zx_plugin_name__ = "更新群/好友信息 [Superuser]" +__plugin_usage__ = """ +usage: + 更新群/好友信息 + 指令: + 更新群信息 + 更新好友信息 +""".strip() +__plugin_des__ = "更新群/好友信息" +__plugin_cmd__ = [ + "更新群信息", + "更新好友信息", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + +update_group_info = on_command( + "更新群信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) +update_friend_info = on_command( + "更新好友信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) + + +@update_group_info.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + bot = get_bot() + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + num = 0 + rst = "" + for g in gl: + group_info = await bot.get_group_info(group_id=g) + if await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + ): + num += 1 + logger.info(f"自动更新群组 {g} 信息成功") + else: + logger.info(f"自动更新群组 {g} 信息失败") + rst += f"{g} 更新失败\n" + await update_group_info.send(f"成功更新了 {num} 个群的信息\n{rst[:-1]}") + + +@update_friend_info.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + num = 0 + rst = "" + fl = await get_bot().get_friend_list() + for f in fl: + if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): + logger.info(f'自动更新好友 {f["user_id"]} 信息成功') + num += 1 + else: + logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') + rst += f'{f["user_id"]} 更新失败\n' + await update_friend_info.send(f"成功更新了 {num} 个好友的信息\n{rst[:-1]}") diff --git a/basic_plugins/super_help/__init__.py b/basic_plugins/super_help/__init__.py old mode 100644 new mode 100755 index 49aa7058..1bdbaf69 --- a/basic_plugins/super_help/__init__.py +++ b/basic_plugins/super_help/__init__.py @@ -1,30 +1,30 @@ -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters import Bot, Event -from nonebot.rule import to_me -from configs.path_config import IMAGE_PATH -from utils.message_builder import image -from .data_source import create_help_image -from pathlib import Path - - -__zx_plugin_name__ = '超级用户帮助 [Superuser]' - - -superuser_help_image = Path(IMAGE_PATH) / 'superuser_help.png' - -if superuser_help_image.exists(): - superuser_help_image.unlink() - -super_help = on_command( - "超级用户帮助", rule=to_me(), priority=1, permission=SUPERUSER, block=True -) - - -@super_help.handle() -async def _(bot: Bot, event: Event, state: T_State): - if not superuser_help_image.exists(): - await create_help_image() - x = image(superuser_help_image) - await super_help.finish(x) +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters import Bot, Event +from nonebot.rule import to_me +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from .data_source import create_help_image +from pathlib import Path + + +__zx_plugin_name__ = '超级用户帮助 [Superuser]' + + +superuser_help_image = Path(IMAGE_PATH) / 'superuser_help.png' + +if superuser_help_image.exists(): + superuser_help_image.unlink() + +super_help = on_command( + "超级用户帮助", rule=to_me(), priority=1, permission=SUPERUSER, block=True +) + + +@super_help.handle() +async def _(bot: Bot, event: Event, state: T_State): + if not superuser_help_image.exists(): + await create_help_image() + x = image(superuser_help_image) + await super_help.finish(x) diff --git a/basic_plugins/super_help/data_source.py b/basic_plugins/super_help/data_source.py old mode 100644 new mode 100755 diff --git a/basic_plugins/update_info.py b/basic_plugins/update_info.py old mode 100644 new mode 100755 index e86e82af..8d11d144 --- a/basic_plugins/update_info.py +++ b/basic_plugins/update_info.py @@ -1,35 +1,35 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, Event -from nonebot.typing import T_State -from utils.message_builder import image - - -__zx_plugin_name__ = "更新信息" -__plugin_usage__ = """ -usage: - 更新信息 - 指令: - 更新信息 -""".strip() -__plugin_des__ = "当前版本的更新信息" -__plugin_cmd__ = ["更新信息"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["更新信息"], -} - - -update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, block=True) - - -@update_info.handle() -async def _(bot: Bot, event: Event, state: T_State): - img = image("update_info.png") - if img: - await update_info.finish(image("update_info.png")) - else: - await update_info.finish("目前没有更新信息哦") +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, Event +from nonebot.typing import T_State +from utils.message_builder import image + + +__zx_plugin_name__ = "更新信息" +__plugin_usage__ = """ +usage: + 更新信息 + 指令: + 更新信息 +""".strip() +__plugin_des__ = "当前版本的更新信息" +__plugin_cmd__ = ["更新信息"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["更新信息"], +} + + +update_info = on_command("更新信息", aliases={"更新日志"}, priority=5, block=True) + + +@update_info.handle() +async def _(bot: Bot, event: Event, state: T_State): + img = image("update_info.png") + if img: + await update_info.finish(image("update_info.png")) + else: + await update_info.finish("目前没有更新信息哦") diff --git a/models/bag_user.py b/models/bag_user.py old mode 100644 new mode 100755 diff --git a/models/ban_user.py b/models/ban_user.py old mode 100644 new mode 100755 index 53f488dd..6c7322e2 --- a/models/ban_user.py +++ b/models/ban_user.py @@ -82,7 +82,7 @@ class BanUser(db.Model): 参数: :param user_qq: 目标用户qq号 :param ban_level: 使用ban命令用户的权限 - :param duration: ban时长 + :param duration: ban时长,秒 """ query = cls.query.where((cls.user_qq == user_qq)) query = query.with_for_update() diff --git a/models/friend_user.py b/models/friend_user.py old mode 100644 new mode 100755 diff --git a/models/group_info.py b/models/group_info.py old mode 100644 new mode 100755 diff --git a/models/group_member_info.py b/models/group_member_info.py old mode 100644 new mode 100755 diff --git a/models/level_user.py b/models/level_user.py old mode 100644 new mode 100755 diff --git a/models/sign_group_user.py b/models/sign_group_user.py old mode 100644 new mode 100755 diff --git a/plugins/Yu-Gi-Oh/__init__.py b/plugins/Yu-Gi-Oh/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/plugins/__init__.py b/plugins/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/aconfig/__init__.py b/plugins/aconfig/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/ai/__init__.py b/plugins/ai/__init__.py old mode 100644 new mode 100755 index fc55b73e..673844dd --- a/plugins/ai/__init__.py +++ b/plugins/ai/__init__.py @@ -24,8 +24,6 @@ __plugin_version__ = 0.1 __plugin_author__ = 'HibiKier' __plugin_settings__ = { "level": 5, - "default_status": True, - "limit_superuser": False, "cmd": ["Ai", "ai", "AI", "aI"], } __plugin_configs__ = { diff --git a/plugins/ai/data_source.py b/plugins/ai/data_source.py old mode 100644 new mode 100755 index 1cbbb2dc..13c8d630 --- a/plugins/ai/data_source.py +++ b/plugins/ai/data_source.py @@ -1,13 +1,12 @@ import os import random import re - -import aiohttp -from aiohttp.client import ClientSession +from utils.http_utils import AsyncHttpx from configs.path_config import IMAGE_PATH, DATA_PATH from services.log import logger from utils.message_builder import image, face from configs.config import Config, NICKNAME +from .utils import ai_message_manager try: import ujson as json @@ -26,7 +25,7 @@ anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8")) async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str: """ - 获取 AI 返回值,顺序:图灵 -> 青云客 + 获取 AI 返回值,顺序: 特殊回复 -> 图灵 -> 青云客 :param text: 问题 :param img_url: 图片链接 :param user_id: 用户id @@ -34,6 +33,11 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) :return: 回答 """ global index + ai_message_manager.add_message(user_id, text) + special_rst = await ai_message_manager.get_result(user_id, nickname) + if special_rst: + ai_message_manager.add_result(user_id, special_rst) + return special_rst if index == 5: index = 0 if len(text) < 6 and random.random() < 0.6: @@ -41,10 +45,9 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) for key in keys: if text.find(key) != -1: return random.choice(anime_data[key]).replace("你", nickname) - async with aiohttp.ClientSession() as sess: - rst = await tu_ling(text, img_url, user_id, sess) - if not rst: - rst = await xie_ai(text, sess) + rst = await tu_ling(text, img_url, user_id) + if not rst: + rst = await xie_ai(text) if not rst: return no_result() if nickname: @@ -55,23 +58,25 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) if nickname.find("大人") == -1: nickname += "大~人~" rst = rst.replace("小主人", nickname).replace("小朋友", nickname) + ai_message_manager.add_result(user_id, rst) + print(ai_message_manager) return rst # 图灵接口 -async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) -> str: +async def tu_ling(text: str, img_url: str, user_id: int) -> str: """ 获取图灵接口的回复 :param text: 问题 :param img_url: 图片链接 :param user_id: 用户id - :param sess: AIOHTTP SESSION :return: 图灵回复 """ global index TL_KEY = Config.get_config("ai", "TL_KEY") + req = None if not TL_KEY: - return '' + return "" try: if text: req = { @@ -98,62 +103,59 @@ async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) -> index = 0 return "" text = "" - async with sess.post(url, json=req) as response: - if response.status != 200: - return no_result() - resp_payload = json.loads(await response.text()) - if int(resp_payload["intent"]["code"]) in [4003]: - return "" - if resp_payload["results"]: - for result in resp_payload["results"]: - if result["resultType"] == "text": - text = result["values"]["text"] - if "请求次数超过" in text: - text = "" + response = await AsyncHttpx.post(url, json=req) + if response.status_code != 200: + return no_result() + resp_payload = json.loads(response.text) + if int(resp_payload["intent"]["code"]) in [4003]: + return "" + if resp_payload["results"]: + for result in resp_payload["results"]: + if result["resultType"] == "text": + text = result["values"]["text"] + if "请求次数超过" in text: + text = "" return text # 屑 AI -async def xie_ai(text: str, sess: ClientSession) -> str: +async def xie_ai(text: str) -> str: """ 获取青云客回复 :param text: 问题 - :param sess: AIOHTTP SESSION :return: 青云可回复 """ - async with sess.get( - f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}" - ) as res: - content = "" - data = json.loads(await res.text()) - if data["result"] == 0: - content = data["content"] - if "菲菲" in content: - content = content.replace("菲菲", NICKNAME) - if "艳儿" in content: - content = content.replace("艳儿", NICKNAME) - if "公众号" in content: - content = "" - if "{br}" in content: - content = content.replace("{br}", "\n") - if "提示" in content: - content = content[: content.find("提示")] - if "淘宝" in content: - return "" - while True: - r = re.search("{face:(.*)}", content) - if r: - id_ = r.group(1) - content = content.replace( - "{" + f"face:{id_}" + "}", str(face(int(id_))) - ) - else: - break - return ( - content - if not content and not Config.get_config("ai", "ALAPI_AI_CHECK") - else await check_text(content, sess) - ) + res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}") + content = "" + data = json.loads(res.text) + if data["result"] == 0: + content = data["content"] + if "菲菲" in content: + content = content.replace("菲菲", NICKNAME) + if "艳儿" in content: + content = content.replace("艳儿", NICKNAME) + if "公众号" in content: + content = "" + if "{br}" in content: + content = content.replace("{br}", "\n") + if "提示" in content: + content = content[: content.find("提示")] + if "淘宝" in content: + return "" + while True: + r = re.search("{face:(.*)}", content) + if r: + id_ = r.group(1) + content = content.replace( + "{" + f"face:{id_}" + "}", str(face(int(id_))) + ) + else: + break + return ( + content + if not content and not Config.get_config("ai", "ALAPI_AI_CHECK") + else await check_text(content) + ) def hello() -> str: @@ -196,21 +198,19 @@ def no_result() -> str: ) -async def check_text(text: str, sess: ClientSession) -> str: +async def check_text(text: str) -> str: """ ALAPI文本检测,主要针对青云客API,检测为恶俗文本改为无回复的回答 :param text: 回复 - :param sess: AIOHTTP SESSION """ if not Config.get_config("alapi", "ALAPI_TOKEN"): return text params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text} try: - async with sess.get(check_url, timeout=2, params=params) as response: - data = await response.json() - if data["code"] == 200: - if data["data"]["conclusion_type"] == 2: - return "" + data = (await AsyncHttpx.get(check_url, timeout=2, params=params)).json() + if data["code"] == 200: + if data["data"]["conclusion_type"] == 2: + return "" except Exception as e: logger.error(f"检测违规文本错误...{type(e)}:{e}") return text diff --git a/plugins/ai/utils.py b/plugins/ai/utils.py new file mode 100755 index 00000000..ebd4f6d8 --- /dev/null +++ b/plugins/ai/utils.py @@ -0,0 +1,137 @@ +from utils.manager import StaticData +from configs.config import NICKNAME +from models.ban_user import BanUser +from typing import Optional +import random +import time + + +class AiMessageManager(StaticData): + def __init__(self): + super().__init__(None) + self._same_message = [ + "为什么要发一样的话?", + "请不要再重复对我说一句话了,不然我就要生气了!", + "别再发这句话了,我已经知道了...", + "你是只会说这一句话吗?", + "[*],你发我也发!", + "[uname],[*]", + f"救命!有笨蛋一直给{NICKNAME}发一样的话!", + "这句话你已经给我发了{}次了,再发就生气!", + ] + self._repeat_message = [ + f"请不要学{NICKNAME}说话", + f"为什么要一直学{NICKNAME}说话?", + "你再学!你再学我就生气了!", + f"呜呜,你是想欺负{NICKNAME}嘛..", + "[uname]不要再学我说话了!", + "再学我说话,我就把你拉进黑名单(生气", + "你再学![uname]是个笨蛋!", + "你已经学我说话{}次了!别再学了!", + ] + + def add_message(self, user_id: int, message: str): + """ + 添加用户消息 + :param user_id: 用户id + :param message: 消息内容 + """ + if message: + if self._data.get(user_id) is None: + self._data[user_id] = { + "time": time.time(), + "message": [], + "result": [], + "repeat_count": 0, + } + if time.time() - self._data[user_id]["time"] > 60 * 10: + self._data[user_id]["message"].clear() + self._data[user_id]["time"] = time.time() + self._data[user_id]["message"].append(message.strip()) + + def add_result(self, user_id: int, message: str): + """ + 添加回复用户的消息 + :param user_id: 用户id + :param message: 回复消息内容 + """ + if message: + if self._data.get(user_id) is None: + self._data[user_id] = { + "time": time.time(), + "message": [], + "result": [], + "repeat_count": 0, + } + if time.time() - self._data[user_id]["time"] > 60 * 10: + self._data[user_id]["result"].clear() + self._data[user_id]["repeat_count"] = 0 + self._data[user_id]["time"] = time.time() + self._data[user_id]["result"].append(message.strip()) + + async def get_result(self, user_id: int, nickname: str) -> Optional[str]: + """ + 特殊消息特殊回复 + :param user_id: 用户id + :param nickname: 用户昵称 + """ + if len(self._data[user_id]["message"]) < 2: + return None + msg = await self._get_user_repeat_message_result(user_id) + if not msg: + msg = await self._get_user_same_message_result(user_id) + if msg: + if "[uname]" in msg: + msg = msg.replace("[uname]", nickname) + if not msg.startswith("生气了!你好烦,闭嘴!") and "[*]" in msg: + msg = msg.replace("[*]", self._data[user_id]["message"][-1]) + return msg + + async def _get_user_same_message_result(self, user_id: int) -> Optional[str]: + """ + 重复消息回复 + :param user_id: 用户id + """ + msg = self._data[user_id]["message"][-1] + cnt = 0 + _tmp = self._data[user_id]["message"][:-1] + _tmp.reverse() + for s in _tmp: + if s == msg: + cnt += 1 + else: + break + if cnt > 1: + if random.random() < 0.5 and cnt > 3: + rand = random.randint(60, 300) + await BanUser.ban(user_id, 9, rand) + self._data[user_id]["message"].clear() + return f"生气了!你好烦,闭嘴!给我老实安静{rand}秒" + return random.choice(self._same_message).format(cnt) + return None + + async def _get_user_repeat_message_result(self, user_id: int) -> Optional[str]: + """ + 复读真寻的消息回复 + :param user_id: 用户id + """ + msg = self._data[user_id]["message"][-1] + if self._data[user_id]["result"]: + rst = self._data[user_id]["result"][-1] + else: + return None + if msg == rst: + self._data[user_id]["repeat_count"] += 1 + cnt = self._data[user_id]["repeat_count"] + if cnt > 1: + if random.random() < 0.5 and cnt > 3: + rand = random.randint(60, 300) + await BanUser.ban(user_id, 9, rand) + self._data[user_id]["result"].clear() + self._data[user_id]["repeat_count"] = 0 + return f"生气了!你好烦,闭嘴!给我老实安静{rand}秒" + return random.choice(self._repeat_message).format(cnt) + return None + + +ai_message_manager = AiMessageManager() diff --git a/plugins/alapi/__init__.py b/plugins/alapi/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/alapi/comments_163.py b/plugins/alapi/comments_163.py old mode 100644 new mode 100755 index b88cc643..ac43c1d2 --- a/plugins/alapi/comments_163.py +++ b/plugins/alapi/comments_163.py @@ -1,46 +1,46 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.typing import T_State -from .data_source import get_data -from services.log import logger - -__zx_plugin_name__ = "网易云热评" -__plugin_usage__ = """ -usage: - 到点了,还是防不了下塔 - 指令: - 网易云热评/到点了/12点了 -""".strip() -__plugin_des__ = "生了个人,我很抱歉" -__plugin_cmd__ = ["网易云热评", "到点了", "12点了"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["网易云热评", "网易云评论", "到点了", "12点了"], -} - - -comments_163 = on_command( - "网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True -) - - -comments_163_url = "https://v2.alapi.cn/api/comment" - - -@comments_163.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - data, code = await get_data(comments_163_url) - if code != 200: - await comments_163.finish(data, at_sender=True) - data = data["data"] - comment = data["comment_content"] - song_name = data["title"] - await comments_163.send(f"{comment}\n\t——《{song_name}》") - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送网易云热评: {comment} \n\t\t————{song_name}" - ) +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from .data_source import get_data +from services.log import logger + +__zx_plugin_name__ = "网易云热评" +__plugin_usage__ = """ +usage: + 到点了,还是防不了下塔 + 指令: + 网易云热评/到点了/12点了 +""".strip() +__plugin_des__ = "生了个人,我很抱歉" +__plugin_cmd__ = ["网易云热评", "到点了", "12点了"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["网易云热评", "网易云评论", "到点了", "12点了"], +} + + +comments_163 = on_command( + "网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True +) + + +comments_163_url = "https://v2.alapi.cn/api/comment" + + +@comments_163.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + data, code = await get_data(comments_163_url) + if code != 200: + await comments_163.finish(data, at_sender=True) + data = data["data"] + comment = data["comment_content"] + song_name = data["title"] + await comments_163.send(f"{comment}\n\t——《{song_name}》") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送网易云热评: {comment} \n\t\t————{song_name}" + ) diff --git a/plugins/alapi/cover.py b/plugins/alapi/cover.py old mode 100644 new mode 100755 index 8d0ce8c9..9f83a452 --- a/plugins/alapi/cover.py +++ b/plugins/alapi/cover.py @@ -1,48 +1,48 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent -from nonebot.typing import T_State -from utils.message_builder import image -from utils.utils import get_message_text -from .data_source import get_data -from services.log import logger - -__zx_plugin_name__ = "b封面" -__plugin_usage__ = """ -usage: - b封面 [链接/av/bv/cv/直播id] - 示例:b封面 av86863038 -""".strip() -__plugin_des__ = "快捷的b站视频封面获取方式" -__plugin_cmd__ = ["b封面/B封面"] -__plugin_type__ = ("一些工具",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["b封面", "B封面"], -} - - -cover = on_command("b封面", aliases={"B封面"}, priority=5, block=True) - - -cover_url = "https://v2.alapi.cn/api/bilibili/cover" - - -@cover.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - params = {"c": msg} - data, code = await get_data(cover_url, params) - if code != 200: - await cover.finish(data, at_sender=True) - data = data["data"] - title = data["title"] - img = data["cover"] - await cover.send(Message(f"title:{title}\n{image(img)}")) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 获取b站封面: {title} url:{img}" - ) +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent +from nonebot.typing import T_State +from utils.message_builder import image +from utils.utils import get_message_text +from .data_source import get_data +from services.log import logger + +__zx_plugin_name__ = "b封面" +__plugin_usage__ = """ +usage: + b封面 [链接/av/bv/cv/直播id] + 示例:b封面 av86863038 +""".strip() +__plugin_des__ = "快捷的b站视频封面获取方式" +__plugin_cmd__ = ["b封面/B封面"] +__plugin_type__ = ("一些工具",) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["b封面", "B封面"], +} + + +cover = on_command("b封面", aliases={"B封面"}, priority=5, block=True) + + +cover_url = "https://v2.alapi.cn/api/bilibili/cover" + + +@cover.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + params = {"c": msg} + data, code = await get_data(cover_url, params) + if code != 200: + await cover.finish(data, at_sender=True) + data = data["data"] + title = data["title"] + img = data["cover"] + await cover.send(Message(f"title:{title}\n{image(img)}")) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 获取b站封面: {title} url:{img}" + ) diff --git a/plugins/alapi/data_source.py b/plugins/alapi/data_source.py old mode 100644 new mode 100755 index 0571e802..ad853dec --- a/plugins/alapi/data_source.py +++ b/plugins/alapi/data_source.py @@ -1,51 +1,49 @@ -from nonebot.adapters.cqhttp import MessageSegment -from utils.image_utils import CreateImg -from utils.message_builder import image -from configs.path_config import IMAGE_PATH -from typing import Optional -from configs.config import Config -import aiohttp - - -async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int": - """ - 获取ALAPI数据 - :param url: 请求链接 - :param params: 参数 - """ - if not params: - params = {} - params["token"] = Config.get_config("alapi", "ALAPI_TOKEN") - async with aiohttp.ClientSession() as session: - try: - async with session.get(url, timeout=2, params=params) as response: - data = await response.json() - if data["code"] == 200: - if not data["data"]: - return "没有搜索到...", 997 - return data, 200 - else: - return f'发生了错误...code:{data["code"]}', 999 - except TimeoutError: - return "超时了....", 998 - - -def gen_wbtop_pic(data: dict) -> MessageSegment: - """ - 生成微博热搜图片 - :param data: 微博热搜数据 - """ - bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979") - wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png") - bk.paste(wbtop_bk) - text_bk = CreateImg(700, 32 * 50, 700, 32, color="#797979") - for i, data in enumerate(data): - title = f"{i+1}. {data['hot_word']}" - hot = data["hot_word_num"] - img = CreateImg(700, 30, font_size=20) - w, h = img.getsize(title) - img.text((10, int((30 - h) / 2)), title) - img.text((580, int((30 - h) / 2)), hot) - text_bk.paste(img) - bk.paste(text_bk, (0, 280)) - return image(b64=bk.pic2bs4()) +from nonebot.adapters.cqhttp import MessageSegment +from utils.image_utils import CreateImg +from utils.message_builder import image +from configs.path_config import IMAGE_PATH +from typing import Optional +from configs.config import Config +from utils.http_utils import AsyncHttpx + + +async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int": + """ + 获取ALAPI数据 + :param url: 请求链接 + :param params: 参数 + """ + if not params: + params = {} + params["token"] = Config.get_config("alapi", "ALAPI_TOKEN") + try: + data = (await AsyncHttpx.get(url, params=params, timeout=5)).json() + if data["code"] == 200: + if not data["data"]: + return "没有搜索到...", 997 + return data, 200 + else: + return f'发生了错误...code:{data["code"]}', 999 + except TimeoutError: + return "超时了....", 998 + + +def gen_wbtop_pic(data: dict) -> MessageSegment: + """ + 生成微博热搜图片 + :param data: 微博热搜数据 + """ + bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979") + wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png") + bk.paste(wbtop_bk) + text_bk = CreateImg(700, 32 * 50, 700, 32, color="#797979") + for i, data in enumerate(data): + title = f"{i+1}. {data['hot_word']}" + hot = data["hot_word_num"] + img = CreateImg(700, 30, font_size=20) + w, h = img.getsize(title) + img.text((10, int((30 - h) / 2)), title) + img.text((580, int((30 - h) / 2)), hot) + text_bk.paste(img) + bk.paste(text_bk, (0, 280)) + return image(b64=bk.pic2bs4()) diff --git a/plugins/alapi/jitang.py b/plugins/alapi/jitang.py new file mode 100755 index 00000000..3fe58ab7 --- /dev/null +++ b/plugins/alapi/jitang.py @@ -0,0 +1,48 @@ +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from utils.http_utils import AsyncHttpx +from configs.config import Config +from .data_source import get_data + + +__zx_plugin_name__ = "鸡汤" +__plugin_usage__ = """ +usage: + 不喝点什么感觉有点不舒服 + 指令: + 鸡汤 +""".strip() +__plugin_des__ = "喏,亲手为你煮的鸡汤" +__plugin_cmd__ = ["鸡汤"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["鸡汤", "毒鸡汤"], +} + +url = "https://v2.alapi.cn/api/soul" + + +jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True) + + +@jitang.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + try: + data, code = await get_data(url) + if code != 200: + await jitang.finish(data, at_sender=True) + await jitang.send(data["data"]["content"]) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送鸡汤:" + data["data"]["content"] + ) + except Exception as e: + await jitang.send("鸡汤煮坏掉了...") + logger.error(f"鸡汤煮坏掉了 {type(e)}:{e}") diff --git a/plugins/alapi/poetry.py b/plugins/alapi/poetry.py old mode 100644 new mode 100755 index 05395c2c..8773117b --- a/plugins/alapi/poetry.py +++ b/plugins/alapi/poetry.py @@ -1,42 +1,42 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.typing import T_State -from services.log import logger -from .data_source import get_data - -__zx_plugin_name__ = "古诗" -__plugin_usage__ = """usage: - 平白无故念首诗 - 示例:念诗/来首诗/念首诗 -""" -__plugin_des__ = "为什么突然文艺起来了!" -__plugin_cmd__ = ["念诗/来首诗/念首诗"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["念诗", "来首诗", "念首诗"], -} - -poetry = on_command("念诗", aliases={"来首诗", "念首诗"}, priority=5, block=True) - - -poetry_url = "https://v2.alapi.cn/api/shici" - - -@poetry.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - data, code = await get_data(poetry_url) - if code != 200: - await poetry.finish(data, at_sender=True) - data = data["data"] - content = data["content"] - title = data["origin"] - author = data["author"] - await poetry.send(f"{content}\n\t——{author}《{title}》") - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送古诗: f'{content}\n\t--{author}《{title}》'" - ) +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from services.log import logger +from .data_source import get_data + +__zx_plugin_name__ = "古诗" +__plugin_usage__ = """usage: + 平白无故念首诗 + 示例:念诗/来首诗/念首诗 +""" +__plugin_des__ = "为什么突然文艺起来了!" +__plugin_cmd__ = ["念诗/来首诗/念首诗"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["念诗", "来首诗", "念首诗"], +} + +poetry = on_command("念诗", aliases={"来首诗", "念首诗"}, priority=5, block=True) + + +poetry_url = "https://v2.alapi.cn/api/shici" + + +@poetry.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + data, code = await get_data(poetry_url) + if code != 200: + await poetry.finish(data, at_sender=True) + data = data["data"] + content = data["content"] + title = data["origin"] + author = data["author"] + await poetry.send(f"{content}\n\t——{author}《{title}》") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送古诗: f'{content}\n\t--{author}《{title}》'" + ) diff --git a/plugins/alapi/wbtop.py b/plugins/alapi/wbtop.py old mode 100644 new mode 100755 index e367e533..9d7ced17 --- a/plugins/alapi/wbtop.py +++ b/plugins/alapi/wbtop.py @@ -1,76 +1,71 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.typing import T_State -from services.log import logger -from .data_source import get_data, gen_wbtop_pic -from utils.browser import get_browser -from utils.utils import get_message_text, is_number -from configs.path_config import IMAGE_PATH -from utils.message_builder import image -import asyncio - -__zx_plugin_name__ = '微博热搜' -__plugin_usage__ = """ -usage: - 在QQ上吃个瓜 - 指令: - 微博热搜:发送实时热搜 - 微博热搜 [id]:截图该热搜页面 - 示例:微博热搜 5 -""".strip() -__plugin_des__ = '刚买完瓜,在吃瓜现场' -__plugin_cmd__ = ['微博热搜', '微博热搜 [id]'] -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ['微博热搜'], -} - -wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True) - - -wbtop_url = 'https://v2.alapi.cn/api/new/wbtop' - -wbtop_data = [] - - -@wbtop.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - global wbtop_data - msg = get_message_text(event.json()) - if not wbtop_data or not msg: - data, code = await get_data(wbtop_url) - if code != 200: - await wbtop.finish(data, at_sender=True) - wbtop_data = data['data'] - if not msg: - img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data) - await wbtop.send(img) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查询微博热搜") - if is_number(msg) and 0 < int(msg) <= 50: - url = wbtop_data[int(msg) - 1]['url'] - browser = await get_browser() - page = None - try: - if not browser: - logger.warning('获取 browser 失败,请部署至 linux 环境....') - await wbtop.finish('获取 browser 对象失败...') - page = await browser.new_page() - await page.goto(url, wait_until='networkidle', timeout=10000) - await page.set_viewport_size({"width": 2560, "height": 1080}) - await asyncio.sleep(5) - div = await page.query_selector("#pl_feedlist_index") - await div.screenshot(path=f'{IMAGE_PATH}/temp/wbtop_{event.user_id}.png', timeout=100000) - await page.close() - await wbtop.send(image(f'wbtop_{event.user_id}.png', 'temp')) - except Exception as e: - logger.error(f'微博热搜截图出错... {type(e)}: {e}') - if page: - await page.close() - await wbtop.send('发生了一些错误.....') - +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from services.log import logger +from .data_source import get_data, gen_wbtop_pic +from utils.utils import get_message_text, is_number +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from utils.http_utils import AsyncPlaywright +import asyncio + +__zx_plugin_name__ = "微博热搜" +__plugin_usage__ = """ +usage: + 在QQ上吃个瓜 + 指令: + 微博热搜:发送实时热搜 + 微博热搜 [id]:截图该热搜页面 + 示例:微博热搜 5 +""".strip() +__plugin_des__ = "刚买完瓜,在吃瓜现场" +__plugin_cmd__ = ["微博热搜", "微博热搜 [id]"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["微博热搜"], +} + +wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True) + + +wbtop_url = "https://v2.alapi.cn/api/new/wbtop" + +wbtop_data = [] + + +@wbtop.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + global wbtop_data + msg = get_message_text(event.json()) + if not wbtop_data or not msg: + data, code = await get_data(wbtop_url) + if code != 200: + await wbtop.finish(data, at_sender=True) + wbtop_data = data["data"] + if not msg: + img = await asyncio.get_event_loop().run_in_executor( + None, gen_wbtop_pic, wbtop_data + ) + await wbtop.send(img) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查询微博热搜" + ) + if is_number(msg) and 0 < int(msg) <= 50: + url = wbtop_data[int(msg) - 1]["url"] + try: + await wbtop.send("开始截取数据...") + img = await AsyncPlaywright.screenshot( + url, + f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png", + "#pl_feedlist_index", + sleep=5 + ) + await wbtop.send(img) + except Exception as e: + logger.error(f"微博热搜截图出错... {type(e)}: {e}") + await wbtop.send("发生了一些错误.....") diff --git a/plugins/bilibili_sub/__init__.py b/plugins/bilibili_sub/__init__.py old mode 100644 new mode 100755 index 6ee0138c..32697051 --- a/plugins/bilibili_sub/__init__.py +++ b/plugins/bilibili_sub/__init__.py @@ -1,235 +1,236 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message -from .data_source import ( - add_live_sub, - delete_sub, - add_up_sub, - add_season_sub, - get_media_id, - get_sub_status, - SubManager, - BilibiliSub -) -from models.level_user import LevelUser -from configs.config import Config -from utils.utils import get_message_text, is_number, scheduler, get_bot -from typing import Optional -from services.log import logger -from nonebot import Driver -import nonebot - -__zx_plugin_name__ = "B站订阅" -__plugin_usage__ = """ -usage: - B站直播,番剧,UP动态开播等提醒 - 主播订阅相当于 直播间订阅 + UP订阅 - 指令:[示例Id乱打的,仅做示例] - 添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名] - 删除订阅 [id] - 查看订阅 - 示例:添加订阅主播 2345344 <-(直播房间id) - 示例:添加订阅UP 2355543 <-(个人主页id) - 示例:添加订阅番剧 史莱姆 <-(支持模糊搜索) - 示例:添加订阅番剧 125344 <-(番剧id) - 示例:删除订阅 2324344 <-(任意id,通过查看订阅获取) -""".strip() -__plugin_des__ = "非常便利的B站订阅通知" -__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["B站订阅", "b站订阅", "添加订阅", "删除订阅", "查看订阅"], -} -__plugin_configs__ = { - "GROUP_BILIBILI_SUB_LEVEL": { - "value": 5, - "help": "群内bilibili订阅需要管理的权限", - "default_value": 5, - } -} - -add_sub = on_command("添加订阅", priority=5, block=True) -del_sub = on_command("删除订阅", priority=5, block=True) -show_sub_info = on_command("查看订阅", priority=5, block=True) - -driver: Driver = nonebot.get_driver() - - -sub_manager: Optional[SubManager] = None - - -@driver.on_startup -async def _(): - global sub_manager - sub_manager = SubManager() - - -@add_sub.args_parser -async def _(bot: Bot, event: MessageEvent, state: T_State): - season_data = state["season_data"] - msg = get_message_text(event.json()) - if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data): - await add_sub.reject("Id必须为数字且在范围内!请重新输入...") - state["id"] = season_data[int(msg) - 1]["media_id"] - - -@add_sub.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()).split() - if len(msg) < 2: - await add_sub.finish("参数不完全,请查看订阅帮助...") - sub_type = msg[0] - id_ = "" - if isinstance(event, GroupMessageEvent): - if not await LevelUser.check_level( - event.user_id, - event.group_id, - Config.get_config("bilibili_sub", "GROUP_BILIBILI_SUB_LEVEL"), - ): - await add_sub.finish( - f"您的权限不足,群内订阅的需要 {Config.get_config('bilibili_sub', 'GROUP_BILIBILI_SUB_LEVEL')} 级权限..", - at_sender=True, - ) - sub_user = f"{event.user_id}:{event.group_id}" - else: - sub_user = f"{event.user_id}" - state["sub_type"] = sub_type - state["sub_user"] = sub_user - if len(msg) > 1: - if "http" in msg[1]: - msg[1] = msg[1].split("?")[0] - msg[1] = msg[1][:-1] if msg[1][-1] == "/" else msg[1] - msg[1] = msg[1].split("/")[-1] - id_ = msg[1][2:] if msg[1].startswith("md") else msg[1] - if not is_number(id_): - if sub_type in ["season", "动漫", "番剧"]: - rst = "*以为您找到以下番剧,请输入Id选择:*\n" - state["season_data"] = await get_media_id(id_) - if len(state["season_data"]) == 0: - await add_sub.finish(f"未找到番剧:{msg}") - for i, x in enumerate(state["season_data"]): - rst += f'{i + 1}.{state["season_data"][x]["title"]}\n----------\n' - await add_sub.send("\n".join(rst.split("\n")[:-1])) - else: - await add_sub.finish("Id 必须为全数字!") - else: - state["id"] = int(id_) - - -@add_sub.got("id") -async def _(bot: Bot, event: MessageEvent, state: T_State): - sub_type = state["sub_type"] - sub_user = state["sub_user"] - id_ = state["id"] - if sub_type in ["主播", "直播"]: - await add_sub.send(await add_live_sub(id_, sub_user)) - elif sub_type.lower() in ["up", "用户"]: - await add_sub.send(await add_up_sub(id_, sub_user)) - elif sub_type in ["season", "动漫", "番剧"]: - await add_sub.send(await add_season_sub(id_, sub_user)) - else: - await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!") - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 添加订阅:{sub_type} -> {sub_user} -> {id_}" - ) - - -@del_sub.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if not is_number(msg): - await del_sub.finish("Id必须为数字!", at_sender=True) - id_ = ( - f"{event.user_id}:{event.group_id}" - if isinstance(event, GroupMessageEvent) - else f"{event.user_id}" - ) - if await BilibiliSub.delete_bilibili_sub(int(msg), id_): - await del_sub.send(f"删除订阅id:{msg} 成功...") - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 删除订阅 {id_}" - ) - else: - await del_sub.send(f"删除订阅id:{msg} 失败...") - - -@show_sub_info.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - id_ = ( - f"{event.user_id}:{event.group_id}" - if isinstance(event, GroupMessageEvent) - else f"{event.user_id}" - ) - data = await BilibiliSub.get_sub_data(id_) - live_rst = "" - up_rst = "" - season_rst = "" - for x in data: - if x.sub_type == "live": - live_rst += ( - f"\t直播间id:{x.sub_id}\n" f"\t名称:{x.uname}\n" f"------------------\n" - ) - if x.sub_type == "up": - up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n" - if x.sub_type == "season": - season_rst += ( - f"\t番名:{x.season_name}\n" - f"\t当前集数:{x.season_current_episode}\n" - f"------------------\n" - ) - live_rst = "当前订阅的直播:\n" + live_rst if live_rst else live_rst - up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst - season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst - if not live_rst and not up_rst and not season_rst: - live_rst = "您目前没有任何订阅..." - await show_sub_info.send(live_rst + up_rst + season_rst) - - -# 推送 -@scheduler.scheduled_job( - "interval", - seconds=30, -) -async def _(): - bot = get_bot() - sub = None - if bot: - try: - await sub_manager.reload_sub_data() - sub = await sub_manager.random_sub_data() - if sub: - rst = await get_sub_status(sub.sub_id, sub.sub_type) - await send_sub_msg(rst, sub, bot) - if sub.sub_type == "live": - rst = await get_sub_status(sub.sub_id, "up") - await send_sub_msg(rst, sub, bot) - except Exception as e: - logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}") - - -async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot): - """ - 推送信息 - :param rst: 回复 - :param sub: BilibiliSub - :param bot: Bot - """ - if rst: - for x in sub.sub_users.split(",")[:-1]: - try: - if ":" in x: - await bot.send_group_msg( - group_id=int(x.split(":")[1]), message=Message(rst) - ) - else: - await bot.send_private_msg(user_id=int(x), message=Message(rst)) - except Exception as e: - logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id} {type(e)}:{e}") +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message +from .data_source import ( + add_live_sub, + delete_sub, + add_up_sub, + add_season_sub, + get_media_id, + get_sub_status, + SubManager, + BilibiliSub +) +from models.level_user import LevelUser +from configs.config import Config +from utils.utils import get_message_text, is_number, scheduler, get_bot +from typing import Optional +from services.log import logger +from nonebot import Driver +import nonebot + +__zx_plugin_name__ = "B站订阅" +__plugin_usage__ = """ +usage: + B站直播,番剧,UP动态开播等提醒 + 主播订阅相当于 直播间订阅 + UP订阅 + 指令:[示例Id乱打的,仅做示例] + 添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名] + 删除订阅 [id] + 查看订阅 + 示例:添加订阅主播 2345344 <-(直播房间id) + 示例:添加订阅UP 2355543 <-(个人主页id) + 示例:添加订阅番剧 史莱姆 <-(支持模糊搜索) + 示例:添加订阅番剧 125344 <-(番剧id) + 示例:删除订阅 2324344 <-(任意id,通过查看订阅获取) +""".strip() +__plugin_des__ = "非常便利的B站订阅通知" +__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["B站订阅", "b站订阅", "添加订阅", "删除订阅", "查看订阅"], +} +__plugin_configs__ = { + "GROUP_BILIBILI_SUB_LEVEL": { + "value": 5, + "help": "群内bilibili订阅需要管理的权限", + "default_value": 5, + } +} + +add_sub = on_command("添加订阅", priority=5, block=True) +del_sub = on_command("删除订阅", priority=5, block=True) +show_sub_info = on_command("查看订阅", priority=5, block=True) + +driver: Driver = nonebot.get_driver() + + +sub_manager: Optional[SubManager] = None + + +@driver.on_startup +async def _(): + global sub_manager + sub_manager = SubManager() + + +@add_sub.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + season_data = state["season_data"] + msg = get_message_text(event.json()) + if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data): + await add_sub.reject("Id必须为数字且在范围内!请重新输入...") + state["id"] = season_data[int(msg) - 1]["media_id"] + + +@add_sub.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).split() + if len(msg) < 2: + await add_sub.finish("参数不完全,请查看订阅帮助...") + sub_type = msg[0] + id_ = "" + if isinstance(event, GroupMessageEvent): + if not await LevelUser.check_level( + event.user_id, + event.group_id, + Config.get_config("bilibili_sub", "GROUP_BILIBILI_SUB_LEVEL"), + ): + await add_sub.finish( + f"您的权限不足,群内订阅的需要 {Config.get_config('bilibili_sub', 'GROUP_BILIBILI_SUB_LEVEL')} 级权限..", + at_sender=True, + ) + sub_user = f"{event.user_id}:{event.group_id}" + else: + sub_user = f"{event.user_id}" + state["sub_type"] = sub_type + state["sub_user"] = sub_user + if len(msg) > 1: + if "http" in msg[1]: + msg[1] = msg[1].split("?")[0] + msg[1] = msg[1][:-1] if msg[1][-1] == "/" else msg[1] + msg[1] = msg[1].split("/")[-1] + id_ = msg[1][2:] if msg[1].startswith("md") else msg[1] + if not is_number(id_): + if sub_type in ["season", "动漫", "番剧"]: + rst = "*以为您找到以下番剧,请输入Id选择:*\n" + state["season_data"] = await get_media_id(id_) + if len(state["season_data"]) == 0: + await add_sub.finish(f"未找到番剧:{msg}") + for i, x in enumerate(state["season_data"]): + rst += f'{i + 1}.{state["season_data"][x]["title"]}\n----------\n' + await add_sub.send("\n".join(rst.split("\n")[:-1])) + else: + await add_sub.finish("Id 必须为全数字!") + else: + state["id"] = int(id_) + + +@add_sub.got("id") +async def _(bot: Bot, event: MessageEvent, state: T_State): + sub_type = state["sub_type"] + sub_user = state["sub_user"] + id_ = state["id"] + if sub_type in ["主播", "直播"]: + await add_sub.send(await add_live_sub(id_, sub_user)) + elif sub_type.lower() in ["up", "用户"]: + await add_sub.send(await add_up_sub(id_, sub_user)) + elif sub_type in ["season", "动漫", "番剧"]: + await add_sub.send(await add_season_sub(id_, sub_user)) + else: + await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!") + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 添加订阅:{sub_type} -> {sub_user} -> {id_}" + ) + + +@del_sub.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not is_number(msg): + await del_sub.finish("Id必须为数字!", at_sender=True) + id_ = ( + f"{event.user_id}:{event.group_id}" + if isinstance(event, GroupMessageEvent) + else f"{event.user_id}" + ) + if await BilibiliSub.delete_bilibili_sub(int(msg), id_): + await del_sub.send(f"删除订阅id:{msg} 成功...") + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 删除订阅 {id_}" + ) + else: + await del_sub.send(f"删除订阅id:{msg} 失败...") + + +@show_sub_info.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + id_ = ( + f"{event.user_id}:{event.group_id}" + if isinstance(event, GroupMessageEvent) + else f"{event.user_id}" + ) + data = await BilibiliSub.get_sub_data(id_) + live_rst = "" + up_rst = "" + season_rst = "" + for x in data: + if x.sub_type == "live": + live_rst += ( + f"\t直播间id:{x.sub_id}\n" f"\t名称:{x.uname}\n" f"------------------\n" + ) + if x.sub_type == "up": + up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n" + if x.sub_type == "season": + season_rst += ( + f"\t番名:{x.season_name}\n" + f"\t当前集数:{x.season_current_episode}\n" + f"------------------\n" + ) + live_rst = "当前订阅的直播:\n" + live_rst if live_rst else live_rst + up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst + season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst + if not live_rst and not up_rst and not season_rst: + live_rst = "您目前没有任何订阅..." + await show_sub_info.send(live_rst + up_rst + season_rst) + + +# 推送 +@scheduler.scheduled_job( + "interval", + seconds=30, +) +async def _(): + bot = get_bot() + sub = None + if bot: + try: + await sub_manager.reload_sub_data() + sub = await sub_manager.random_sub_data() + if sub: + logger.info(f"Bilibili订阅开始检测:{sub.sub_id}") + rst = await get_sub_status(sub.sub_id, sub.sub_type) + await send_sub_msg(rst, sub, bot) + if sub.sub_type == "live": + rst = await get_sub_status(sub.sub_id, "up") + await send_sub_msg(rst, sub, bot) + except Exception as e: + logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}") + + +async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot): + """ + 推送信息 + :param rst: 回复 + :param sub: BilibiliSub + :param bot: Bot + """ + if rst: + for x in sub.sub_users.split(",")[:-1]: + try: + if ":" in x: + await bot.send_group_msg( + group_id=int(x.split(":")[1]), message=Message(rst) + ) + else: + await bot.send_private_msg(user_id=int(x), message=Message(rst)) + except Exception as e: + logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id} {type(e)}:{e}") diff --git a/plugins/bilibili_sub/data_source.py b/plugins/bilibili_sub/data_source.py old mode 100644 new mode 100755 index 3261df3e..df1dd841 --- a/plugins/bilibili_sub/data_source.py +++ b/plugins/bilibili_sub/data_source.py @@ -1,394 +1,391 @@ -from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException -from utils.manager import resources_manager -from asyncio.exceptions import TimeoutError -from .model import BilibiliSub -from bilibili_api.live import LiveRoom -from bilibili_api import bangumi -from utils.message_builder import image -from bilibili_api.user import User -from bilibili_api import user -from typing import Optional -from pathlib import Path -from configs.path_config import IMAGE_PATH -from datetime import datetime -from utils.browser import get_browser -from services.db_context import db -from services.log import logger -import aiohttp -import random - - -bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2" - -dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic" -dynamic_path.mkdir(exist_ok=True, parents=True) - - -resources_manager.add_temp_dir(dynamic_path) - - -async def add_live_sub(live_id: int, sub_user: str) -> str: - """ - 添加直播订阅 - :param live_id: 直播房间号 - :param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group) - :return: - """ - try: - async with db.transaction(): - try: - live = LiveRoom(live_id) - live_info = (await live.get_room_info())["room_info"] - except ResponseCodeException: - return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确" - uid = live_info["uid"] - room_id = live_info["room_id"] - short_id = live_info["short_id"] - title = live_info["title"] - live_status = live_info["live_status"] - if await BilibiliSub.add_bilibili_sub( - room_id, - "live", - sub_user, - uid=uid, - live_short_id=short_id, - live_status=live_status, - ): - await _get_up_status(live_id) - uname = (await BilibiliSub.get_sub(live_id)).uname - return ( - "已成功订阅主播:\n" - f"\ttitle:{title}\n" - f"\tname: {uname}\n" - f"\tlive_id:{live_id}\n" - f"\tuid:{uid}" - ) - else: - return "添加订阅失败..." - except Exception as e: - logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}") - return "添加订阅失败..." - - -async def add_up_sub(uid: int, sub_user: str) -> str: - """ - 添加订阅 UP - :param uid: UP uid - :param sub_user: 订阅用户 - """ - try: - async with db.transaction(): - try: - u = user.User(uid) - user_info = await u.get_user_info() - except ResponseCodeException: - return f"未找到UpId:{uid} 的信息,请检查Id是否正确" - uname = user_info["name"] - dynamic_info = await u.get_dynamics(0) - dynamic_upload_time = 0 - if dynamic_info.get("cards"): - dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] - video_info = await u.get_videos() - latest_video_created = 0 - if video_info["list"].get("vlist"): - latest_video_created = video_info["list"]["vlist"][0]["created"] - if await BilibiliSub.add_bilibili_sub( - uid, - "up", - sub_user, - uid=uid, - uname=uname, - dynamic_upload_time=dynamic_upload_time, - latest_video_created=latest_video_created, - ): - return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}" - else: - return "添加订阅失败..." - except Exception as e: - logger.error(f"订阅Up uid:{uid} 发生了错误 {type(e)}:{e}") - return "添加订阅失败..." - - -async def add_season_sub(media_id: int, sub_user: str) -> str: - """ - 添加订阅 UP - :param media_id: 番剧 media_id - :param sub_user: 订阅用户 - """ - try: - async with db.transaction(): - try: - season_info = await bangumi.get_meta(media_id) - except ResponseCodeException: - return f"未找到media_id:{media_id} 的信息,请检查Id是否正确" - season_id = season_info["media"]["season_id"] - season_current_episode = season_info["media"]["new_ep"]["index"] - season_name = season_info["media"]["title"] - if await BilibiliSub.add_bilibili_sub( - media_id, - "season", - sub_user, - season_name=season_name, - season_id=season_id, - season_current_episode=season_current_episode, - ): - return ( - "已成功订阅番剧:\n" - f"\ttitle: {season_name}\n" - f"\tcurrent_episode: {season_current_episode}" - ) - else: - return "添加订阅失败..." - except Exception as e: - logger.error(f"订阅番剧 media_id:{media_id} 发生了错误 {type(e)}:{e}") - return "添加订阅失败..." - - -async def delete_sub(sub_id: str, sub_user: str) -> str: - """ - 删除订阅 - :param sub_id: 订阅 id - :param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group) - """ - if await BilibiliSub.delete_bilibili_sub(sub_id, sub_user): - return f"已成功取消订阅:{sub_id}" - else: - return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...." - - -async def get_media_id(keyword: str) -> dict: - """ - 获取番剧的 media_id - :param keyword: 番剧名称 - """ - params = {"keyword": keyword} - async with aiohttp.ClientSession() as session: - for _ in range(3): - try: - _season_data = {} - async with session.get( - bilibili_search_url, timeout=5, params=params - ) as response: - if response.status == 200: - data = await response.json() - if data.get("data"): - for item in data["data"]["result"]: - if item["result_type"] == "media_bangumi": - idx = 0 - for x in item["data"]: - _season_data[idx] = { - "media_id": x["media_id"], - "title": x["title"] - .replace('', "") - .replace("", ""), - } - idx += 1 - return _season_data - except TimeoutError: - pass - return {} - - -async def get_sub_status(id_: int, sub_type: str) -> Optional[str]: - """ - 获取订阅状态 - :param id_: 订阅 id - :param sub_type: 订阅类型 - :return: - """ - try: - if sub_type == "live": - return await _get_live_status(id_) - elif sub_type == "up": - return await _get_up_status(id_) - elif sub_type == "season": - return await _get_season_status(id_) - except ResponseCodeException: - return "获取信息失败...请检查订阅Id是否存在或稍后再试..." - # except Exception as e: - # logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}") - # return "发生了预料之外的错误..请稍后再试或联系管理员....." - - -async def _get_live_status(id_: int) -> Optional[str]: - """ - 获取直播订阅状态 - :param id_: 直播间 id - """ - live = LiveRoom(id_) - live_info = (await live.get_room_info())["room_info"] - title = live_info["title"] - room_id = live_info["room_id"] - live_status = live_info["live_status"] - cover = live_info["cover"] - sub = await BilibiliSub.get_sub(id_) - if sub.live_status != live_status: - await BilibiliSub.update_sub_info(id_, live_status=live_status) - if sub.live_status == 0 and live_status == 1: - return ( - f"{image(cover)}\n" - f"{sub.uname} 开播啦!\n" - f"标题:{title}\n" - f"直链:https://live.bilibili.com/{room_id}" - ) - return None - - -async def _get_up_status(id_: int) -> Optional[str]: - """ - 获取用户投稿状态 - :param id_: 用户 id - :return: - """ - _user = await BilibiliSub.get_sub(id_) - u = user.User(_user.uid) - user_info = await u.get_user_info() - uname = user_info["name"] - video_info = await u.get_videos() - latest_video_created = 0 - video = None - if _user.uname != uname: - await BilibiliSub.update_sub_info(id_, uname=uname) - dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user) - if video_info["list"].get("vlist"): - video = video_info["list"]["vlist"][0] - latest_video_created = video["created"] - rst = "" - if dynamic_img: - await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time) - rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n" - if _user.latest_video_created != latest_video_created and video: - rst = rst + "-------------\n" if rst else rst - await BilibiliSub.update_sub_info( - id_, latest_video_created=latest_video_created - ) - rst += ( - f'{image(video["pic"])}\n' - f"{uname} 投稿了新视频啦\n" - f'标题:{video["title"]}\n' - f'Bvid:{video["bvid"]}\n' - f'直链:https://www.bilibili.com/video/{video["bvid"]}' - ) - rst = None if rst == "-------------\n" else rst - return rst - - -async def _get_season_status(id_) -> Optional[str]: - """ - 获取 番剧 更新状态 - :param id_: 番剧 id - """ - season_info = await bangumi.get_meta(id_) - title = season_info["media"]["title"] - _idx = (await BilibiliSub.get_sub(id_)).season_current_episode - new_ep = season_info["media"]["new_ep"]["index"] - if new_ep != _idx: - await BilibiliSub.update_sub_info( - id_, season_current_episode=new_ep, season_update_time=datetime.now() - ) - return ( - f'{image(season_info["media"]["cover"])}\n' - f"[{title}]更新啦\n" - f"最新集数:{new_ep}" - ) - return None - - -async def get_user_dynamic( - u: User, local_user: BilibiliSub -) -> "Optional[MessageSegment], int": - """ - 获取用户动态 - :param u: 用户类 - :param local_user: 数据库存储的用户数据 - :return: 最新动态截图与时间 - """ - dynamic_info = await u.get_dynamics(0) - browser = await get_browser() - if dynamic_info.get("cards") and browser: - dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] - if local_user.dynamic_upload_time != dynamic_upload_time: - page = await browser.new_page() - await page.goto( - f"https://space.bilibili.com/{local_user.uid}/dynamic", - wait_until="networkidle", - timeout=10000, - ) - await page.set_viewport_size({"width": 2560, "height": 1080}) - # 删除置顶 - await page.evaluate( - """ - xs = document.getElementsByClassName('first-card-with-title'); - for (x of xs) { - x.remove(); - } - """ - ) - card = await page.query_selector(".card") - # 截图并保存 - await card.screenshot( - path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg", - timeout=100000, - ) - await page.close() - return ( - image( - f"{local_user.sub_id}_{dynamic_upload_time}.jpg", - "bilibili_sub/dynamic", - ), - dynamic_upload_time, - ) - return None, None - - -class SubManager: - def __init__(self): - self.live_data = [] - self.up_data = [] - self.season_data = [] - self.current_index = -1 - - async def reload_sub_data(self): - """ - 重载数据 - """ - if not self.live_data or not self.up_data or not self.season_data: - ( - _live_data, - _up_data, - _season_data, - ) = await BilibiliSub.get_all_sub_data() - if not self.live_data: - self.live_data = _live_data - if not self.up_data: - self.up_data = _up_data - if not self.season_data: - self.season_data = _season_data - - async def random_sub_data(self) -> Optional[BilibiliSub]: - """ - 随机获取一条数据 - :return: - """ - sub = None - if not self.live_data and not self.up_data and not self.season_data: - return sub - self.current_index += 1 - if self.current_index == 0: - if self.live_data: - sub = random.choice(self.live_data) - self.live_data.remove(sub) - elif self.current_index == 1: - if self.up_data: - sub = random.choice(self.up_data) - self.up_data.remove(sub) - elif self.current_index == 2: - if self.season_data: - sub = random.choice(self.season_data) - self.season_data.remove(sub) - else: - self.current_index = -1 - if sub: - return sub - await self.reload_sub_data() - return await self.random_sub_data() +from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException +from utils.manager import resources_manager +from asyncio.exceptions import TimeoutError +from .model import BilibiliSub +from bilibili_api.live import LiveRoom +from bilibili_api import bangumi +from utils.message_builder import image +from bilibili_api.user import User +from bilibili_api import user +from typing import Optional +from pathlib import Path +from configs.path_config import IMAGE_PATH +from datetime import datetime +from utils.browser import get_browser +from services.db_context import db +from services.log import logger +from utils.http_utils import AsyncHttpx +import random + + +bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2" + +dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic" +dynamic_path.mkdir(exist_ok=True, parents=True) + + +resources_manager.add_temp_dir(dynamic_path) + + +async def add_live_sub(live_id: int, sub_user: str) -> str: + """ + 添加直播订阅 + :param live_id: 直播房间号 + :param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group) + :return: + """ + try: + async with db.transaction(): + try: + live = LiveRoom(live_id) + live_info = (await live.get_room_info())["room_info"] + except ResponseCodeException: + return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确" + uid = live_info["uid"] + room_id = live_info["room_id"] + short_id = live_info["short_id"] + title = live_info["title"] + live_status = live_info["live_status"] + if await BilibiliSub.add_bilibili_sub( + room_id, + "live", + sub_user, + uid=uid, + live_short_id=short_id, + live_status=live_status, + ): + await _get_up_status(live_id) + uname = (await BilibiliSub.get_sub(live_id)).uname + return ( + "已成功订阅主播:\n" + f"\ttitle:{title}\n" + f"\tname: {uname}\n" + f"\tlive_id:{live_id}\n" + f"\tuid:{uid}" + ) + else: + return "添加订阅失败..." + except Exception as e: + logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}") + return "添加订阅失败..." + + +async def add_up_sub(uid: int, sub_user: str) -> str: + """ + 添加订阅 UP + :param uid: UP uid + :param sub_user: 订阅用户 + """ + try: + async with db.transaction(): + try: + u = user.User(uid) + user_info = await u.get_user_info() + except ResponseCodeException: + return f"未找到UpId:{uid} 的信息,请检查Id是否正确" + uname = user_info["name"] + dynamic_info = await u.get_dynamics(0) + dynamic_upload_time = 0 + if dynamic_info.get("cards"): + dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] + video_info = await u.get_videos() + latest_video_created = 0 + if video_info["list"].get("vlist"): + latest_video_created = video_info["list"]["vlist"][0]["created"] + if await BilibiliSub.add_bilibili_sub( + uid, + "up", + sub_user, + uid=uid, + uname=uname, + dynamic_upload_time=dynamic_upload_time, + latest_video_created=latest_video_created, + ): + return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}" + else: + return "添加订阅失败..." + except Exception as e: + logger.error(f"订阅Up uid:{uid} 发生了错误 {type(e)}:{e}") + return "添加订阅失败..." + + +async def add_season_sub(media_id: int, sub_user: str) -> str: + """ + 添加订阅 UP + :param media_id: 番剧 media_id + :param sub_user: 订阅用户 + """ + try: + async with db.transaction(): + try: + season_info = await bangumi.get_meta(media_id) + except ResponseCodeException: + return f"未找到media_id:{media_id} 的信息,请检查Id是否正确" + season_id = season_info["media"]["season_id"] + season_current_episode = season_info["media"]["new_ep"]["index"] + season_name = season_info["media"]["title"] + if await BilibiliSub.add_bilibili_sub( + media_id, + "season", + sub_user, + season_name=season_name, + season_id=season_id, + season_current_episode=season_current_episode, + ): + return ( + "已成功订阅番剧:\n" + f"\ttitle: {season_name}\n" + f"\tcurrent_episode: {season_current_episode}" + ) + else: + return "添加订阅失败..." + except Exception as e: + logger.error(f"订阅番剧 media_id:{media_id} 发生了错误 {type(e)}:{e}") + return "添加订阅失败..." + + +async def delete_sub(sub_id: str, sub_user: str) -> str: + """ + 删除订阅 + :param sub_id: 订阅 id + :param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group) + """ + if await BilibiliSub.delete_bilibili_sub(int(sub_id), sub_user): + return f"已成功取消订阅:{sub_id}" + else: + return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...." + + +async def get_media_id(keyword: str) -> dict: + """ + 获取番剧的 media_id + :param keyword: 番剧名称 + """ + params = {"keyword": keyword} + for _ in range(3): + try: + _season_data = {} + response = await AsyncHttpx.get(bilibili_search_url, params=params, timeout=5) + if response.status_code == 200: + data = response.json() + if data.get("data"): + for item in data["data"]["result"]: + if item["result_type"] == "media_bangumi": + idx = 0 + for x in item["data"]: + _season_data[idx] = { + "media_id": x["media_id"], + "title": x["title"] + .replace('', "") + .replace("", ""), + } + idx += 1 + return _season_data + except TimeoutError: + pass + return {} + + +async def get_sub_status(id_: int, sub_type: str) -> Optional[str]: + """ + 获取订阅状态 + :param id_: 订阅 id + :param sub_type: 订阅类型 + :return: + """ + try: + if sub_type == "live": + return await _get_live_status(id_) + elif sub_type == "up": + return await _get_up_status(id_) + elif sub_type == "season": + return await _get_season_status(id_) + except ResponseCodeException: + return "获取信息失败...请检查订阅Id是否存在或稍后再试..." + # except Exception as e: + # logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}") + # return "发生了预料之外的错误..请稍后再试或联系管理员....." + + +async def _get_live_status(id_: int) -> Optional[str]: + """ + 获取直播订阅状态 + :param id_: 直播间 id + """ + live = LiveRoom(id_) + live_info = (await live.get_room_info())["room_info"] + title = live_info["title"] + room_id = live_info["room_id"] + live_status = live_info["live_status"] + cover = live_info["cover"] + sub = await BilibiliSub.get_sub(id_) + if sub.live_status != live_status: + await BilibiliSub.update_sub_info(id_, live_status=live_status) + if sub.live_status == 0 and live_status == 1: + return ( + f"{image(cover)}\n" + f"{sub.uname} 开播啦!\n" + f"标题:{title}\n" + f"直链:https://live.bilibili.com/{room_id}" + ) + return None + + +async def _get_up_status(id_: int) -> Optional[str]: + """ + 获取用户投稿状态 + :param id_: 用户 id + :return: + """ + _user = await BilibiliSub.get_sub(id_) + u = user.User(_user.uid) + user_info = await u.get_user_info() + uname = user_info["name"] + video_info = await u.get_videos() + latest_video_created = 0 + video = None + if _user.uname != uname: + await BilibiliSub.update_sub_info(id_, uname=uname) + dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user) + if video_info["list"].get("vlist"): + video = video_info["list"]["vlist"][0] + latest_video_created = video["created"] + rst = "" + if dynamic_img: + await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time) + rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n" + if _user.latest_video_created != latest_video_created and video: + rst = rst + "-------------\n" if rst else rst + await BilibiliSub.update_sub_info( + id_, latest_video_created=latest_video_created + ) + rst += ( + f'{image(video["pic"])}\n' + f"{uname} 投稿了新视频啦\n" + f'标题:{video["title"]}\n' + f'Bvid:{video["bvid"]}\n' + f'直链:https://www.bilibili.com/video/{video["bvid"]}' + ) + rst = None if rst == "-------------\n" else rst + return rst + + +async def _get_season_status(id_) -> Optional[str]: + """ + 获取 番剧 更新状态 + :param id_: 番剧 id + """ + season_info = await bangumi.get_meta(id_) + title = season_info["media"]["title"] + _idx = (await BilibiliSub.get_sub(id_)).season_current_episode + new_ep = season_info["media"]["new_ep"]["index"] + if new_ep != _idx: + await BilibiliSub.update_sub_info( + id_, season_current_episode=new_ep, season_update_time=datetime.now() + ) + return ( + f'{image(season_info["media"]["cover"])}\n' + f"[{title}]更新啦\n" + f"最新集数:{new_ep}" + ) + return None + + +async def get_user_dynamic( + u: User, local_user: BilibiliSub +) -> "Optional[MessageSegment], int": + """ + 获取用户动态 + :param u: 用户类 + :param local_user: 数据库存储的用户数据 + :return: 最新动态截图与时间 + """ + dynamic_info = await u.get_dynamics(0) + browser = await get_browser() + if dynamic_info.get("cards") and browser: + dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] + if local_user.dynamic_upload_time != dynamic_upload_time: + page = await browser.new_page() + await page.goto( + f"https://space.bilibili.com/{local_user.uid}/dynamic", + wait_until="networkidle", + timeout=10000, + ) + await page.set_viewport_size({"width": 2560, "height": 1080}) + # 删除置顶 + await page.evaluate( + """ + xs = document.getElementsByClassName('first-card-with-title'); + for (x of xs) { + x.remove(); + } + """ + ) + card = await page.query_selector(".card") + # 截图并保存 + await card.screenshot( + path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg", + timeout=100000, + ) + await page.close() + return ( + image( + f"{local_user.sub_id}_{dynamic_upload_time}.jpg", + "bilibili_sub/dynamic", + ), + dynamic_upload_time, + ) + return None, None + + +class SubManager: + def __init__(self): + self.live_data = [] + self.up_data = [] + self.season_data = [] + self.current_index = -1 + + async def reload_sub_data(self): + """ + 重载数据 + """ + if not self.live_data or not self.up_data or not self.season_data: + ( + _live_data, + _up_data, + _season_data, + ) = await BilibiliSub.get_all_sub_data() + if not self.live_data: + self.live_data = _live_data + if not self.up_data: + self.up_data = _up_data + if not self.season_data: + self.season_data = _season_data + + async def random_sub_data(self) -> Optional[BilibiliSub]: + """ + 随机获取一条数据 + :return: + """ + sub = None + if not self.live_data and not self.up_data and not self.season_data: + return sub + self.current_index += 1 + if self.current_index == 0: + if self.live_data: + sub = random.choice(self.live_data) + self.live_data.remove(sub) + elif self.current_index == 1: + if self.up_data: + sub = random.choice(self.up_data) + self.up_data.remove(sub) + elif self.current_index == 2: + if self.season_data: + sub = random.choice(self.season_data) + self.season_data.remove(sub) + else: + self.current_index = -1 + if sub: + return sub + await self.reload_sub_data() + return await self.random_sub_data() diff --git a/plugins/bilibili_sub/model.py b/plugins/bilibili_sub/model.py old mode 100644 new mode 100755 index f201c575..777cebf4 --- a/plugins/bilibili_sub/model.py +++ b/plugins/bilibili_sub/model.py @@ -1,249 +1,249 @@ -from services.log import logger -from services.db_context import db -from datetime import datetime -from typing import Optional, List - - -class BilibiliSub(db.Model): - __tablename__ = "bilibili_sub" - - id = db.Column(db.Integer(), primary_key=True) - sub_id = db.Column(db.Integer(), nullable=False) - sub_type = db.Column(db.String(), nullable=False) - # 订阅用户 - sub_users = db.Column(db.String(), nullable=False) - # 直播 - live_short_id = db.Column(db.Integer()) - live_status = db.Column(db.Integer) - # 主播/UP - uid = db.Column(db.BigInteger()) - uname = db.Column(db.String()) - latest_video_created = db.Column(db.BigInteger()) # 视频上传时间 - dynamic_upload_time = db.Column(db.BigInteger(), default=0) # 动态发布时间 - # 番剧 - season_name = db.Column(db.String()) - season_id = db.Column(db.Integer()) - season_current_episode = db.Column(db.String()) - season_update_time = db.Column(db.DateTime()) - - _idx1 = db.Index("bilibili_sub_idx1", "sub_id", "sub_type", unique=True) - - @classmethod - async def add_bilibili_sub( - cls, - sub_id: int, - sub_type: str, - sub_user: str, - *, - live_short_id: Optional[int] = None, - live_status: Optional[int] = None, - dynamic_upload_time: Optional[int] = None, - uid: Optional[int] = None, - uname: Optional[str] = None, - latest_video_created: Optional[int] = None, - season_name: Optional[str] = None, - season_id: Optional[int] = None, - season_current_episode: Optional[str] = None, - season_update_time: Optional[datetime] = None, - ) -> bool: - """ - 说明: - 添加订阅 - 参数: - :param sub_id: 订阅名称,房间号,番剧号等 - :param sub_type: 订阅类型 - :param sub_user: 订阅此条目的用户 - :param live_short_id: 直接短 id - :param live_status: 主播开播状态 - :param dynamic_upload_time: 主播/UP最新动态时间 - :param uid: 主播/UP uid - :param uname: 用户名称 - :param latest_video_created: 最新视频上传时间 - :param season_name: 番剧名称 - :param season_id: 番剧 season_id - :param season_current_episode: 番剧最新集数 - :param season_update_time: 番剧更新时间 - """ - try: - async with db.transaction(): - query = ( - await cls.query.where(cls.sub_id == sub_id) - .with_for_update() - .gino.first() - ) - sub_user = sub_user if sub_user[-1] == "," else f"{sub_user}," - if query: - if sub_user not in query.sub_users: - sub_users = query.sub_users + sub_user - await query.update(sub_users=sub_users).apply() - else: - sub = await cls.create( - sub_id=sub_id, sub_type=sub_type, sub_users=sub_user - ) - await sub.update( - live_short_id=live_short_id - if live_short_id - else sub.live_short_id, - live_status=live_status if live_status else sub.live_status, - dynamic_upload_time=dynamic_upload_time - if dynamic_upload_time - else sub.dynamic_upload_time, - uid=uid if uid else sub.uid, - uname=uname if uname else sub.uname, - latest_video_created=latest_video_created - if latest_video_created - else sub.latest_video_created, - season_update_time=season_update_time - if season_update_time - else sub.season_update_time, - season_current_episode=season_current_episode - if season_current_episode - else sub.season_current_episode, - season_id=season_id if season_id else sub.season_id, - season_name=season_name if season_name else sub.season_name, - ).apply() - return True - except Exception as e: - logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}") - return False - - @classmethod - async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool: - """ - 说明: - 删除订阅 - 参数: - :param sub_id: 订阅名称 - :param sub_user: 删除此条目的用户 - """ - try: - async with db.transaction(): - query = ( - await cls.query.where( - (cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user)) - ) - .with_for_update() - .gino.first() - ) - if not query: - return False - await query.update( - sub_users=query.sub_users.replace(f"{sub_user},", "") - ).apply() - if not query.sub_users.strip(): - await query.delete() - return True - except Exception as e: - logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}") - return False - - @classmethod - async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]: - """ - 说明: - 获取订阅对象 - 参数: - :param sub_id: 订阅 id - """ - return await cls.query.where(cls.sub_id == sub_id).gino.first() - - @classmethod - async def get_sub_data(cls, id_: str) -> List["BilibiliSub"]: - """ - 获取 id_ 订阅的所有内容 - :param id_: id - """ - query = cls.query.where(cls.sub_users.contains(id_)) - return await query.gino.all() - - @classmethod - async def update_sub_info( - cls, - sub_id: int, - *, - live_short_id: Optional[int] = None, - live_status: Optional[int] = None, - dynamic_upload_time: Optional[int] = None, - uid: Optional[int] = None, - uname: Optional[str] = None, - latest_video_created: Optional[int] = None, - season_name: Optional[str] = None, - season_id: Optional[int] = None, - season_current_episode: Optional[str] = None, - season_update_time: Optional[datetime] = None, - ) -> bool: - """ - 说明: - 更新订阅信息 - 参数: - :param sub_id: 订阅名称,房间号,番剧号等 - :param live_short_id: 直接短 id - :param live_status: 主播开播状态 - :param dynamic_upload_time: 主播/UP最新动态时间 - :param uid: 主播/UP uid - :param uname: 用户名称 - :param latest_video_created: 最新视频上传时间 - :param season_name: 番剧名称 - :param season_id: 番剧 season_id - :param season_current_episode: 番剧最新集数 - :param season_update_time: 番剧更新时间 - """ - try: - async with db.transaction(): - sub = ( - await cls.query.where(cls.sub_id == sub_id) - .with_for_update() - .gino.first() - ) - if sub: - await sub.update( - live_short_id=live_short_id - if live_short_id is not None - else sub.live_short_id, - live_status=live_status - if live_status is not None - else sub.live_status, - dynamic_upload_time=dynamic_upload_time - if dynamic_upload_time is not None - else sub.dynamic_upload_time, - uid=uid if uid is not None else sub.uid, - uname=uname if uname is not None else sub.uname, - latest_video_created=latest_video_created - if latest_video_created is not None - else sub.latest_video_created, - season_update_time=season_update_time - if season_update_time is not None - else sub.season_update_time, - season_current_episode=season_current_episode - if season_current_episode is not None - else sub.season_current_episode, - season_id=season_id if season_id is not None else sub.season_id, - season_name=season_name - if season_name is not None - else sub.season_name, - ).apply() - return True - except Exception as e: - logger.info(f"bilibili_sub 更新订阅错误 {type(e)}: {e}") - return False - - @classmethod - async def get_all_sub_data( - cls, - ) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]": - """ - 说明: - 分类获取所有数据 - """ - live_data = [] - up_data = [] - season_data = [] - query = await cls.query.gino.all() - for x in query: - if x.sub_type == "live": - live_data.append(x) - if x.sub_type == "up": - up_data.append(x) - if x.sub_type == "season": - season_data.append(x) - return live_data, up_data, season_data +from services.log import logger +from services.db_context import db +from datetime import datetime +from typing import Optional, List + + +class BilibiliSub(db.Model): + __tablename__ = "bilibili_sub" + + id = db.Column(db.Integer(), primary_key=True) + sub_id = db.Column(db.Integer(), nullable=False) + sub_type = db.Column(db.String(), nullable=False) + # 订阅用户 + sub_users = db.Column(db.String(), nullable=False) + # 直播 + live_short_id = db.Column(db.Integer()) + live_status = db.Column(db.Integer) + # 主播/UP + uid = db.Column(db.BigInteger()) + uname = db.Column(db.String()) + latest_video_created = db.Column(db.BigInteger()) # 视频上传时间 + dynamic_upload_time = db.Column(db.BigInteger(), default=0) # 动态发布时间 + # 番剧 + season_name = db.Column(db.String()) + season_id = db.Column(db.Integer()) + season_current_episode = db.Column(db.String()) + season_update_time = db.Column(db.DateTime()) + + _idx1 = db.Index("bilibili_sub_idx1", "sub_id", "sub_type", unique=True) + + @classmethod + async def add_bilibili_sub( + cls, + sub_id: int, + sub_type: str, + sub_user: str, + *, + live_short_id: Optional[int] = None, + live_status: Optional[int] = None, + dynamic_upload_time: Optional[int] = None, + uid: Optional[int] = None, + uname: Optional[str] = None, + latest_video_created: Optional[int] = None, + season_name: Optional[str] = None, + season_id: Optional[int] = None, + season_current_episode: Optional[str] = None, + season_update_time: Optional[datetime] = None, + ) -> bool: + """ + 说明: + 添加订阅 + 参数: + :param sub_id: 订阅名称,房间号,番剧号等 + :param sub_type: 订阅类型 + :param sub_user: 订阅此条目的用户 + :param live_short_id: 直接短 id + :param live_status: 主播开播状态 + :param dynamic_upload_time: 主播/UP最新动态时间 + :param uid: 主播/UP uid + :param uname: 用户名称 + :param latest_video_created: 最新视频上传时间 + :param season_name: 番剧名称 + :param season_id: 番剧 season_id + :param season_current_episode: 番剧最新集数 + :param season_update_time: 番剧更新时间 + """ + try: + async with db.transaction(): + query = ( + await cls.query.where(cls.sub_id == sub_id) + .with_for_update() + .gino.first() + ) + sub_user = sub_user if sub_user[-1] == "," else f"{sub_user}," + if query: + if sub_user not in query.sub_users: + sub_users = query.sub_users + sub_user + await query.update(sub_users=sub_users).apply() + else: + sub = await cls.create( + sub_id=sub_id, sub_type=sub_type, sub_users=sub_user + ) + await sub.update( + live_short_id=live_short_id + if live_short_id + else sub.live_short_id, + live_status=live_status if live_status else sub.live_status, + dynamic_upload_time=dynamic_upload_time + if dynamic_upload_time + else sub.dynamic_upload_time, + uid=uid if uid else sub.uid, + uname=uname if uname else sub.uname, + latest_video_created=latest_video_created + if latest_video_created + else sub.latest_video_created, + season_update_time=season_update_time + if season_update_time + else sub.season_update_time, + season_current_episode=season_current_episode + if season_current_episode + else sub.season_current_episode, + season_id=season_id if season_id else sub.season_id, + season_name=season_name if season_name else sub.season_name, + ).apply() + return True + except Exception as e: + logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}") + return False + + @classmethod + async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool: + """ + 说明: + 删除订阅 + 参数: + :param sub_id: 订阅名称 + :param sub_user: 删除此条目的用户 + """ + try: + async with db.transaction(): + query = ( + await cls.query.where( + (cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user)) + ) + .with_for_update() + .gino.first() + ) + if not query: + return False + await query.update( + sub_users=query.sub_users.replace(f"{sub_user},", "") + ).apply() + if not query.sub_users.strip(): + await query.delete() + return True + except Exception as e: + logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}") + return False + + @classmethod + async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]: + """ + 说明: + 获取订阅对象 + 参数: + :param sub_id: 订阅 id + """ + return await cls.query.where(cls.sub_id == sub_id).gino.first() + + @classmethod + async def get_sub_data(cls, id_: str) -> List["BilibiliSub"]: + """ + 获取 id_ 订阅的所有内容 + :param id_: id + """ + query = cls.query.where(cls.sub_users.contains(id_)) + return await query.gino.all() + + @classmethod + async def update_sub_info( + cls, + sub_id: int, + *, + live_short_id: Optional[int] = None, + live_status: Optional[int] = None, + dynamic_upload_time: Optional[int] = None, + uid: Optional[int] = None, + uname: Optional[str] = None, + latest_video_created: Optional[int] = None, + season_name: Optional[str] = None, + season_id: Optional[int] = None, + season_current_episode: Optional[str] = None, + season_update_time: Optional[datetime] = None, + ) -> bool: + """ + 说明: + 更新订阅信息 + 参数: + :param sub_id: 订阅名称,房间号,番剧号等 + :param live_short_id: 直接短 id + :param live_status: 主播开播状态 + :param dynamic_upload_time: 主播/UP最新动态时间 + :param uid: 主播/UP uid + :param uname: 用户名称 + :param latest_video_created: 最新视频上传时间 + :param season_name: 番剧名称 + :param season_id: 番剧 season_id + :param season_current_episode: 番剧最新集数 + :param season_update_time: 番剧更新时间 + """ + try: + async with db.transaction(): + sub = ( + await cls.query.where(cls.sub_id == sub_id) + .with_for_update() + .gino.first() + ) + if sub: + await sub.update( + live_short_id=live_short_id + if live_short_id is not None + else sub.live_short_id, + live_status=live_status + if live_status is not None + else sub.live_status, + dynamic_upload_time=dynamic_upload_time + if dynamic_upload_time is not None + else sub.dynamic_upload_time, + uid=uid if uid is not None else sub.uid, + uname=uname if uname is not None else sub.uname, + latest_video_created=latest_video_created + if latest_video_created is not None + else sub.latest_video_created, + season_update_time=season_update_time + if season_update_time is not None + else sub.season_update_time, + season_current_episode=season_current_episode + if season_current_episode is not None + else sub.season_current_episode, + season_id=season_id if season_id is not None else sub.season_id, + season_name=season_name + if season_name is not None + else sub.season_name, + ).apply() + return True + except Exception as e: + logger.info(f"bilibili_sub 更新订阅错误 {type(e)}: {e}") + return False + + @classmethod + async def get_all_sub_data( + cls, + ) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]": + """ + 说明: + 分类获取所有数据 + """ + live_data = [] + up_data = [] + season_data = [] + query = await cls.query.gino.all() + for x in query: + if x.sub_type == "live": + live_data.append(x) + if x.sub_type == "up": + up_data.append(x) + if x.sub_type == "season": + season_data.append(x) + return live_data, up_data, season_data diff --git a/plugins/bilibili_sub/utils.py b/plugins/bilibili_sub/utils.py old mode 100644 new mode 100755 index 89325453..869944f2 --- a/plugins/bilibili_sub/utils.py +++ b/plugins/bilibili_sub/utils.py @@ -1,72 +1,71 @@ -from utils.image_utils import CreateImg -from configs.path_config import IMAGE_PATH -from pathlib import Path -from bilibili_api import user -from io import BytesIO -import aiohttp - - -BORDER_PATH = Path(IMAGE_PATH) / 'border' -BORDER_PATH.mkdir(parents=True, exist_ok=True) - - -async def get_pic(url: str) -> bytes: - """ - 获取图像 - :param url: 图像链接 - :return: 图像二进制 - """ - async with aiohttp.ClientSession() as session: - async with session.get(url, timeout=2) as response: - return await response.read() - - -async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str): - """ - 生成主播简介图片 - :param uid: 主播 uid - :param title: 直播间标题 - :param cover: 直播封面 - :param tags: 直播标签 - :param des: 直播简介 - :return: - """ - u = user.User(uid) - user_info = await u.get_user_info() - name = user_info['name'] - sex = user_info['sex'] - face = user_info['face'] - sign = user_info['sign'] - ava = CreateImg(100, 100, background=BytesIO(await get_pic(face))) - ava.circle() - cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover))) - print() - - -def _create_live_des_image(title: str, cover: CreateImg, tags: str, des: str, user_name: str, sex: str, sign: str, ava: CreateImg): - """ - 生成主播简介图片 - :param title: 直播间标题 - :param cover: 直播封面 - :param tags: 直播标签 - :param des: 直播简介 - :param user_name: 主播名称 - :param sex: 主播性别 - :param sign: 主播签名 - :param ava: 主播头像 - :return: - """ - border = BORDER_PATH / '0.png' - border_img = None - if border.exists(): - border_img = CreateImg(1772, 2657, background=border) - bk = CreateImg(1772, 2657, font_size=30) - bk.paste(cover, (0, 100), center_type='by_width') - - - - - - - - +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH +from utils.http_utils import AsyncHttpx +from pathlib import Path +from bilibili_api import user +from io import BytesIO + + +BORDER_PATH = Path(IMAGE_PATH) / "border" +BORDER_PATH.mkdir(parents=True, exist_ok=True) + + +async def get_pic(url: str) -> bytes: + """ + 获取图像 + :param url: 图像链接 + :return: 图像二进制 + """ + return (await AsyncHttpx.get(url, timeout=10)).content + + +async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str): + """ + 生成主播简介图片 + :param uid: 主播 uid + :param title: 直播间标题 + :param cover: 直播封面 + :param tags: 直播标签 + :param des: 直播简介 + :return: + """ + u = user.User(uid) + user_info = await u.get_user_info() + name = user_info["name"] + sex = user_info["sex"] + face = user_info["face"] + sign = user_info["sign"] + ava = CreateImg(100, 100, background=BytesIO(await get_pic(face))) + ava.circle() + cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover))) + print() + + +def _create_live_des_image( + title: str, + cover: CreateImg, + tags: str, + des: str, + user_name: str, + sex: str, + sign: str, + ava: CreateImg, +): + """ + 生成主播简介图片 + :param title: 直播间标题 + :param cover: 直播封面 + :param tags: 直播标签 + :param des: 直播简介 + :param user_name: 主播名称 + :param sex: 主播性别 + :param sign: 主播签名 + :param ava: 主播头像 + :return: + """ + border = BORDER_PATH / "0.png" + border_img = None + if border.exists(): + border_img = CreateImg(1772, 2657, background=border) + bk = CreateImg(1772, 2657, font_size=30) + bk.paste(cover, (0, 100), center_type="by_width") diff --git a/plugins/bt/__init__.py b/plugins/bt/__init__.py old mode 100644 new mode 100755 index d155550e..baecd6e3 --- a/plugins/bt/__init__.py +++ b/plugins/bt/__init__.py @@ -7,7 +7,6 @@ from nonebot.adapters.cqhttp import PrivateMessageEvent from utils.utils import get_message_text from nonebot.adapters.cqhttp.permission import PRIVATE from asyncio.exceptions import TimeoutError -from aiohttp.client_exceptions import ServerDisconnectedError __zx_plugin_name__ = "磁力搜索" __plugin_usage__ = """ @@ -89,11 +88,9 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): send_flag = True except TimeoutError: await bt.finish(f"搜索 {keyword} 超时...") - except ServerDisconnectedError: - await bt.finish(f"搜索 {keyword} 连接失败") except Exception as e: await bt.finish(f"bt 其他未知错误..") - logger.error(f"bt 错误 e:{e}") + logger.error(f"bt 错误 {type(e)}:{e}") if not send_flag: await bt.send(f"{keyword} 未搜索到...") logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页") diff --git a/plugins/bt/data_source.py b/plugins/bt/data_source.py old mode 100644 new mode 100755 index 3ea7270e..ddec06f5 --- a/plugins/bt/data_source.py +++ b/plugins/bt/data_source.py @@ -1,8 +1,6 @@ -from utils.user_agent import get_user_agent -import aiohttp +from utils.http_utils import AsyncHttpx from configs.config import Config from bs4 import BeautifulSoup -from utils.utils import get_local_proxy import platform if platform.system() == "Windows": @@ -15,36 +13,39 @@ url = "http://www.eclzz.world" async def get_bt_info(keyword: str, page: str): - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get( - f"{url}/s/{keyword}_rel_{page}.html", proxy=get_local_proxy(), timeout=5 - ) as response: - text = await response.text() - if text.find("大约0条结果") != -1: - return - soup = BeautifulSoup(text, "lxml") - item_lst = soup.find_all("div", {"class": "search-item"}) - bt_max_num = Config.get_config("bt", "BT_MAX_NUM") - bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst) - for item in item_lst[:bt_max_num]: - divs = item.find_all("div") - title = ( - str(divs[0].find("a").text) - .replace("", "") - .replace("", "") - .strip() - ) - spans = divs[2].find_all("span") - itype = spans[0].text - create_time = spans[1].find("b").text - file_size = spans[2].find("b").text - link = await get_download_link(divs[0].find("a")["href"], session) - yield title, itype, create_time, file_size, link + """ + 获取资源信息 + :param keyword: 关键词 + :param page: 页数 + """ + text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text + if text.find("大约0条结果") != -1: + return + soup = BeautifulSoup(text, "lxml") + item_lst = soup.find_all("div", {"class": "search-item"}) + bt_max_num = Config.get_config("bt", "BT_MAX_NUM") + bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst) + for item in item_lst[:bt_max_num]: + divs = item.find_all("div") + title = ( + str(divs[0].find("a").text) + .replace("", "") + .replace("", "") + .strip() + ) + spans = divs[2].find_all("span") + itype = spans[0].text + create_time = spans[1].find("b").text + file_size = spans[2].find("b").text + link = await get_download_link(divs[0].find("a")["href"]) + yield title, itype, create_time, file_size, link -async def get_download_link(_url: str, session) -> str: - async with session.get( - f"{url}{_url}", proxy=get_local_proxy(), timeout=30 - ) as response: - soup = BeautifulSoup(await response.text(), "lxml") - return soup.find("a", {"id": "down-url"})["href"] +async def get_download_link(_url: str) -> str: + """ + 获取资源下载地址 + :param _url: 链接 + """ + text = (await AsyncHttpx.get(f"{url}{_url}")).text + soup = BeautifulSoup(text, "lxml") + return soup.find("a", {"id": "down-url"})["href"] diff --git a/plugins/c_song/__init__.py b/plugins/c_song/__init__.py old mode 100644 new mode 100755 index c2195941..d398d59a --- a/plugins/c_song/__init__.py +++ b/plugins/c_song/__init__.py @@ -1,57 +1,57 @@ -from .music_163 import get_song_id, get_song_info -from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent -from nonebot.typing import T_State -from services.log import logger -from nonebot import on_command - - -__zx_plugin_name__ = "点歌" -__plugin_usage__ = """ -usage: - 在线点歌 - 指令: - 点歌 [歌名] -""".strip() -__plugin_des__ = "为你点播了一首曾经的歌" -__plugin_cmd__ = ["点歌 [歌名]"] -__plugin_type__ = ("一些工具",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["点歌"], -} - - -songpicker = on_command("点歌", priority=5, block=True) - - -@songpicker.handle() -async def handle_first_receive(bot: Bot, event: Event, state: T_State): - args = str(event.get_message()).strip() - if args: - state["song_name"] = args - - -@songpicker.got("song_name", prompt="歌名是?") -async def _(bot: Bot, event: Event, state: T_State): - song = state["song_name"] - song_id = await get_song_id(song) - if not song_id: - await songpicker.finish("没有找到这首歌!", at_sender=True) - for _ in range(3): - song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}] - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 点歌 :{song}" - ) - await songpicker.finish(song_content) - else: - await songpicker.finish("网易云繁忙...") - - - - +from .music_163 import get_song_id, get_song_info +from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent +from nonebot.typing import T_State +from services.log import logger +from nonebot import on_command + + +__zx_plugin_name__ = "点歌" +__plugin_usage__ = """ +usage: + 在线点歌 + 指令: + 点歌 [歌名] +""".strip() +__plugin_des__ = "为你点播了一首曾经的歌" +__plugin_cmd__ = ["点歌 [歌名]"] +__plugin_type__ = ("一些工具",) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["点歌"], +} + + +music = on_command("点歌", priority=5, block=True) + + +@music.handle() +async def handle_first_receive(bot: Bot, event: Event, state: T_State): + args = str(event.get_message()).strip() + if args: + state["song_name"] = args + + +@music.got("song_name", prompt="歌名是?") +async def _(bot: Bot, event: Event, state: T_State): + song = state["song_name"] + song_id = await get_song_id(song) + if not song_id: + await music.finish("没有找到这首歌!", at_sender=True) + for _ in range(3): + song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}] + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 点歌 :{song}" + ) + await music.finish(song_content) + else: + await music.finish("网易云繁忙...") + + + + diff --git a/plugins/c_song/music_163.py b/plugins/c_song/music_163.py old mode 100644 new mode 100755 index 0202313b..8552d06f --- a/plugins/c_song/music_163.py +++ b/plugins/c_song/music_163.py @@ -1,47 +1,38 @@ -import aiohttp -import json - - -headers = {"referer": "http://music.163.com"} -cookies = {"appver": "2.0.2"} - - -async def search_song(song_name: str): - async with aiohttp.ClientSession( - headers=headers, cookies=cookies - ) as session: - async with session.post( - f"http://music.163.com/api/search/get/", - data={"s": song_name, "limit": 1, "type": 1, "offset": 0}, - ) as r: - if r.status != 200: - return None - r = await r.text() - return json.loads(r) - - -async def get_song_id(songName: str) -> int: - """ - 根据用户输入的songName 获取候选songId列表 [默认songId数量:5] - """ - r = await search_song(songName) - return r["result"]["songs"][0]["id"] - - -async def get_song_info(songId: int): - """ - 获取歌曲信息 - """ - async with aiohttp.ClientSession( - headers=headers, cookies=cookies - ) as session: - async with session.post( - f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D", - ) as r: - if r.status != 200: - return None - r = await r.text() - return json.loads(r) - - - +from utils.http_utils import AsyncHttpx +import json + + +headers = {"referer": "http://music.163.com"} +cookies = {"appver": "2.0.2"} + + +async def search_song(song_name: str): + """ + 搜索歌曲 + :param song_name: 歌名 + """ + r = await AsyncHttpx.post( + f"http://music.163.com/api/search/get/", + data={"s": song_name, "limit": 1, "type": 1, "offset": 0}, + ) + if r.status_code != 200: + return None + return json.loads(r.text) + + +async def get_song_id(song_name: str) -> int: + """ """ + r = await search_song(song_name) + return r["result"]["songs"][0]["id"] + + +async def get_song_info(songId: int): + """ + 获取歌曲信息 + """ + r = await AsyncHttpx.post( + f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D", + ) + if r.status_code != 200: + return None + return json.loads(r.text) diff --git a/plugins/check/__init__.py b/plugins/check/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/check/data_source.py b/plugins/check/data_source.py old mode 100644 new mode 100755 index 81b156ee..c77865cf --- a/plugins/check/data_source.py +++ b/plugins/check/data_source.py @@ -1,11 +1,7 @@ import psutil -import aiohttp import time from datetime import datetime -from utils.user_agent import get_user_agent -from asyncio.exceptions import TimeoutError -from aiohttp.client_exceptions import ClientConnectorError -from utils.utils import get_local_proxy +from utils.http_utils import AsyncHttpx from utils.image_utils import CreateImg from configs.path_config import IMAGE_PATH from pathlib import Path @@ -34,23 +30,16 @@ class Check: self.disk = psutil.disk_usage("/").percent async def check_network(self): - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - try: - async with session.get( - "https://www.baidu.com/", proxy=get_local_proxy(), timeout=3 - ) as response: - pass - except (TimeoutError, ClientConnectorError) as e: - logger.warning(f"访问BaiDu失败... e: {e}") - self.baidu = 404 - try: - async with session.get( - "https://www.google.com/", proxy=get_local_proxy(), timeout=3 - ) as response: - pass - except (TimeoutError, ClientConnectorError) as e: - logger.warning(f"访问Google失败... e: {e}") - self.google = 404 + try: + await AsyncHttpx.get("https://www.baidu.com/", timeout=5) + except Exception as e: + logger.warning(f"访问BaiDu失败... {type(e)}: {e}") + self.baidu = 404 + try: + await AsyncHttpx.get("https://www.google.com/", timeout=5) + except Exception as e: + logger.warning(f"访问Google失败... {type(e)}: {e}") + self.google = 404 def check_user(self): rst = "" diff --git a/plugins/check_zhenxun_update/__init__.py b/plugins/check_zhenxun_update/__init__.py old mode 100644 new mode 100755 index 1014025b..ff590e40 --- a/plugins/check_zhenxun_update/__init__.py +++ b/plugins/check_zhenxun_update/__init__.py @@ -1,126 +1,126 @@ -from nonebot.adapters.cqhttp import Bot, MessageEvent -from nonebot.typing import T_State -from nonebot.permission import SUPERUSER -from nonebot import on_command -from .data_source import check_update, get_latest_version_data -from services.log import logger -from utils.utils import scheduler, get_bot -from pathlib import Path -from configs.config import Config -from nonebot.rule import to_me -import platform -import os - - -__zx_plugin_name__ = "自动更新 [Superuser]" -__plugin_usage__ = """ -usage: - 检查更新真寻最新版本,包括了自动更新 - 指令: - 检查更新真寻 - 重启 -""".strip() -__plugin_des__ = "就算是真寻也会成长的" -__plugin_cmd__ = ["检查更新真寻", "重启"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_configs__ = { - "AUTO_UPDATE_ZHENXUN": { - "value": False, - "help": "真寻是否自动检查更新", - "default": False, - } -} - -update_zhenxun = on_command("检查更新真寻", permission=SUPERUSER, priority=1, block=True) - -restart = on_command( - "重启", - aliases={"restart"}, - permission=SUPERUSER, - rule=to_me(), - priority=1, - block=True, -) - - -@update_zhenxun.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - try: - code, error = await check_update(bot) - if error: - logger.error(f"更新真寻未知错误 {error}") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), message=f"更新真寻未知错误 {error}" - ) - except Exception as e: - logger.error(f"更新真寻未知错误 {type(e)}:{e}") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"更新真寻未知错误 {type(e)}:{e}", - ) - else: - if code == 200: - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...." - ) - - -@restart.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - if str(platform.system()).lower() == "windows": - await restart.finish("暂无windows重启脚本...") - - -@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)") -async def _(bot: Bot, event: MessageEvent, state: T_State): - flag = state["flag"] - if flag.lower() in ["true", "是", "好", "确定", "确定是"]: - await restart.send("开始重启真寻..请稍等...") - open("is_restart", "w") - os.system("./restart.sh") - else: - await restart.send("已取消操作...") - - -@scheduler.scheduled_job( - "cron", - hour=12, - minute=0, -) -async def _(): - if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"): - _version = "v0.0.0" - _version_file = Path() / "__version__" - if _version_file.exists(): - _version = ( - open(_version_file, "r", encoding="utf8") - .readline() - .split(":")[-1] - .strip() - ) - data = await get_latest_version_data() - if data: - latest_version = data["name"] - if _version != latest_version: - bot = get_bot() - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"检测到真寻版本更新\n" - f"当前版本:{_version},最新版本:{latest_version}\n" - f"尝试自动更新...", - ) - try: - code = await check_update(bot) - except Exception as e: - logger.error(f"更新真寻未知错误 {type(e)}:{e}") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"更新真寻未知错误 {type(e)}:{e}\n", - ) - else: - if code == 200: - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"更新完毕,请重启真寻....", - ) +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.typing import T_State +from nonebot.permission import SUPERUSER +from nonebot import on_command +from .data_source import check_update, get_latest_version_data +from services.log import logger +from utils.utils import scheduler, get_bot +from pathlib import Path +from configs.config import Config +from nonebot.rule import to_me +import platform +import os + + +__zx_plugin_name__ = "自动更新 [Superuser]" +__plugin_usage__ = """ +usage: + 检查更新真寻最新版本,包括了自动更新 + 指令: + 检查更新真寻 + 重启 +""".strip() +__plugin_des__ = "就算是真寻也会成长的" +__plugin_cmd__ = ["检查更新真寻", "重启"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_configs__ = { + "AUTO_UPDATE_ZHENXUN": { + "value": False, + "help": "真寻是否自动检查更新", + "default": False, + } +} + +update_zhenxun = on_command("检查更新真寻", permission=SUPERUSER, priority=1, block=True) + +restart = on_command( + "重启", + aliases={"restart"}, + permission=SUPERUSER, + rule=to_me(), + priority=1, + block=True, +) + + +@update_zhenxun.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + try: + code, error = await check_update(bot) + if error: + logger.error(f"更新真寻未知错误 {error}") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), message=f"更新真寻未知错误 {error}" + ) + except Exception as e: + logger.error(f"更新真寻未知错误 {type(e)}:{e}") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"更新真寻未知错误 {type(e)}:{e}", + ) + else: + if code == 200: + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...." + ) + + +@restart.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(platform.system()).lower() == "windows": + await restart.finish("暂无windows重启脚本...") + + +@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)") +async def _(bot: Bot, event: MessageEvent, state: T_State): + flag = state["flag"] + if flag.lower() in ["true", "是", "好", "确定", "确定是"]: + await restart.send("开始重启真寻..请稍等...") + open("is_restart", "w") + os.system("./restart.sh") + else: + await restart.send("已取消操作...") + + +@scheduler.scheduled_job( + "cron", + hour=12, + minute=0, +) +async def _(): + if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"): + _version = "v0.0.0" + _version_file = Path() / "__version__" + if _version_file.exists(): + _version = ( + open(_version_file, "r", encoding="utf8") + .readline() + .split(":")[-1] + .strip() + ) + data = await get_latest_version_data() + if data: + latest_version = data["name"] + if _version != latest_version: + bot = get_bot() + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"检测到真寻版本更新\n" + f"当前版本:{_version},最新版本:{latest_version}\n" + f"尝试自动更新...", + ) + try: + code = await check_update(bot) + except Exception as e: + logger.error(f"更新真寻未知错误 {type(e)}:{e}") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"更新真寻未知错误 {type(e)}:{e}\n", + ) + else: + if code == 200: + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"更新完毕,请重启真寻....", + ) diff --git a/plugins/check_zhenxun_update/data_source.py b/plugins/check_zhenxun_update/data_source.py old mode 100644 new mode 100755 index c240fefe..bf7ac1a1 --- a/plugins/check_zhenxun_update/data_source.py +++ b/plugins/check_zhenxun_update/data_source.py @@ -1,231 +1,212 @@ -from aiohttp.client_exceptions import ClientConnectorError -from nonebot.adapters.cqhttp import Bot, Message -from utils.user_agent import get_user_agent -from utils.utils import get_local_proxy -from utils.image_utils import CreateImg -from configs.path_config import IMAGE_PATH -from utils.message_builder import image -from typing import List -from services.log import logger -from pathlib import Path -import ujson as json -import nonebot -import asyncio -import aiofiles -import aiohttp -import platform -import tarfile -import shutil -import os - -if str(platform.system()).lower() == "windows": - policy = asyncio.WindowsSelectorEventLoopPolicy() - asyncio.set_event_loop_policy(policy) - - -driver = nonebot.get_driver() - -release_url = "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest" - -_version_file = Path() / "__version__" -zhenxun_latest_tar_gz = Path() / "zhenxun_latest_file.tar.gz" -temp_dir = Path() / "temp" -backup_dir = Path() / "backup" - - -@driver.on_bot_connect -async def remind(bot: Bot): - if str(platform.system()).lower() != "windows": - restart = Path() / "restart.sh" - if not restart.exists(): - with open(restart, "w", encoding="utf8") as f: - f.write( - f"pid=$(netstat -tunlp | grep " - + str(bot.config.port) - + " | awk '{print $7}')\n" - "pid=${pid%/*}\n" - "kill -9 $pid\n" - "sleep 3\n" - "python3 bot.py" - ) - os.system("chmod +x ./restart.sh") - logger.info("已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合...") - is_restart_file = Path() / "is_restart" - if is_restart_file.exists(): - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"真寻重启完毕...", - ) - is_restart_file.unlink() - - -async def check_update(bot: Bot) -> 'int, str': - logger.info("开始检查更新真寻酱....") - _version = "v0.0.0" - if _version_file.exists(): - _version = ( - open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip() - ) - data = await get_latest_version_data() - if data: - latest_version = data["name"] - if _version != latest_version: - tar_gz_url = data["tarball_url"] - logger.info(f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....", - ) - logger.info(f"开始下载真寻最新版文件....") - if await download_latest_file(tar_gz_url): - logger.info("下载真寻最新版文件完成....") - error = await asyncio.get_event_loop().run_in_executor( - None, _file_handle, latest_version - ) - if error: - return 998, error - logger.info("真寻更新完毕,清理文件完成....") - logger.info("开始获取真寻更新日志.....") - update_info = data["body"] - width = 0 - height = len(update_info.split('\n')) * 24 - A = CreateImg(width, height, font_size=20) - for m in update_info.split('\n'): - w, h = A.getsize(m) - if w > width: - width = w - A = CreateImg(width + 50, height, font_size=20) - A.text((10, 10), update_info) - A.save(f'{IMAGE_PATH}/update_info.png') - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=Message(f"真寻更新完成,版本:{_version} -> {latest_version}\n" - f"更新日期:{data['created_at']}\n" - f"更新日志:\n" - f"{image('update_info.png')}"), - ) - return 200, '' - else: - logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"下载真寻最新版本失败...版本号:{latest_version}.", - ) - else: - logger.info(f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...", - ) - else: - logger.warning("自动获取真寻版本失败....") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...." - ) - return 999, '' - - -def _file_handle(latest_version: str) -> str: - if not temp_dir.exists(): - temp_dir.mkdir(exist_ok=True, parents=True) - if backup_dir.exists(): - shutil.rmtree(backup_dir) - tf = None - error = '' - try: - backup_dir.mkdir(exist_ok=True, parents=True) - logger.info("开始解压真寻文件压缩包....") - tf = tarfile.open(zhenxun_latest_tar_gz) - tf.extractall(temp_dir) - logger.info("解压真寻文件压缩包完成....") - zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0] - update_info_file = Path(zhenxun_latest_file) / "update_info.json" - update_info = json.load(open(update_info_file, "r", encoding="utf8")) - update_file = update_info["update_file"] - add_file = update_info["add_file"] - delete_file = update_info["delete_file"] - config_file = Path() / "configs" / "config.py" - config_path_file = Path() / "configs" / "config_path.py" - for file in delete_file + update_file: - file = Path() / file - backup_file = Path(backup_dir) / file - if file.exists(): - backup_file.parent.mkdir(parents=True, exist_ok=True) - if backup_file.exists(): - backup_file.unlink() - if file not in [config_file, config_path_file]: - os.rename(file.absolute(), backup_file.absolute()) - else: - with open(file, "r", encoding="utf8") as rf: - data = rf.read() - with open(backup_file, "w", encoding="utf8") as wf: - wf.write(data) - logger.info(f"已备份文件:{file}") - for file in add_file + update_file: - new_file = Path(zhenxun_latest_file) / file - old_file = Path() / file - if old_file not in [config_file, config_path_file]: - if not old_file.exists() and new_file.exists(): - os.rename(new_file.absolute(), old_file.absolute()) - logger.info(f"已更新文件:{file}") - else: - tmp = "" - new_lines = open(new_file, "r", encoding="utf8").readlines() - old_lines = open(old_file, "r", encoding="utf8").readlines() - for nl in new_lines: - tmp += check_old_lines(old_lines, nl) - with open(file, "w", encoding="utf8") as f: - f.write(tmp) - except Exception as e: - error = f'{type(e)}:{e}' - if tf: - tf.close() - if temp_dir.exists(): - shutil.rmtree(temp_dir) - if zhenxun_latest_tar_gz.exists(): - zhenxun_latest_tar_gz.unlink() - local_update_info_file = Path() / "update_info.json" - if local_update_info_file.exists(): - local_update_info_file.unlink() - with open(_version_file, "w", encoding="utf8") as f: - f.write(f"__version__: {latest_version}") - return error - - -# 获取最新版本号 -async def get_latest_version_data() -> dict: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - for _ in range(3): - try: - async with session.get(release_url, proxy=get_local_proxy()) as res: - if res.status == 200: - return await res.json() - except (TimeoutError, ClientConnectorError): - pass - return {} - - -# 下载文件 -async def download_latest_file(url_: str) -> bool: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - for _ in range(3): - try: - async with session.get(url_, proxy=get_local_proxy()) as res: - if res.status == 200: - async with aiofiles.open(zhenxun_latest_tar_gz, "wb") as f: - await f.write(await res.read()) - return True - except (TimeoutError, ClientConnectorError): - pass - return False - - -# 逐行检测 -def check_old_lines(lines: List[str], line: str) -> str: - if "=" not in line: - return line - for l in lines: - if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip(): - if l.split("=")[1].strip() == 'None': - return l - return line +from nonebot.adapters.cqhttp import Bot, Message +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from utils.http_utils import AsyncHttpx +from typing import List +from services.log import logger +from pathlib import Path +import ujson as json +import nonebot +import asyncio +import platform +import tarfile +import shutil +import os + +if str(platform.system()).lower() == "windows": + policy = asyncio.WindowsSelectorEventLoopPolicy() + asyncio.set_event_loop_policy(policy) + + +driver = nonebot.get_driver() + +release_url = "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest" + +_version_file = Path() / "__version__" +zhenxun_latest_tar_gz = Path() / "zhenxun_latest_file.tar.gz" +temp_dir = Path() / "temp" +backup_dir = Path() / "backup" + + +@driver.on_bot_connect +async def remind(bot: Bot): + if str(platform.system()).lower() != "windows": + restart = Path() / "restart.sh" + if not restart.exists(): + with open(restart, "w", encoding="utf8") as f: + f.write( + f"pid=$(netstat -tunlp | grep " + + str(bot.config.port) + + " | awk '{print $7}')\n" + "pid=${pid%/*}\n" + "kill -9 $pid\n" + "sleep 3\n" + "python3 bot.py" + ) + os.system("chmod +x ./restart.sh") + logger.info("已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合...") + is_restart_file = Path() / "is_restart" + if is_restart_file.exists(): + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"真寻重启完毕...", + ) + is_restart_file.unlink() + + +async def check_update(bot: Bot) -> 'int, str': + logger.info("开始检查更新真寻酱....") + _version = "v0.0.0" + if _version_file.exists(): + _version = ( + open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip() + ) + data = await get_latest_version_data() + if data: + latest_version = data["name"] + if _version != latest_version: + tar_gz_url = data["tarball_url"] + logger.info(f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....", + ) + logger.info(f"开始下载真寻最新版文件....") + if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz): + logger.info("下载真寻最新版文件完成....") + error = await asyncio.get_event_loop().run_in_executor( + None, _file_handle, latest_version + ) + if error: + return 998, error + logger.info("真寻更新完毕,清理文件完成....") + logger.info("开始获取真寻更新日志.....") + update_info = data["body"] + width = 0 + height = len(update_info.split('\n')) * 24 + A = CreateImg(width, height, font_size=20) + for m in update_info.split('\n'): + w, h = A.getsize(m) + if w > width: + width = w + A = CreateImg(width + 50, height, font_size=20) + A.text((10, 10), update_info) + A.save(f'{IMAGE_PATH}/update_info.png') + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=Message(f"真寻更新完成,版本:{_version} -> {latest_version}\n" + f"更新日期:{data['created_at']}\n" + f"更新日志:\n" + f"{image('update_info.png')}"), + ) + return 200, '' + else: + logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"下载真寻最新版本失败...版本号:{latest_version}.", + ) + else: + logger.info(f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...", + ) + else: + logger.warning("自动获取真寻版本失败....") + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...." + ) + return 999, '' + + +def _file_handle(latest_version: str) -> str: + if not temp_dir.exists(): + temp_dir.mkdir(exist_ok=True, parents=True) + if backup_dir.exists(): + shutil.rmtree(backup_dir) + tf = None + error = '' + try: + backup_dir.mkdir(exist_ok=True, parents=True) + logger.info("开始解压真寻文件压缩包....") + tf = tarfile.open(zhenxun_latest_tar_gz) + tf.extractall(temp_dir) + logger.info("解压真寻文件压缩包完成....") + zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0] + update_info_file = Path(zhenxun_latest_file) / "update_info.json" + update_info = json.load(open(update_info_file, "r", encoding="utf8")) + update_file = update_info["update_file"] + add_file = update_info["add_file"] + delete_file = update_info["delete_file"] + config_file = Path() / "configs" / "config.py" + config_path_file = Path() / "configs" / "config_path.py" + for file in delete_file + update_file: + file = Path() / file + backup_file = Path(backup_dir) / file + if file.exists(): + backup_file.parent.mkdir(parents=True, exist_ok=True) + if backup_file.exists(): + backup_file.unlink() + if file not in [config_file, config_path_file]: + os.rename(file.absolute(), backup_file.absolute()) + else: + with open(file, "r", encoding="utf8") as rf: + data = rf.read() + with open(backup_file, "w", encoding="utf8") as wf: + wf.write(data) + logger.info(f"已备份文件:{file}") + for file in add_file + update_file: + new_file = Path(zhenxun_latest_file) / file + old_file = Path() / file + if old_file not in [config_file, config_path_file]: + if not old_file.exists() and new_file.exists(): + os.rename(new_file.absolute(), old_file.absolute()) + logger.info(f"已更新文件:{file}") + else: + tmp = "" + new_lines = open(new_file, "r", encoding="utf8").readlines() + old_lines = open(old_file, "r", encoding="utf8").readlines() + for nl in new_lines: + tmp += check_old_lines(old_lines, nl) + with open(file, "w", encoding="utf8") as f: + f.write(tmp) + except Exception as e: + error = f'{type(e)}:{e}' + if tf: + tf.close() + if temp_dir.exists(): + shutil.rmtree(temp_dir) + if zhenxun_latest_tar_gz.exists(): + zhenxun_latest_tar_gz.unlink() + local_update_info_file = Path() / "update_info.json" + if local_update_info_file.exists(): + local_update_info_file.unlink() + with open(_version_file, "w", encoding="utf8") as f: + f.write(f"__version__: {latest_version}") + return error + + +# 获取最新版本号 +async def get_latest_version_data() -> dict: + for _ in range(3): + try: + res = await AsyncHttpx.get(release_url) + if res.status_code == 200: + return res.json() + except TimeoutError: + pass + except Exception as e: + logger.error(f"检查更新真寻获取版本失败 {type(e)}:{e}") + return {} + + +# 逐行检测 +def check_old_lines(lines: List[str], line: str) -> str: + if "=" not in line: + return line + for l in lines: + if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip(): + return l + return line diff --git a/plugins/coser/__init__.py b/plugins/coser/__init__.py old mode 100644 new mode 100755 index 1d67c019..947dc567 --- a/plugins/coser/__init__.py +++ b/plugins/coser/__init__.py @@ -1,13 +1,8 @@ from nonebot import on_command from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from services.log import logger -from asyncio.exceptions import TimeoutError +from nonebot.adapters.cqhttp import Bot, MessageEvent from utils.message_builder import image -from configs.path_config import IMAGE_PATH -from utils.utils import get_local_proxy -import aiohttp -import aiofiles +from services.log import logger __zx_plugin_name__ = "coser" __plugin_usage__ = """ @@ -32,38 +27,13 @@ coser = on_command( ) -url = "http://ovooa.com/API/cosplay/api.php" +url = "http://iw233.cn/API/cos.php" @coser.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - async with aiohttp.ClientSession() as session: - try: - for _ in range(3): - try: - async with session.get(url, proxy=get_local_proxy(), timeout=2, verify_ssl=False) as response: - _url = (await response.json())['text'] - async with session.get( - _url, timeout=5, proxy=get_local_proxy(), verify_ssl=False - ) as res: - if res.status == 200: - async with aiofiles.open( - f"{IMAGE_PATH}/temp/{event.user_id}_coser.jpg", "wb" - ) as f: - await f.write(await res.read()) - logger.info( - f"(USER {event.user_id}, " - f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送COSER" - ) - await coser.send( - image(f"{event.user_id}_coser.jpg", "temp") - ) - break - except (TimeoutError, KeyError): - pass - else: - await coser.send("你cos给我看!") - except Exception as e: - await coser.send("发生了预料之外的错误..请稍后再试或联系管理员修复...") - logger.error(f"coser 发送了未知错误 {type(e)}:{e}") + try: + await coser.send(image(url)) + except Exception as e: + await coser.send("你cos给我看!") + logger.error(f"coser 发送了未知错误 {type(e)}:{e}") diff --git a/plugins/csgo/__init__.py b/plugins/csgo/__init__.py deleted file mode 100644 index e955a89f..00000000 --- a/plugins/csgo/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent -from utils.utils import get_message_text, is_number -from .data_source import get_csgola_data, get_5e_data -from services.log import logger - - -__zx_plugin_name__ = "cs国服/平台信息查找" -__plugin_usage__ = """ -usage: - 快速查询csgo战绩和数据 - 指令: - cs国服查询 [steam主页个人id] - 5e查询 [5e战绩个人名称] - 示例:cs国服查询 23848238483 - 示例:5e查询 poster -""" -__plugin_des__ = "什么?你也是rush B玩家?" -__plugin_cmd__ = ["cs国服查询 [steam主页个人id]", "5e查询 [5e战绩个人名称]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["csgo战绩查询", "cs国服查询", "5e查询"], -} - -csgola = on_command("cs国服查询", priority=5, block=True) - -csgo5e = on_command("5e查询", priority=5, block=True) - - -@csgola.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if "http" in msg: - msg = msg[:-1] if msg[-1] == "/" else msg - msg = msg.split("/")[-1] - if not is_number(msg): - await csgola.finish("Id必须为数字!", at_sender=True) - await csgola.send("开始查找...") - img, code = await get_csgola_data(int(msg)) - if code == 200: - await csgola.send(img, at_sender=True) - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查询csgo国服战绩:{msg}" - ) - else: - await csgola.send(img, at_sender=True) - - -@csgo5e.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - await csgola.send("开始查找...") - img, code = await get_5e_data(msg) - if code == 200: - await csgo5e.send(img, at_sender=True) - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查询csgo国服战绩:{msg}" - ) - else: - await csgo5e.send(img, at_sender=True) diff --git a/plugins/csgo/data_source.py b/plugins/csgo/data_source.py deleted file mode 100644 index b6c6969e..00000000 --- a/plugins/csgo/data_source.py +++ /dev/null @@ -1,130 +0,0 @@ -from configs.path_config import IMAGE_PATH -from utils.image_utils import CreateImg -from utils.message_builder import image -from services.log import logger -from utils.browser import get_browser -from playwright._impl._api_types import TimeoutError - -csgola_url = "https://www.csgola.com/player/" -_5e_url = "https://arena.5eplay.com/data/player/" - - -async def get_csgola_data(uid: int) -> "str, int": - page = None - try: - browser = await get_browser() - if not browser: - return "", 997 - page = await browser.new_page() - for _ in range(3): - try: - await page.goto(f"{csgola_url}{uid}", wait_until="networkidle", timeout=10000) - break - except TimeoutError: - pass - else: - return '连接超时...', 995 - await page.set_viewport_size({"width": 2560, "height": 1080}) - - data = await page.query_selector_all(".panel-body") - if not data: - return "未查询到该Id....", 999 - await data[0].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_1.png", timeout=100000) - await data[3].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_2.png", timeout=100000) - await data[5].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_3.png", timeout=100000) - await data[7].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_5.png", timeout=100000) - - ava = await page.query_selector("div.container:nth-child(4) > div:nth-child(1)") - await ava.screenshot(path=f"{IMAGE_PATH}/temp/{uid}_0.png", timeout=100000) - - weapon_data = await page.query_selector(".gun-stats-sec") - await weapon_data.screenshot( - path=f"{IMAGE_PATH}/temp/{uid}_4.png", timeout=100000 - ) - - ava = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_0.png") - statistical_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_1.png") - combined_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_2.png") - detailed_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_3.png") - weapon_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_4.png") - map_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_5.png") - if statistical_data.h > 300: - statistical_data.crop((0, 0, statistical_data.w, 300)) - if combined_data.h > 260: - combined_data.crop((0, 0, combined_data.w, 260)) - if detailed_data.h > 400: - detailed_data.crop((0, 0, detailed_data.w, 400)) - weapon_data.crop((0, 100, weapon_data.w, weapon_data.h)) - map_data.crop((0, 310, map_data.w, map_data.h)) - height = ( - ava.h - + statistical_data.h - + combined_data.h - + detailed_data.h - + weapon_data.h - + map_data.h - ) - bk = CreateImg(1168, height) - current_h = 0 - for img in [ - ava, - statistical_data, - combined_data, - detailed_data, - weapon_data, - map_data, - ]: - bk.paste(img, (0, current_h)) - current_h += img.h - bk.save(f"{IMAGE_PATH}/temp/csgo_{uid}.png") - except Exception as e: - logger.error(f"生成csgola图片错误 {type(e)}:{e}") - if page: - await page.close() - return "发生了错误....", 998 - if page: - await page.close() - return image(f"csgo_{uid}.png", "temp"), 200 - - -async def get_5e_data(uname: str) -> "str, int": - page = None - try: - browser = await get_browser() - if not browser: - return "", 997 - page = await browser.new_page() - await page.goto(f"{_5e_url}{uname}", wait_until="networkidle", timeout=10000) - if "HTTP ERROR 404" in await page.content(): - return "未查询到该玩家...", 999 - await page.set_viewport_size({"width": 2560, "height": 1080}) - body = await page.query_selector("body") - await body.screenshot( - path=f"{IMAGE_PATH}/temp/csgo_{uname}_0.png", timeout=100000 - ) - await page.click("a.match-tab-item:nth-child(2)") - body = await page.query_selector("body") - await body.screenshot( - path=f"{IMAGE_PATH}/temp/csgo_{uname}_1.png", timeout=100000 - ) - await page.click("a.match-tab-item:nth-child(1)") - body = await page.query_selector("body") - await body.screenshot( - path=f"{IMAGE_PATH}/temp/csgo_{uname}_2.png", timeout=100000 - ) - bk = CreateImg(1344 * 3, 2307) - current_w = 0 - for i in range(3): - body = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/csgo_{uname}_{i}.png") - body.crop((600, 90, body.w - 600, body.h - 410)) - bk.paste(body, (current_w, 0)) - current_w += 1344 - bk.save(f"{IMAGE_PATH}/temp/csgo_{uname}.png") - except Exception as e: - logger.error(f"生成5e图片错误 {type(e)}:{e}") - if page: - await page.close() - return "发生了错误...", 998 - if page: - await page.close() - return image(f"csgo_{uname}.png", "temp"), 200 diff --git a/plugins/dialogue/__init__.py b/plugins/dialogue/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/__init__.py b/plugins/draw_card/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/announcement.py b/plugins/draw_card/announcement.py old mode 100644 new mode 100755 index 819fc8d5..42abc559 --- a/plugins/draw_card/announcement.py +++ b/plugins/draw_card/announcement.py @@ -1,11 +1,11 @@ -import aiohttp from bs4 import BeautifulSoup -import re from datetime import datetime, timedelta from .config import DRAW_PATH from pathlib import Path from asyncio.exceptions import TimeoutError from services.log import logger +from utils.http_utils import AsyncHttpx +import re try: import ujson as json except ModuleNotFoundError: @@ -66,16 +66,14 @@ class PrtsAnnouncement: self.game_name = '明日方舟' async def _get_announcement_text(self): - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(prts_url, timeout=7) as res: - soup = BeautifulSoup(await res.text(), 'lxml') - ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'}) - for li in ol: - itype = li.find('span', {'class': 'articleItemCate'}).text - if itype == '活动': - a = li.find('a')['href'] - async with session.get(f'https://ak.hypergryph.com{a}', headers=headers, timeout=7) as res: - return await res.text() + text = (await AsyncHttpx.get(prts_url)).text + soup = BeautifulSoup(text, 'lxml') + ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'}) + for li in ol: + itype = li.find('span', {'class': 'articleItemCate'}).text + if itype == '活动': + a = li.find('a')['href'] + return (await AsyncHttpx.get(f'https://ak.hypergryph.com{a}')).text async def update_up_char(self): prts_up_char.parent.mkdir(parents=True, exist_ok=True) @@ -147,9 +145,7 @@ class GenshinAnnouncement: self.game_name = '原神' async def _get_announcement_text(self): - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(genshin_url, timeout=7) as res: - return await res.text() + return (await AsyncHttpx.get(genshin_url)).text async def update_up_char(self): genshin_up_char.parent.mkdir(exist_ok=True, parents=True) @@ -218,21 +214,20 @@ class PrettyAnnouncement: self.game_name = '赛马娘' async def _get_announcement_text(self): - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(pretty_url, timeout=7) as res: - soup = BeautifulSoup(await res.text(), 'lxml') - divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div') - for div in divs: - a = div.find('a') - try: - title = a['title'] - except (KeyError, TypeError): - continue - if title.find('新角色追加') != -1: - url = a['href'] - break - async with session.get(f'https://wiki.biligame.com/{url}', timeout=7) as res: - return await res.text(), title[:-2] + text = (await AsyncHttpx.get(pretty_url)).text + soup = BeautifulSoup(text, 'lxml') + divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div') + title = " " + for div in divs: + a = div.find('a') + try: + title = a['title'] + except (KeyError, TypeError): + continue + if title.find('新角色追加') != -1: + url = a['href'] + break + return (await AsyncHttpx.get(f'https://wiki.biligame.com/{url}')).text, title[:-2] async def update_up_char(self): pretty_up_char.parent.mkdir(exist_ok=True, parents=True) @@ -343,9 +338,7 @@ class GuardianAnnouncement: self.game_name = '坎公骑冠剑' async def _get_announcement_text(self): - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(guardian_url, timeout=7) as res: - return await res.text() + return (await AsyncHttpx.get(guardian_url)).text async def update_up_char(self): data = { diff --git a/plugins/draw_card/async_update_game_info.py b/plugins/draw_card/async_update_game_info.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/azur_handle.py b/plugins/draw_card/azur_handle.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/config.py b/plugins/draw_card/config.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/fgo_handle.py b/plugins/draw_card/fgo_handle.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/genshin_handle.py b/plugins/draw_card/genshin_handle.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/guardian_handle.py b/plugins/draw_card/guardian_handle.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/init_card_pool.py b/plugins/draw_card/init_card_pool.py old mode 100644 new mode 100755 index 797659e9..f875b02a --- a/plugins/draw_card/init_card_pool.py +++ b/plugins/draw_card/init_card_pool.py @@ -2,6 +2,7 @@ from typing import Any from .config import DATA_PATH from utils.utils import is_number from pathlib import Path +from services.log import logger try: import ujson as json except ModuleNotFoundError: @@ -27,8 +28,11 @@ def init_game_pool(game: str, data: dict, Operator: Any): limited = True if key.find('阿米娅') != -1: continue - tmp_lst.append(Operator(name=key, star=int(data[key]['星级']), - limited=limited, recruit_only=recruit_only, event_only=event_only)) + try: + tmp_lst.append(Operator(name=key, star=int(data[key]['星级']), + limited=limited, recruit_only=recruit_only, event_only=event_only)) + except Exception as e: + logger.warning(f"明日方舟导入角色 {key} 数据错误:{type(e)}:{e}") if game == 'genshin': for key in data.keys(): if key.find('旅行者') != -1: @@ -36,17 +40,26 @@ def init_game_pool(game: str, data: dict, Operator: Any): limited = False if data[key]['常驻/限定'] == '限定UP': limited = True - tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited)) + try: + tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited)) + except Exception as e: + logger.warning(f"原神导入角色 {key} 数据错误:{type(e)}:{e}") if game == 'genshin_arms': for key in data.keys(): if data[key]['获取途径'].find('祈愿') != -1: limited = False if data[key]['获取途径'].find('限定祈愿') != -1: limited = True - tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited)) + try: + tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited)) + except Exception as e: + logger.warning(f"原神导入武器 {key} 数据错误:{type(e)}:{e}") if game == 'pretty': for key in data.keys(): - tmp_lst.append(Operator(name=key, star=data[key]['初始星级'], limited=False)) + try: + tmp_lst.append(Operator(name=key, star=data[key]['初始星级'], limited=False)) + except Exception as e: + logger.warning(f"赛马娘导入角色 {key} 数据错误:{type(e)}:{e}") if game == 'pretty_card': for key in data.keys(): limited = False @@ -54,7 +67,10 @@ def init_game_pool(game: str, data: dict, Operator: Any): limited = True if not data[key]['获取方式']: limited = False - tmp_lst.append(Operator(name=data[key]['中文名'], star=len(data[key]['稀有度']), limited=limited)) + try: + tmp_lst.append(Operator(name=data[key]['中文名'], star=len(data[key]['稀有度']), limited=limited)) + except Exception as e: + logger.warning(f"赛马娘导入卡片 {key} 数据错误:{type(e)}:{e}") if game in ['guardian', 'guardian_arms']: for key in data.keys(): tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=False)) @@ -63,15 +79,21 @@ def init_game_pool(game: str, data: dict, Operator: Any): limited = False if key.find('(') != -1: limited = True - tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) + try: + tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) + except Exception as e: + logger.warning(f"公主连接导入角色 {key} 数据错误:{type(e)}:{e}") if game == 'azur': for key in data.keys(): if is_number(data[key]['星级']): limited = False if '可以建造' not in data[key]['获取途径']: limited = True - tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), - limited=limited, itype=data[key]['类型'])) + try: + tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), + limited=limited, itype=data[key]['类型'])) + except Exception as e: + logger.warning(f"碧蓝航线导入角色 {key} 数据错误:{type(e)}:{e}") if game in ['fgo', 'fgo_card']: for key in data.keys(): limited = False @@ -80,14 +102,20 @@ def init_game_pool(game: str, data: dict, Operator: Any): limited = True except KeyError: pass - tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) + try: + tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) + except Exception as e: + logger.warning(f"FGO导入角色 {key} 数据错误:{type(e)}:{e}") if game == 'onmyoji': for key in data.keys(): limited = False if key in ['奴良陆生', '卖药郎', '鬼灯', '阿香', '蜜桃&芥子', '犬夜叉', '杀生丸', '桔梗', '朽木露琪亚', '黑崎一护', '灶门祢豆子', '灶门炭治郎']: limited = True - tmp_lst.append(Operator(name=data[key]['名称'], star=data[key]['星级'], limited=limited)) + try: + tmp_lst.append(Operator(name=data[key]['名称'], star=data[key]['星级'], limited=limited)) + except Exception as e: + logger.warning(f"阴阳师导入角色 {key} 数据错误:{type(e)}:{e}") # print(tmp_lst) char_name_lst = [x.name for x in tmp_lst] up_char_file = Path(f'{DATA_PATH}/draw_card/draw_card_up/{game.split("_")[0]}_up_char.json') diff --git a/plugins/draw_card/onmyoji_handle.py b/plugins/draw_card/onmyoji_handle.py old mode 100644 new mode 100755 index ca02803f..7b468f0a --- a/plugins/draw_card/onmyoji_handle.py +++ b/plugins/draw_card/onmyoji_handle.py @@ -6,7 +6,6 @@ from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max from .config import ONMYOJI_SR, ONMYOJI_SSR, ONMYOJI_SP, ONMYOJI_R, DRAW_PATH, ONMYOJI_FLAG from dataclasses import dataclass from .init_card_pool import init_game_pool -import nonebot try: import ujson as json except ModuleNotFoundError: diff --git a/plugins/draw_card/pcr_handle.py b/plugins/draw_card/pcr_handle.py old mode 100644 new mode 100755 index 524062fc..c48c8a5a --- a/plugins/draw_card/pcr_handle.py +++ b/plugins/draw_card/pcr_handle.py @@ -1,7 +1,5 @@ import ujson as json -import os from nonebot.adapters.cqhttp import MessageSegment -import nonebot import random from .update_game_info import update_info from .update_game_simple_info import update_simple_info @@ -10,7 +8,6 @@ from .config import PCR_TWO_P, PCR_THREE_P, PCR_ONE_P, DRAW_PATH, PCR_FLAG, PCR_ from dataclasses import dataclass from .init_card_pool import init_game_pool -driver: nonebot.Driver = nonebot.get_driver() ALL_CHAR = [] diff --git a/plugins/draw_card/pretty_handle.py b/plugins/draw_card/pretty_handle.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/prts_handle.py b/plugins/draw_card/prts_handle.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/rule.py b/plugins/draw_card/rule.py old mode 100644 new mode 100755 diff --git a/plugins/draw_card/update_game_info.py b/plugins/draw_card/update_game_info.py old mode 100644 new mode 100755 index af11eb7c..1b588a1d --- a/plugins/draw_card/update_game_info.py +++ b/plugins/draw_card/update_game_info.py @@ -1,13 +1,11 @@ -#coding:utf-8 -import aiohttp from .config import DRAW_PATH from asyncio.exceptions import TimeoutError from bs4 import BeautifulSoup -import asyncio from .util import download_img from urllib.parse import unquote from services.log import logger from .util import remove_prohibited_str +from utils.http_utils import AsyncHttpx import bs4 import re try: @@ -26,40 +24,39 @@ async def update_info(url: str, game_name: str, info_list: list = None) -> 'dict except (ValueError, FileNotFoundError): data = {} try: - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(url, timeout=7) as response: - soup = BeautifulSoup(await response.text(), 'lxml') - _tbody = get_tbody(soup, game_name, url) - trs = _tbody.find_all('tr') - att_dict, start_index, index = init_attr(game_name) - if game_name == 'guardian': - start_index = 1 - if game_name == 'azur': - start_index = 0 - for th in trs[0].find_all('th')[start_index:]: - text = th.text - if text[-1] == '\n': - text = text[:-1] - att_dict[text] = index - index += 1 - for tr in trs[1:]: - member_dict = {} - tds = tr.find_all('td') - if not info_list: - info_list = att_dict.keys() - for key in info_list: - key, attr = parse_key(key, game_name) - td = tds[att_dict[key]] - last_tag = unquote(_find_last_tag(td, attr, game_name), 'utf-8') - member_dict[key] = last_tag - member_dict = intermediate_check(member_dict, key, game_name, td) - avatar_img = await _modify_avatar_url(session, game_name, member_dict["名称"]) - member_dict['头像'] = avatar_img if avatar_img else member_dict['头像'] - member_dict, name = replace_update_name(member_dict, game_name) - await download_img(member_dict['头像'], game_name, name) - data[name] = member_dict - logger.info(f'{name} is update...') - data = await _last_check(data, game_name, session) + text = (await AsyncHttpx.get(url)).text + soup = BeautifulSoup(text, 'lxml') + _tbody = get_tbody(soup, game_name, url) + trs = _tbody.find_all('tr') + att_dict, start_index, index = init_attr(game_name) + if game_name == 'guardian': + start_index = 1 + if game_name == 'azur': + start_index = 0 + for th in trs[0].find_all('th')[start_index:]: + text = th.text + if text[-1] == '\n': + text = text[:-1] + att_dict[text] = index + index += 1 + for tr in trs[1:]: + member_dict = {} + tds = tr.find_all('td') + if not info_list: + info_list = att_dict.keys() + for key in info_list: + key, attr = parse_key(key, game_name) + td = tds[att_dict[key]] + last_tag = unquote(_find_last_tag(td, attr, game_name), 'utf-8') + member_dict[key] = last_tag + member_dict = intermediate_check(member_dict, key, game_name, td) + avatar_img = await _modify_avatar_url(game_name, member_dict["名称"]) + member_dict['头像'] = avatar_img if avatar_img else member_dict['头像'] + member_dict, name = replace_update_name(member_dict, game_name) + await download_img(member_dict['头像'], game_name, name) + data[name] = member_dict + logger.info(f'{name} is update...') + data = await _last_check(data, game_name) except TimeoutError: logger.warning(f'更新 {game_name} 超时...') return {}, 999 @@ -97,7 +94,7 @@ def _find_last_tag(element: bs4.element.Tag, attr: str, game_name: str) -> str: # 获取大图(小图快爬) -async def _modify_avatar_url(session: aiohttp.ClientSession, game_name: str, char_name: str): +async def _modify_avatar_url(game_name: str, char_name: str): # if game_name == 'prts': # async with session.get(f'https://wiki.biligame.com/arknights/{char_name}', timeout=7) as res: # soup = BeautifulSoup(await res.text(), 'lxml') @@ -109,10 +106,10 @@ async def _modify_avatar_url(session: aiohttp.ClientSession, game_name: str, cha if game_name == 'genshin': return None if game_name == 'pretty_card': - async with session.get(f'https://wiki.biligame.com/umamusume/{char_name}', timeout=7) as res: - soup = BeautifulSoup(await res.text(), 'lxml') - img_url = soup.find('div', {'class': 'support_card-left'}).find('div').find('img').get('src') - return img_url + text = (await AsyncHttpx.get(f'https://wiki.biligame.com/umamusume/{char_name}')).text + soup = BeautifulSoup(text, 'lxml') + img_url = soup.find('div', {'class': 'support_card-left'}).find('div').find('img').get('src') + return img_url if game_name == 'guardian': # 未上传图片太多,换成像素图 # async with session.get(f'https://wiki.biligame.com/gt/{char_name}', timeout=7) as res: @@ -130,7 +127,7 @@ async def _modify_avatar_url(session: aiohttp.ClientSession, game_name: str, cha # 数据最后处理(是否需要额外数据或处理数据) -async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession): +async def _last_check(data: dict, game_name: str): # if game_name == 'prts': # url = 'https://wiki.biligame.com/arknights/' # tasks = [] @@ -142,23 +139,23 @@ async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession # data[key]['获取途径'] = x[key]['获取途径'] if game_name == 'genshin': for key in data.keys(): - async with session.get(f'https://wiki.biligame.com/ys/{key}', timeout=7) as res: - soup = BeautifulSoup(await res.text(), 'lxml') - _trs = '' - for table in soup.find_all('table', {'class': 'wikitable'}): - if str(table).find('常驻/限定') != -1: - _trs = table.find('tbody').find_all('tr') - break - for tr in _trs: - data[key]['常驻/限定'] = '未知' - if str(tr).find('限定UP') != -1: - data[key]['常驻/限定'] = '限定UP' - logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}') - break - elif str(tr).find('常驻UP') != -1: - data[key]['常驻/限定'] = '常驻UP' - logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}') - break + text = (await AsyncHttpx.get(f'https://wiki.biligame.com/ys/{key}')).text + soup = BeautifulSoup(text, 'lxml') + _trs = '' + for table in soup.find_all('table', {'class': 'wikitable'}): + if str(table).find('常驻/限定') != -1: + _trs = table.find('tbody').find_all('tr') + break + for tr in _trs: + data[key]['常驻/限定'] = '未知' + if str(tr).find('限定UP') != -1: + data[key]['常驻/限定'] = '限定UP' + logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}') + break + elif str(tr).find('常驻UP') != -1: + data[key]['常驻/限定'] = '常驻UP' + logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}') + break if game_name == 'pretty': for keys in data.keys(): for key in data[keys].keys(): diff --git a/plugins/draw_card/update_game_requests_info.py b/plugins/draw_card/update_game_requests_info.py old mode 100644 new mode 100755 index 68af67dc..30032edb --- a/plugins/draw_card/update_game_requests_info.py +++ b/plugins/draw_card/update_game_requests_info.py @@ -1,58 +1,59 @@ -import aiohttp from .config import DRAW_PATH, SEMAPHORE from asyncio.exceptions import TimeoutError from .util import download_img from bs4 import BeautifulSoup from .util import remove_prohibited_str +from utils.http_utils import AsyncHttpx from services.log import logger import asyncio + try: import ujson as json except ModuleNotFoundError: import json -headers = {'User-Agent': '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'} +headers = { + "User-Agent": '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"' +} async def update_requests_info(game_name: str): try: - with open(DRAW_PATH + f'{game_name}.json', 'r', encoding='utf8') as f: + with open(DRAW_PATH + f"{game_name}.json", "r", encoding="utf8") as f: data = json.load(f) except (ValueError, FileNotFoundError): data = {} try: - async with aiohttp.ClientSession(headers=headers) as session: - if game_name in ['fgo', 'fgo_card']: - if game_name == 'fgo': - url = 'http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn=' - else: - url = 'http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn=' - for i in range(9999): - async with session.get(f'{url}{i}', timeout=7) as response: - fgo_data = json.loads(await response.text()) - if int(fgo_data['nums']) == 0: - break - for x in fgo_data['data']: - x['name'] = remove_prohibited_str(x['name']) - key = x['name'] - data = add_to_data(data, x, game_name) - await download_img(data[key]['头像'], game_name, key) - logger.info(f'{key} is update...') - if game_name == 'onmyoji': - url = 'https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74=' - async with session.get(f'{url}', timeout=7) as response: - onmyoji_data = await response.json() - for x in onmyoji_data: - x['name'] = remove_prohibited_str(x['name']) - key = x['name'] - data = add_to_data(data, x, game_name) - logger.info(f'{key} is update...') - data = await _last_check(data, game_name, session) + if game_name in ["fgo", "fgo_card"]: + if game_name == "fgo": + url = "http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn=" + else: + url = "http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn=" + for i in range(9999): + text = (await AsyncHttpx.get(f"{url}{i}")).text + fgo_data = json.loads(text) + if int(fgo_data["nums"]) == 0: + break + for x in fgo_data["data"]: + x["name"] = remove_prohibited_str(x["name"]) + key = x["name"] + data = add_to_data(data, x, game_name) + await download_img(data[key]["头像"], game_name, key) + logger.info(f"{key} is update...") + if game_name == "onmyoji": + url = "https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74=" + onmyoji_data = (await AsyncHttpx.get(f"{url}")).json() + for x in onmyoji_data: + x["name"] = remove_prohibited_str(x["name"]) + key = x["name"] + data = add_to_data(data, x, game_name) + logger.info(f"{key} is update...") + data = await _last_check(data, game_name) except TimeoutError: - logger.warning(f'更新 {game_name} 超时...') + logger.warning(f"更新 {game_name} 超时...") return {}, 999 - with open(DRAW_PATH + f'{game_name}.json', 'w', encoding='utf8') as wf: + with open(DRAW_PATH + f"{game_name}.json", "w", encoding="utf8") as wf: json.dump(data, wf, ensure_ascii=False, indent=4) return data, 200 @@ -60,91 +61,95 @@ async def update_requests_info(game_name: str): # 添加到字典 def add_to_data(data: dict, x: dict, game_name: str) -> dict: member_dict = {} - if game_name == 'fgo': + if game_name == "fgo": member_dict = { - 'id': x['id'], - 'card_id': x['charid'], - '头像': x['icon'], - '名称': x['name'], - '职阶': x['classes'], - '星级': x['star'], - 'hp': x['lvmax4hp'], - 'atk': x['lvmax4atk'], - 'card_quick': x['cardquick'], - 'card_arts': x['cardarts'], - 'card_buster': x['cardbuster'], - '宝具': x['tprop'], + "id": x["id"], + "card_id": x["charid"], + "头像": x["icon"], + "名称": x["name"], + "职阶": x["classes"], + "星级": x["star"], + "hp": x["lvmax4hp"], + "atk": x["lvmax4atk"], + "card_quick": x["cardquick"], + "card_arts": x["cardarts"], + "card_buster": x["cardbuster"], + "宝具": x["tprop"], } - if game_name == 'fgo_card': + if game_name == "fgo_card": member_dict = { - 'id': x['id'], - 'card_id': x['equipid'], - '头像': x['icon'], - '名称': x['name'], - '星级': x['star'], - 'hp': x['lvmax_hp'], - 'atk': x['lvmax_atk'], - 'skill_e': x['skill_e'].split('
')[: -1], + "id": x["id"], + "card_id": x["equipid"], + "头像": x["icon"], + "名称": x["name"], + "星级": x["star"], + "hp": x["lvmax_hp"], + "atk": x["lvmax_atk"], + "skill_e": x["skill_e"].split("
")[:-1], } - if game_name == 'onmyoji': + if game_name == "onmyoji": member_dict = { - 'id': x['id'], - '名称': x['name'], - '星级': x['level'], + "id": x["id"], + "名称": x["name"], + "星级": x["level"], } - data[member_dict['名称']] = member_dict + data[member_dict["名称"]] = member_dict return data # 获取额外数据 -async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession) -> dict: - if game_name == 'fgo': - url = 'http://fgo.vgtime.com/servant/' +async def _last_check(data: dict, game_name: str) -> dict: + if game_name == "fgo": + url = "http://fgo.vgtime.com/servant/" tasks = [] semaphore = asyncio.Semaphore(SEMAPHORE) for key in data.keys(): - tasks.append(asyncio.ensure_future(_async_update_fgo_extra_info(url, key, data[key]['id'], session, semaphore))) + tasks.append( + asyncio.ensure_future( + _async_update_fgo_extra_info(url, key, data[key]["id"], semaphore) + ) + ) asyResult = await asyncio.gather(*tasks) for x in asyResult: for key in x.keys(): - data[key]['入手方式'] = x[key]['入手方式'] - if game_name == 'onmyoji': - url = 'https://yys.163.com/shishen/{}.html' + data[key]["入手方式"] = x[key]["入手方式"] + if game_name == "onmyoji": + url = "https://yys.163.com/shishen/{}.html" for key in data.keys(): - async with session.get(f'{url.format(data[key]["id"])}', timeout=7) as response: - soup = BeautifulSoup(await response.text(), 'lxml') - data[key]['头像'] = "https:" + soup.find('div', {'class': 'pic_wrap'}).find('img')['src'] - await download_img(data[key]['头像'], game_name, key) + text = (await AsyncHttpx.get(f'{url.format(data[key]["id"])}')).text + soup = BeautifulSoup(text, "lxml") + data[key]["头像"] = ( + "https:" + soup.find("div", {"class": "pic_wrap"}).find("img")["src"] + ) + await download_img(data[key]["头像"], game_name, key) return data -async def _async_update_fgo_extra_info(url: str, key: str, _id: str, session: aiohttp.ClientSession, semaphore): +async def _async_update_fgo_extra_info(url: str, key: str, _id: str, semaphore): # 防止访问超时 async with semaphore: for i in range(10): try: - async with session.get(f'{url}{_id}', timeout=7) as response: - soup = BeautifulSoup(await response.text(), 'lxml') - obtain = soup.find('table', {'class': 'uk-table uk-codex-table'}).find_all('td')[-1].text - if obtain.find('限时活动免费获取 活动结束后无法获得') != -1: - obtain = ['活动获取'] - elif obtain.find('非限时UP无法获得') != -1: - obtain = ['限时召唤'] + text = (await AsyncHttpx.get(f"{url}{_id}")).text + soup = BeautifulSoup(text, "lxml") + obtain = ( + soup.find("table", {"class": "uk-table uk-codex-table"}) + .find_all("td")[-1] + .text + ) + if obtain.find("限时活动免费获取 活动结束后无法获得") != -1: + obtain = ["活动获取"] + elif obtain.find("非限时UP无法获得") != -1: + obtain = ["限时召唤"] + else: + if obtain.find("&") != -1: + obtain = obtain.strip().split("&") else: - if obtain.find('&') != -1: - obtain = obtain.strip().split('&') - else: - obtain = obtain.strip().split(' ') - logger.info(f'Fgo获取额外信息 {key}....{obtain}') - x = {key: {}} - x[key]['入手方式'] = obtain - return x + obtain = obtain.strip().split(" ") + logger.info(f"Fgo获取额外信息 {key}....{obtain}") + x = {key: {}} + x[key]["入手方式"] = obtain + return x except TimeoutError: - logger.warning(f'访问{url}{_id} 第 {i}次 超时...已再次访问') + logger.warning(f"访问{url}{_id} 第 {i}次 超时...已再次访问") return {} - - - - - - diff --git a/plugins/draw_card/update_game_simple_info.py b/plugins/draw_card/update_game_simple_info.py old mode 100644 new mode 100755 index c708e6f5..df684166 --- a/plugins/draw_card/update_game_simple_info.py +++ b/plugins/draw_card/update_game_simple_info.py @@ -1,4 +1,3 @@ -import aiohttp from .config import DRAW_PATH, SEMAPHORE from asyncio.exceptions import TimeoutError from bs4 import BeautifulSoup @@ -6,6 +5,7 @@ from .util import download_img from .util import remove_prohibited_str from urllib.parse import unquote from services.log import logger +from utils.http_utils import AsyncHttpx import bs4 import asyncio @@ -24,19 +24,21 @@ async def update_simple_info(url: str, game_name: str) -> 'dict, int': except (ValueError, FileNotFoundError): data = {} try: - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(url, timeout=7) as response: - soup = BeautifulSoup(await response.text(), 'lxml') - divs = get_char_divs(soup, game_name) - for div in divs: - type_lst = get_type_lst(div, game_name) - index = 0 - for char_lst in type_lst: - contents = get_char_lst_contents(char_lst, game_name) - for char in contents: - data = await retrieve_char_data(char, game_name, data, index) - index += 1 - data = await _last_check(data, game_name, session) + text = (await AsyncHttpx.get(url)).text + soup = BeautifulSoup(text, 'lxml') + divs = get_char_divs(soup, game_name) + for div in divs: + type_lst = get_type_lst(div, game_name) + index = 0 + for char_lst in type_lst: + try: + contents = get_char_lst_contents(char_lst, game_name) + except AttributeError: + continue + for char in contents[1:]: + data = await retrieve_char_data(char, game_name, data, index) + index += 1 + data = await _last_check(data, game_name) except TimeoutError: logger.warning(f'更新 {game_name} 超时...') return {}, 999 @@ -71,7 +73,7 @@ def get_char_lst_contents(char_lst: bs4.element.Tag, game_name: str): # 额外数据 -async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession) -> dict: +async def _last_check(data: dict, game_name: str) -> dict: if game_name == 'azur': idx = 1 for url in [ @@ -91,7 +93,7 @@ async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession tasks = [] semaphore = asyncio.Semaphore(SEMAPHORE) for key in data.keys(): - tasks.append(asyncio.ensure_future(_async_update_azur_extra_info(key, session, semaphore))) + tasks.append(asyncio.ensure_future(_async_update_azur_extra_info(key, semaphore))) asyResult = await asyncio.gather(*tasks) for x in asyResult: for key in x.keys(): @@ -125,10 +127,11 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict, '名称': remove_prohibited_str(char.find('a')['title']), '星级': 3 - index} if game_name == 'azur': - char = char.find('td').find('div') + print(char) + char = char.find('div').find('div').find('div').find('div') avatar_img = char.find('a').find('img') try: - member_dict['名称'] = remove_prohibited_str(str(avatar_img['alt'])[: str(avatar_img['alt']).find('头像')]) + member_dict['名称'] = remove_prohibited_str(char.find('a')['title']) except TypeError: member_dict['名称'] = char.find('a')['title'][:-4] try: @@ -138,7 +141,7 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict, except TypeError: member_dict['头像'] = "img link not find..." logger.warning(f'{member_dict["名称"]} 图片缺失....') - star = char.find('div').find('img')['alt'] + star = char.find('img')['alt'] if star == '舰娘头像外框普通.png': star = 1 elif star == '舰娘头像外框稀有.png': @@ -161,28 +164,28 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict, return data -async def _async_update_azur_extra_info(key: str, session: aiohttp.ClientSession, semaphore): +async def _async_update_azur_extra_info(key: str, semaphore): if key[-1] == '改': return {key: {'获取途径': ['无法建造']}} async with semaphore: for i in range(20): try: - async with session.get(f'https://wiki.biligame.com/blhx/{key}', timeout=7) as res: - soup = BeautifulSoup(await res.text(), 'lxml') - try: - construction_time = str(soup.find('table', {'class': 'wikitable sv-general'}).find('tbody')) - x = {key: {'获取途径': []}} - if construction_time.find('无法建造') != -1: - x[key]['获取途径'].append('无法建造') - elif construction_time.find('活动已关闭') != -1: - x[key]['获取途径'].append('活动限定') - else: - x[key]['获取途径'].append('可以建造') - logger.info(f'碧蓝航线获取额外信息 {key}...{x[key]["获取途径"]}') - except AttributeError: - x = {key: {'获取途径': []}} - logger.warning(f'碧蓝航线获取额外信息错误 {key}...{[]}') - return x + text = (await AsyncHttpx.get(f'https://wiki.biligame.com/blhx/{key}')).text + soup = BeautifulSoup(text, 'lxml') + try: + construction_time = str(soup.find('table', {'class': 'wikitable sv-general'}).find('tbody')) + x = {key: {'获取途径': []}} + if construction_time.find('无法建造') != -1: + x[key]['获取途径'].append('无法建造') + elif construction_time.find('活动已关闭') != -1: + x[key]['获取途径'].append('活动限定') + else: + x[key]['获取途径'].append('可以建造') + logger.info(f'碧蓝航线获取额外信息 {key}...{x[key]["获取途径"]}') + except AttributeError: + x = {key: {'获取途径': []}} + logger.warning(f'碧蓝航线获取额外信息错误 {key}...{[]}') + return x except TimeoutError: logger.warning(f'访问 https://wiki.biligame.com/blhx/{key} 第 {i}次 超时...已再次访问') return {} diff --git a/plugins/draw_card/util.py b/plugins/draw_card/util.py old mode 100644 new mode 100755 index 07c267ce..0f8bf8cf --- a/plugins/draw_card/util.py +++ b/plugins/draw_card/util.py @@ -1,13 +1,9 @@ - -import aiohttp -import aiofiles -from asyncio.exceptions import TimeoutError -from aiohttp.client_exceptions import InvalidURL from nonebot.adapters.cqhttp import MessageSegment from typing import List, Union, Set from pathlib import Path from .config import path_dict from configs.path_config import IMAGE_PATH +from utils.http_utils import AsyncHttpx import nonebot import pypinyin from utils.image_utils import CreateImg @@ -54,21 +50,12 @@ async def download_img(url: str, path: str, name: str) -> bool: if not file.exists(): file.parent.mkdir(exist_ok=True, parents=True) try: - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(url, timeout=7) as response: - async with aiofiles.open(IMAGE_PATH + f'/draw_card/{path}/{codename}.png', 'wb') as f: - await f.write(await response.read()) - logger.info(f'下载 {path_dict[path]} 图片成功,名称:{name},url:{url}') - return True - except TimeoutError: - logger.warning(f'下载 {path_dict[path]} 图片超时,名称:{name},url:{url}') - return False - except InvalidURL: - logger.warning(f'下载 {path_dict[path]} 链接错误,名称:{name},url:{url}') - return False - else: - # logger.info(f'{path_dict[path]} 图片 {name} 已存在') - return False + if await AsyncHttpx.download_file(url, IMAGE_PATH + f'/draw_card/{path}/{codename}.png'): + logger.info(f'下载 {path_dict[path]} 图片成功,名称:{name},url:{url}') + return True + except Exception as e: + logger.warning(f'下载 {path_dict[path]} 链接错误 {type(e)}:{e},名称:{name},url:{url}') + return False @driver.on_startup diff --git a/plugins/epic/__init__.py b/plugins/epic/__init__.py old mode 100644 new mode 100755 index 9065a091..a2fa2c2a --- a/plugins/epic/__init__.py +++ b/plugins/epic/__init__.py @@ -15,7 +15,7 @@ usage: """.strip() __plugin_des__ = "可以不玩,不能没有,每日白嫖" __plugin_cmd__ = ["epic"] -__plugin_version__ = 0.2 +__plugin_version__ = 0.1 __plugin_author__ = "AkashiCoin" __plugin_settings__ = { "level": 5, diff --git a/plugins/epic/data_source.py b/plugins/epic/data_source.py old mode 100644 new mode 100755 index ed78181a..074a01c8 --- a/plugins/epic/data_source.py +++ b/plugins/epic/data_source.py @@ -32,11 +32,11 @@ async def get_epic_game(): "withPromotions": True, }, } - async with AsyncClient(proxies={"all://": None}) as client: + async with AsyncClient(headers=headers) as client: try: - res = await client.post(epic_url, headers=headers, json=data, timeout=10.0) - resJson = res.json() - games = resJson["data"]["Catalog"]["searchStore"]["elements"] + res = await client.post(epic_url, json=data, timeout=10.0) + res_json = res.json() + games = res_json["data"]["Catalog"]["searchStore"]["elements"] return games except Exception as e: logger.error(str(e)) @@ -53,60 +53,89 @@ async def get_epic_free(bot: Bot, event: Event): else: msg_list = [] for game in games: + game_name = game["title"] + game_corp = game["seller"]["name"] + game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"] + # 赋初值以避免 local variable referenced before assignment + game_dev, game_pub, game_thumbnail = (None, None, None) try: - msg = "" - game_name = game["title"] - game_corp = game["seller"]["name"] - game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"] game_promotions = game["promotions"]["promotionalOffers"] upcoming_promotions = game["promotions"]["upcomingPromotionalOffers"] if not game_promotions and upcoming_promotions: - continue - else: - for image in game["keyImages"]: - game_thumbnail = ( - image["url"] if image["type"] == "Thumbnail" else None - ) - for pair in game["customAttributes"]: - game_dev = ( - pair["value"] - if pair["key"] == "developerName" - else game_corp - ) - game_pub = ( - pair["value"] - if pair["key"] == "publisherName" - else game_corp - ) - game_desp = game["description"] - end_date = "" - if len(game["promotions"]["promotionalOffers"]) != 0: - end_date_iso = game["promotions"]["promotionalOffers"][0][ - "promotionalOffers" - ][0]["endDate"][:-1] - end_date = datetime.fromisoformat(end_date_iso).strftime( - "%b.%d %H:%M" - ) - # API 返回不包含游戏商店 URL,此处自行拼接,可能出现少数游戏 404 请反馈 - game_url = f"https://www.epicgames.com/store/zh-CN/p/{game['productSlug'].replace('/home', '')}" - msg = ( - f"[CQ:image,file={game_thumbnail}]\n\n" - if game_thumbnail - else "" + # 促销暂未上线,但即将上线 + promotion_data = upcoming_promotions[0]["promotionalOffers"][0] + start_date_iso, end_date_iso = ( + promotion_data["startDate"][:-1], + promotion_data["endDate"][:-1], ) - msg += f"FREE now :: {game_name} ({game_price})\n\n{game_desp}\n\n" - msg += ( - f"游戏由 {game_pub} 发售," - if game_dev == game_pub - else f"游戏由 {game_dev} 开发、{game_pub} 出版," + # 删除字符串中最后一个 "Z" 使 Python datetime 可处理此时间 + start_date = datetime.fromisoformat(start_date_iso).strftime( + "%b.%d %H:%M" + ) + end_date = datetime.fromisoformat(end_date_iso).strftime( + "%b.%d %H:%M" ) - msg += f"将在 UTC 时间 {end_date} 结束免费游玩,戳链接领取吧~\n{game_url}" - _message = msg if isinstance(event, GroupMessageEvent): + _message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format( + game_corp, game_name, game_price, start_date, end_date + ) data = { "type": "node", "data": { - "name": f"{NICKNAME}", + "name": f"这里是{NICKNAME}酱", + "uin": f"{bot.self_id}", + "content": _message, + }, + } + msg_list.append(data) + else: + msg = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format( + game_corp, game_name, game_price, start_date, end_date + ) + msg_list.append(msg) + else: + for image in game["keyImages"]: + if image["type"] == "Thumbnail": + game_thumbnail = image["url"] + for pair in game["customAttributes"]: + if pair["key"] == "developerName": + game_dev = pair["value"] + if pair["key"] == "publisherName": + game_pub = pair["value"] + # 如 game['customAttributes'] 未找到则均使用 game_corp 值 + game_dev = game_dev if game_dev is not None else game_corp + game_pub = game_pub if game_pub is not None else game_corp + game_desp = game["description"] + end_date_iso = game["promotions"]["promotionalOffers"][0][ + "promotionalOffers" + ][0]["endDate"][:-1] + end_date = datetime.fromisoformat(end_date_iso).strftime( + "%b.%d %H:%M" + ) + # API 返回不包含游戏商店 URL,此处自行拼接,可能出现少数游戏 404 请反馈 + game_url_part = ( + (game["productSlug"].replace("/home", "")) + if ("/home" in game["productSlug"]) + else game["productSlug"] + ) + game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format( + game_url_part + ) + if isinstance(event, GroupMessageEvent): + _message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format( + game_thumbnail, + game_name, + game_price, + game_desp, + game_dev, + game_pub, + end_date, + game_url, + ) + data = { + "type": "node", + "data": { + "name": f"这里是{NICKNAME}酱", "uin": f"{bot.self_id}", "content": _message, }, diff --git a/plugins/fake_msg.py b/plugins/fake_msg.py old mode 100644 new mode 100755 diff --git a/plugins/fudu.py b/plugins/fudu.py old mode 100644 new mode 100755 index 56423c4c..8e483e9e --- a/plugins/fudu.py +++ b/plugins/fudu.py @@ -4,12 +4,11 @@ from utils.image_utils import get_img_hash import random from utils.message_builder import image from nonebot import on_message -from utils.utils import get_message_text, get_message_imgs, get_local_proxy +from utils.utils import get_message_text, get_message_imgs from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -import aiohttp -import aiofiles from configs.config import Config +from utils.http_utils import AsyncHttpx from utils.manager import group_manager from services.log import logger @@ -121,14 +120,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def get_fudu_img_hash(url, group_id): try: - async with aiohttp.ClientSession() as session: - async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: - async with aiofiles.open( - IMAGE_PATH + f"temp/compare_{group_id}_img.jpg", "wb" - ) as f: - await f.write(await response.read()) - img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg") - return str(img_hash) + if await AsyncHttpx.download_file( + url, IMAGE_PATH + f"temp/compare_{group_id}_img.jpg" + ): + img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg") + return str(img_hash) + else: + logger.warning(f"复读下载图片失败...") except Exception as e: logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}") return "" diff --git a/plugins/genshin/__init__.py b/plugins/genshin/__init__.py old mode 100644 new mode 100755 index d16bafc6..2d21e611 --- a/plugins/genshin/__init__.py +++ b/plugins/genshin/__init__.py @@ -1,3 +1,4 @@ import nonebot nonebot.load_plugins("plugins/genshin") + diff --git a/plugins/genshin/almanac/__init__.py b/plugins/genshin/almanac/__init__.py old mode 100644 new mode 100755 index 94785243..a8c49454 --- a/plugins/genshin/almanac/__init__.py +++ b/plugins/genshin/almanac/__init__.py @@ -38,12 +38,15 @@ ALC_PATH.mkdir(parents=True, exist_ok=True) @almanac.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): alc_img = await get_alc_image(ALC_PATH) - mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub" - await almanac.send(mes) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送查看原神黄历" - ) + if alc_img: + mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub" + await almanac.send(mes) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送查看原神黄历" + ) + else: + await almanac.send("黄历图片下载失败...") @scheduler.scheduled_job( @@ -58,7 +61,8 @@ async def _(): gl = await bot.get_group_list() gl = [g["group_id"] for g in gl] alc_img = await get_alc_image(ALC_PATH) - mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub" - for gid in gl: - if await group_manager.check_group_task_status(gid, "genshin_alc"): - await bot.send_group_msg(group_id=int(gid), message=mes) + if alc_img: + mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub" + for gid in gl: + if await group_manager.check_group_task_status(gid, "genshin_alc"): + await bot.send_group_msg(group_id=int(gid), message=mes) diff --git a/plugins/genshin/almanac/data_source.py b/plugins/genshin/almanac/data_source.py old mode 100644 new mode 100755 index c9cec6f4..85dc2e58 --- a/plugins/genshin/almanac/data_source.py +++ b/plugins/genshin/almanac/data_source.py @@ -1,43 +1,26 @@ -from utils.browser import get_browser -from utils.message_builder import image -from datetime import datetime -from services.log import logger -from pathlib import Path -import os - -url = "https://genshin.pub" - - -async def get_alc_image(path: Path): - date = datetime.now().date() - for file in os.listdir(path): - if f'{date}.png' != file: - file = path / file - file.unlink() - if f'{date}.png' in os.listdir(path): - return image(f'{date}.png', 'genshin/alc') - page = None - try: - browser = await get_browser() - page = await browser.new_page() - await page.goto(url, wait_until="networkidle", timeout=10000) - await page.set_viewport_size({"width": 2560, "height": 1080}) - card = await page.query_selector('.GSAlmanacs_gs_almanacs__3qT_A') - await card.screenshot(path=path / f'{date}.png', timeout=100000) - except Exception as e: - logger.error(f'获取原神黄历发生错误..{type(e)}: {e}') - finally: - if page: - await page.close() - return image(f'{date}.png', 'genshin/alc') - - - - - - - - - - - +from utils.message_builder import image +from datetime import datetime +from pathlib import Path +from utils.http_utils import AsyncPlaywright +from nonebot.adapters.cqhttp import MessageSegment +from typing import Optional +import os + +url = "https://genshin.pub" + + +async def get_alc_image(path: Path) -> Optional[MessageSegment]: + """ + 截取黄历 + :param path: 存储路径 + """ + date = datetime.now().date() + for file in os.listdir(path): + if f"{date}.png" != file: + file = path / file + file.unlink() + if f"{date}.png" in os.listdir(path): + return image(f"{date}.png", "genshin/alc") + return await AsyncPlaywright.screenshot( + url, path / f"{date}.png", ".GSAlmanacs_gs_almanacs__3qT_A" + ) diff --git a/plugins/genshin/material_remind/__init__.py b/plugins/genshin/material_remind/__init__.py old mode 100644 new mode 100755 index c54bef37..54925dde --- a/plugins/genshin/material_remind/__init__.py +++ b/plugins/genshin/material_remind/__init__.py @@ -1,6 +1,6 @@ from nonebot import on_command, Driver from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent from utils.message_builder import image from utils.image_utils import CreateImg from utils.browser import get_browser @@ -59,7 +59,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): ) ) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 发送查看今日素材" ) diff --git a/plugins/genshin/qiu_qiu_translation/__init__.py b/plugins/genshin/qiu_qiu_translation/__init__.py deleted file mode 100644 index 512639a6..00000000 --- a/plugins/genshin/qiu_qiu_translation/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -from .qiu_translation import qiu_qiu_word_translation, qiu_qiu_phrase_translation -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.typing import T_State -from nonebot import on_command -from utils.utils import get_message_text -from services.log import logger - -__zx_plugin_name__ = "丘丘语翻译" -__plugin_usage__ = """ -usage: - 异世界旅游小助手,仅支持丘丘语翻译至中文 - 指令: - 丘丘语翻译/丘丘一下 [文本] -""".strip() -__plugin_des__ = "其实我听得懂丘丘人讲话" -__plugin_cmd__ = ["丘丘语翻译/丘丘一下 [文本]"] -__plugin_type__ = ("原神相关",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["丘丘语翻译", "丘丘一下"], -} - -qiuqiu = on_command("丘丘语翻译", aliases={"丘丘一下", "丘丘翻译"}, priority=5, block=True) - -suffix = "\n※ 只能从丘丘语翻译为中文,不能反向翻译\n" "※ 注意空格,不要加入任何标点符号\n" "※ 翻译数据来源于 米游社论坛" - - -@qiuqiu.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - txt = get_message_text(event.json()).lower() - if txt == "": - return - mes = qiu_qiu_phrase_translation(txt) - if not mes: - mes = qiu_qiu_word_translation(txt) - mes += suffix - # print(mes) - await qiuqiu.send(mes, at_sender=True) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送丘丘翻译:" + txt - ) diff --git a/plugins/genshin/qiu_qiu_translation/qiu_qiu_dictionary.json b/plugins/genshin/qiu_qiu_translation/qiu_qiu_dictionary.json deleted file mode 100644 index 08d0f329..00000000 --- a/plugins/genshin/qiu_qiu_translation/qiu_qiu_dictionary.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "word": { - - "a": "啊", - - "beru": "做", - "biadam": "找死啊", - "biat": "暴揍", - "buka": "肚子", - - "celi": "元素", - - "dada": "厉害", - "dala": "什么", - "domu": "跳舞", - - "eleka": "此时此刻", - - "guru": "咕", - "gusha": "草", - - "ika": "敌人", - - "kundela": "活着", - "kuzi": "强大", - "kucha": "吃草", - - "la": "啦~", - "lata": "水元素", - - "mani": "给予", - "mi": "我", - "mimi": "我们", - "mita": "肉类", - "mosi": "吃", - "movo": "带来", - "muhe": "胜利", - - "ni": "风", - "nini": "风暴", - "nunu": "睡觉", - "nye": "不", - - "odomu": "朋友", - "olah": "你好", - - "plata": "盾牌", - "pupu": "噗噗", - - "sada": "唱歌", - "shato": "伴随", - "si": "什么", - - "tomo": "感激", - "todo": "放心", - "tiga": "矿石", - - "upa": "聚集", - "unu": "乌努", - - "valo": "谢谢你", - "vin": "酒", - - "ya": "人", - "yaya": "人们", - "ye": "家伙", - "yeye": "这些家伙", - "yo": "你", - "yoyo": "你们", - - "zido": "杀死" - - }, - "phrase": { - "beru si": "做什么", - "biadam": "可恶啊,找死啊,愤怒语气", - "biat": "打爆,暴揍;可恶的", - - "dada": " 极好的,厉害的", - "dala": "表示疑问,不明白的集合,翻译为哪个,什么", - - "guru-guru": "拟声词,形容肚子咕咕叫的声音,饥饿", - "guru guru": "拟声词,形容肚子咕咕叫的声音,饥饿", - "gusha": "植物类食物,不怎么好的,难过的", - - "kucha pupu": "拟声词,吃草时候的拟声词,咯吱噗噗,形容吃草吃得香,贬义", - - "mita": "肉类,好的,好东西,有时代丘丘人,丘丘人部落,好肉族", - "mosi mita": "吃肉,表示开心", - "mosi gusha": "吃草,表示难过", - "movo": "带来,搬运;可引申为收获,丰收", - "muhe": "胜利,战胜;成功的", - - "nini": "大量风元素,风暴,狂风", - "nye": "表示否定,没有了,不是", - - "pupu":"拟声词,噗噗,表示嘲讽", - - "sada": "唱歌,歌颂", - "shato": "乘着,伴随着", - - "upa": "凝聚,聚集,集合;聚落,部落", - "unu": "乌努,丘丘人所崇拜的神灵", - - "valo": "谢谢你,不客气/再见", - "vin": "酒的,喝酒有关的,酒桶盖子", - - "ya": "特指人类,一个人类", - "yaya": "人类的复数 ,大群人类", - "ye": "你这个家伙,蔑称;或用于地位高者对地位低者的称呼", - "yeye": "你们这些家伙,蔑称,ye的复数", - "yo": "你,友善称呼", - "yoyo": "你们,yo的复数,友善称呼ye", - - - - "nye mita da ye mosi zido": "再见,一路平安", - "vin plata dada": "你的酒桶盾牌真不错", - "kucha pupu gucha ye": "你咯吱噗噗的吃草的样子真的好搞笑", - "mani nini biaodomu": "愿风暴给予你死亡", - "celi dada mimi nunu": "赞美元素,我们睡觉吧", - "muhe ye": "你们是不可战胜的", - "ye dada": "你们可真棒", - "ye yika": "你们是敌人", - "nini zido": "愿风暴杀死你" - } -} \ No newline at end of file diff --git a/plugins/genshin/qiu_qiu_translation/qiu_translation.py b/plugins/genshin/qiu_qiu_translation/qiu_translation.py deleted file mode 100644 index 45c236f4..00000000 --- a/plugins/genshin/qiu_qiu_translation/qiu_translation.py +++ /dev/null @@ -1,73 +0,0 @@ -import json -import os - -FILE_PATH = os.path.dirname(__file__) - -QIU_QIU_WORD = {} -QIU_QIU_PHRASE = {} - -with open( - os.path.join(FILE_PATH, "qiu_qiu_dictionary.json"), "r", encoding="UTF-8" -) as f: - data = json.load(f) - QIU_QIU_WORD = data["word"] - QIU_QIU_PHRASE = data["phrase"] - - -def compare_words(word): - # 比对word库是否有匹配的单词,有的话返回翻译,没有返回原词 - if word in QIU_QIU_WORD: - return QIU_QIU_WORD[word] - - return word - - -def compare_phrase(phrase): - # 比对phrase库是否有匹配的单词,有的话返回翻译,没有的话匹配word库,都没有返回原词 - if phrase in QIU_QIU_PHRASE: - return QIU_QIU_PHRASE[phrase] - if phrase in QIU_QIU_WORD: - return QIU_QIU_WORD[phrase] - - return phrase - - -def qiu_qiu_word_translation(txt: str): - # 对语句按空格分隔替换单词翻译 - txt_list = txt.split(" ") - mes = "你查询的的丘丘语意思为:\n" - - for word in txt_list: - tra_word = compare_words(word) - - if tra_word == word: - # 如果是原词表示没有翻译,前后加空格接回语句里 - if not mes[-1] == " ": - mes += " " - mes += tra_word - mes += " " - else: - mes += tra_word - mes += "\n" - return mes - - -def qiu_qiu_phrase_translation(phrase): - # 语句翻译,先看phrase库是不是有匹配的语句 - # 没有的话把单词拆开返回单词的意思 - tra_phrase = compare_phrase(phrase) - if tra_phrase != phrase: - return f"\n翻译丘丘语意思为:\n【{tra_phrase}】\n" - - txt_list = phrase.split(" ") - mes = "没有查到这句丘丘语,以下是单词的翻译\n" - for word in txt_list: - if word == " ": - continue - tra_word = compare_phrase(word) - if tra_word == word: - mes += f"{word} : 没有这个词的翻译\n" - else: - mes += f"{word} : {tra_word}\n" - - return mes diff --git a/plugins/genshin/query_resource_points/__init__.py b/plugins/genshin/query_resource_points/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/genshin/query_resource_points/map.py b/plugins/genshin/query_resource_points/map.py old mode 100644 new mode 100755 index 6efcc1e0..449dbfc4 --- a/plugins/genshin/query_resource_points/map.py +++ b/plugins/genshin/query_resource_points/map.py @@ -1,266 +1,266 @@ -from pathlib import Path -from configs.path_config import IMAGE_PATH, TEXT_PATH -from utils.image_utils import CreateImg -from typing import Tuple, List -from math import sqrt, pow -import random - -try: - import ujson as json -except ModuleNotFoundError: - import json - -icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon" -map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png" -resource_label_file = Path(TEXT_PATH) / "genshin" / "resource_label_file.json" -resource_point_file = Path(TEXT_PATH) / "genshin" / "resource_point_file.json" - - -class Map: - """ - 原神资源生成类 - """ - - def __init__( - self, - resource_name: str, - center_point: Tuple[int, int], - deviation: Tuple[int, int] = (25, 51), - padding: int = 100, - planning_route: bool = False, - ratio: float = 1, - ): - """ - 参数: - :param resource_name: 资源名称 - :param center_point: 中心点 - :param deviation: 坐标误差 - :param padding: 截图外边距 - :param planning_route: 是否规划最佳线路 - :param ratio: 压缩比率 - """ - self.map = CreateImg(0, 0, background=map_path) - self.resource_name = resource_name - self.center_x = center_point[0] - self.center_y = center_point[1] - self.deviation = deviation - self.padding = int(padding * ratio) - self.planning_route = planning_route - self.ratio = ratio - - self.deviation = ( - int(self.deviation[0] * ratio), - int(self.deviation[1] * ratio), - ) - - data = json.load(open(resource_label_file, "r", encoding="utf8")) - # 资源 id - self.resource_id = [ - data[x]["id"] - for x in data - if x != "CENTER_POINT" and data[x]["name"] == resource_name - ][0] - # 传送锚点 id - self.teleport_anchor_id = [ - data[x]["id"] - for x in data - if x != "CENTER_POINT" and data[x]["name"] == "传送锚点" - ][0] - # 神像 id - self.teleport_god_id = [ - data[x]["id"] - for x in data - if x != "CENTER_POINT" and data[x]["name"] == "七天神像" - ][0] - # 资源坐标 - data = json.load(open(resource_point_file, "r", encoding="utf8")) - self.resource_point = [ - Resources( - int((self.center_x + data[x]["x_pos"]) * ratio), - int((self.center_y + data[x]["y_pos"]) * ratio), - ) - for x in data - if x != "CENTER_POINT" and data[x]["label_id"] == self.resource_id - ] - # 传送锚点坐标 - self.teleport_anchor_point = [ - Resources( - int((self.center_x + data[x]["x_pos"]) * ratio), - int((self.center_y + data[x]["y_pos"]) * ratio), - ) - for x in data - if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_anchor_id - ] - # 神像坐标 - self.teleport_god_point = [ - Resources( - int((self.center_x + data[x]["x_pos"]) * ratio), - int((self.center_y + data[x]["y_pos"]) * ratio), - ) - for x in data - if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_god_id - ] - - # 将地图上生成资源图标 - def generate_resource_icon_in_map(self) -> int: - x_list = [x.x for x in self.resource_point] - y_list = [x.y for x in self.resource_point] - min_width = min(x_list) - self.padding - max_width = max(x_list) + self.padding - min_height = min(y_list) - self.padding - max_height = max(y_list) + self.padding - self._generate_transfer_icon((min_width, min_height, max_width, max_height)) - for res in self.resource_point: - icon = self._get_icon_image(self.resource_id) - self.map.paste( - icon, (res.x - self.deviation[0], res.y - self.deviation[1]), True - ) - if self.planning_route: - self._generate_best_route() - self.map.crop((min_width, min_height, max_width, max_height)) - rand = random.randint(1, 10000) - self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png") - return rand - - # 资源数量 - def get_resource_count(self) -> int: - return len(self.resource_point) - - # 生成传送锚点和神像 - def _generate_transfer_icon(self, box: Tuple[int, int, int, int]): - min_width, min_height, max_width, max_height = box - for resources in [self.teleport_anchor_point, self.teleport_god_point]: - id_ = ( - self.teleport_anchor_id - if resources == self.teleport_anchor_point - else self.teleport_god_id - ) - for res in resources: - if min_width < res.x < max_width and min_height < res.y < max_height: - icon = self._get_icon_image(id_) - self.map.paste( - icon, - (res.x - self.deviation[0], res.y - self.deviation[1]), - True, - ) - - # 生成最优路线(说是最优其实就是直线最短) - def _generate_best_route(self): - line_points = [] - teleport_list = self.teleport_anchor_point + self.teleport_god_point - for teleport in teleport_list: - current_res, res_min_distance = teleport.get_resource_distance(self.resource_point) - current_teleport, teleport_min_distance = current_res.get_resource_distance(teleport_list) - if current_teleport == teleport: - self.map.line( - (current_teleport.x, current_teleport.y, current_res.x, current_res.y), (255, 0, 0), width=1 - ) - is_used_res_points = [] - for res in self.resource_point: - if res in is_used_res_points: - continue - current_teleport, teleport_min_distance = res.get_resource_distance(teleport_list) - current_res, res_min_distance = res.get_resource_distance(self.resource_point) - if teleport_min_distance < res_min_distance: - self.map.line( - (current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1 - ) - else: - is_used_res_points.append(current_res) - self.map.line( - (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1 - ) - res_cp = self.resource_point[:] - res_cp.remove(current_res) - # for _ in res_cp: - current_teleport_, teleport_min_distance = res.get_resource_distance(teleport_list) - current_res, res_min_distance = res.get_resource_distance(res_cp) - if teleport_min_distance < res_min_distance: - self.map.line( - (current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1 - ) - else: - self.map.line( - (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1 - ) - is_used_res_points.append(current_res) - is_used_res_points.append(res) - - # resources_route = [] - # # 先连上最近的资源路径 - # for res in self.resource_point: - # # 拿到最近的资源 - # current_res, _ = res.get_resource_distance( - # self.resource_point - # + self.teleport_anchor_point - # + self.teleport_god_point - # ) - # self.map.line( - # (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1 - # ) - # resources_route.append((current_res, res)) - # teleport_list = self.teleport_anchor_point + self.teleport_god_point - # for res1, res2 in resources_route: - # point_list = [x for x in resources_route if res1 in x or res2 in x] - # if not list(set(point_list).intersection(set(teleport_list))): - # if res1 not in teleport_list and res2 not in teleport_list: - # # while True: - # # tmp = [x for x in point_list] - # # break - # teleport1, distance1 = res1.get_resource_distance(teleport_list) - # teleport2, distance2 = res2.get_resource_distance(teleport_list) - # if distance1 > distance2: - # self.map.line( - # (teleport1.x, teleport1.y, res1.x, res1.y), - # (255, 0, 0), - # width=1, - # ) - # else: - # self.map.line( - # (teleport2.x, teleport2.y, res2.x, res2.y), - # (255, 0, 0), - # width=1, - # ) - - # self.map.line(xy, (255, 0, 0), width=3) - - # 获取资源图标 - def _get_icon_image(self, id_: int) -> "CreateImg": - icon = icon_path / f"{id_}.png" - if icon.exists(): - return CreateImg( - int(50 * self.ratio), int(50 * self.ratio), background=icon - ) - return CreateImg( - int(50 * self.ratio), - int(50 * self.ratio), - background=f"{icon_path}/box.png", - ) - - # def _get_shortest_path(self, res: 'Resources', res_2: 'Resources'): - - -# 资源类 -class Resources: - def __init__(self, x: int, y: int): - self.x = x - self.y = y - - def get_distance(self, x: int, y: int): - return int(sqrt(pow(abs(self.x - x), 2) + pow(abs(self.y - y), 2))) - - # 拿到资源在该列表中的最短路径 - def get_resource_distance(self, resources: List["Resources"]) -> "Resources, int": - current_res = None - min_distance = 999999 - for res in resources: - distance = self.get_distance(res.x, res.y) - if distance < min_distance and res != self: - current_res = res - min_distance = distance - return current_res, min_distance - - - - - +from pathlib import Path +from configs.path_config import IMAGE_PATH, TEXT_PATH +from utils.image_utils import CreateImg +from typing import Tuple, List +from math import sqrt, pow +import random + +try: + import ujson as json +except ModuleNotFoundError: + import json + +icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon" +map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png" +resource_label_file = Path(TEXT_PATH) / "genshin" / "resource_label_file.json" +resource_point_file = Path(TEXT_PATH) / "genshin" / "resource_point_file.json" + + +class Map: + """ + 原神资源生成类 + """ + + def __init__( + self, + resource_name: str, + center_point: Tuple[int, int], + deviation: Tuple[int, int] = (25, 51), + padding: int = 100, + planning_route: bool = False, + ratio: float = 1, + ): + """ + 参数: + :param resource_name: 资源名称 + :param center_point: 中心点 + :param deviation: 坐标误差 + :param padding: 截图外边距 + :param planning_route: 是否规划最佳线路 + :param ratio: 压缩比率 + """ + self.map = CreateImg(0, 0, background=map_path) + self.resource_name = resource_name + self.center_x = center_point[0] + self.center_y = center_point[1] + self.deviation = deviation + self.padding = int(padding * ratio) + self.planning_route = planning_route + self.ratio = ratio + + self.deviation = ( + int(self.deviation[0] * ratio), + int(self.deviation[1] * ratio), + ) + + data = json.load(open(resource_label_file, "r", encoding="utf8")) + # 资源 id + self.resource_id = [ + data[x]["id"] + for x in data + if x != "CENTER_POINT" and data[x]["name"] == resource_name + ][0] + # 传送锚点 id + self.teleport_anchor_id = [ + data[x]["id"] + for x in data + if x != "CENTER_POINT" and data[x]["name"] == "传送锚点" + ][0] + # 神像 id + self.teleport_god_id = [ + data[x]["id"] + for x in data + if x != "CENTER_POINT" and data[x]["name"] == "七天神像" + ][0] + # 资源坐标 + data = json.load(open(resource_point_file, "r", encoding="utf8")) + self.resource_point = [ + Resources( + int((self.center_x + data[x]["x_pos"]) * ratio), + int((self.center_y + data[x]["y_pos"]) * ratio), + ) + for x in data + if x != "CENTER_POINT" and data[x]["label_id"] == self.resource_id + ] + # 传送锚点坐标 + self.teleport_anchor_point = [ + Resources( + int((self.center_x + data[x]["x_pos"]) * ratio), + int((self.center_y + data[x]["y_pos"]) * ratio), + ) + for x in data + if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_anchor_id + ] + # 神像坐标 + self.teleport_god_point = [ + Resources( + int((self.center_x + data[x]["x_pos"]) * ratio), + int((self.center_y + data[x]["y_pos"]) * ratio), + ) + for x in data + if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_god_id + ] + + # 将地图上生成资源图标 + def generate_resource_icon_in_map(self) -> int: + x_list = [x.x for x in self.resource_point] + y_list = [x.y for x in self.resource_point] + min_width = min(x_list) - self.padding + max_width = max(x_list) + self.padding + min_height = min(y_list) - self.padding + max_height = max(y_list) + self.padding + self._generate_transfer_icon((min_width, min_height, max_width, max_height)) + for res in self.resource_point: + icon = self._get_icon_image(self.resource_id) + self.map.paste( + icon, (res.x - self.deviation[0], res.y - self.deviation[1]), True + ) + if self.planning_route: + self._generate_best_route() + self.map.crop((min_width, min_height, max_width, max_height)) + rand = random.randint(1, 10000) + self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png") + return rand + + # 资源数量 + def get_resource_count(self) -> int: + return len(self.resource_point) + + # 生成传送锚点和神像 + def _generate_transfer_icon(self, box: Tuple[int, int, int, int]): + min_width, min_height, max_width, max_height = box + for resources in [self.teleport_anchor_point, self.teleport_god_point]: + id_ = ( + self.teleport_anchor_id + if resources == self.teleport_anchor_point + else self.teleport_god_id + ) + for res in resources: + if min_width < res.x < max_width and min_height < res.y < max_height: + icon = self._get_icon_image(id_) + self.map.paste( + icon, + (res.x - self.deviation[0], res.y - self.deviation[1]), + True, + ) + + # 生成最优路线(说是最优其实就是直线最短) + def _generate_best_route(self): + line_points = [] + teleport_list = self.teleport_anchor_point + self.teleport_god_point + for teleport in teleport_list: + current_res, res_min_distance = teleport.get_resource_distance(self.resource_point) + current_teleport, teleport_min_distance = current_res.get_resource_distance(teleport_list) + if current_teleport == teleport: + self.map.line( + (current_teleport.x, current_teleport.y, current_res.x, current_res.y), (255, 0, 0), width=1 + ) + is_used_res_points = [] + for res in self.resource_point: + if res in is_used_res_points: + continue + current_teleport, teleport_min_distance = res.get_resource_distance(teleport_list) + current_res, res_min_distance = res.get_resource_distance(self.resource_point) + if teleport_min_distance < res_min_distance: + self.map.line( + (current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1 + ) + else: + is_used_res_points.append(current_res) + self.map.line( + (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1 + ) + res_cp = self.resource_point[:] + res_cp.remove(current_res) + # for _ in res_cp: + current_teleport_, teleport_min_distance = res.get_resource_distance(teleport_list) + current_res, res_min_distance = res.get_resource_distance(res_cp) + if teleport_min_distance < res_min_distance: + self.map.line( + (current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1 + ) + else: + self.map.line( + (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1 + ) + is_used_res_points.append(current_res) + is_used_res_points.append(res) + + # resources_route = [] + # # 先连上最近的资源路径 + # for res in self.resource_point: + # # 拿到最近的资源 + # current_res, _ = res.get_resource_distance( + # self.resource_point + # + self.teleport_anchor_point + # + self.teleport_god_point + # ) + # self.map.line( + # (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1 + # ) + # resources_route.append((current_res, res)) + # teleport_list = self.teleport_anchor_point + self.teleport_god_point + # for res1, res2 in resources_route: + # point_list = [x for x in resources_route if res1 in x or res2 in x] + # if not list(set(point_list).intersection(set(teleport_list))): + # if res1 not in teleport_list and res2 not in teleport_list: + # # while True: + # # tmp = [x for x in point_list] + # # break + # teleport1, distance1 = res1.get_resource_distance(teleport_list) + # teleport2, distance2 = res2.get_resource_distance(teleport_list) + # if distance1 > distance2: + # self.map.line( + # (teleport1.x, teleport1.y, res1.x, res1.y), + # (255, 0, 0), + # width=1, + # ) + # else: + # self.map.line( + # (teleport2.x, teleport2.y, res2.x, res2.y), + # (255, 0, 0), + # width=1, + # ) + + # self.map.line(xy, (255, 0, 0), width=3) + + # 获取资源图标 + def _get_icon_image(self, id_: int) -> "CreateImg": + icon = icon_path / f"{id_}.png" + if icon.exists(): + return CreateImg( + int(50 * self.ratio), int(50 * self.ratio), background=icon + ) + return CreateImg( + int(50 * self.ratio), + int(50 * self.ratio), + background=f"{icon_path}/box.png", + ) + + # def _get_shortest_path(self, res: 'Resources', res_2: 'Resources'): + + +# 资源类 +class Resources: + def __init__(self, x: int, y: int): + self.x = x + self.y = y + + def get_distance(self, x: int, y: int): + return int(sqrt(pow(abs(self.x - x), 2) + pow(abs(self.y - y), 2))) + + # 拿到资源在该列表中的最短路径 + def get_resource_distance(self, resources: List["Resources"]) -> "Resources, int": + current_res = None + min_distance = 999999 + for res in resources: + distance = self.get_distance(res.x, res.y) + if distance < min_distance and res != self: + current_res = res + min_distance = distance + return current_res, min_distance + + + + + diff --git a/plugins/genshin/query_resource_points/query_resource.py b/plugins/genshin/query_resource_points/query_resource.py old mode 100644 new mode 100755 index e7fc3ed2..958546a0 --- a/plugins/genshin/query_resource_points/query_resource.py +++ b/plugins/genshin/query_resource_points/query_resource.py @@ -3,18 +3,15 @@ from configs.path_config import IMAGE_PATH, TEXT_PATH from PIL.Image import UnidentifiedImageError from utils.message_builder import image from services.log import logger -from .map import Map from utils.image_utils import CreateImg -import asyncio -from pathlib import Path from asyncio.exceptions import TimeoutError from asyncio import Semaphore -from aiohttp.client import ClientSession -from utils.user_agent import get_user_agent from utils.image_utils import is_valid +from utils.http_utils import AsyncHttpx +from pathlib import Path +from .map import Map +import asyncio import nonebot -import aiohttp -import aiofiles import os try: @@ -96,130 +93,126 @@ async def init(flag: bool = False): global CENTER_POINT, resource_name_list try: semaphore = asyncio.Semaphore(10) - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - await download_map_init(session, semaphore, MAP_RATIO, flag) - await download_resource_data(session, semaphore) - await download_resource_type(session) - if not CENTER_POINT: - CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[ - "CENTER_POINT" - ] - with open(resource_type_file, "r", encoding="utf8") as f: - data = json.load(f) - for id_ in data: - for x in data[id_]["children"]: - resource_name_list.append(x["name"]) + await download_map_init(semaphore, flag) + await download_resource_data(semaphore) + await download_resource_type() + if not CENTER_POINT: + CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[ + "CENTER_POINT" + ] + with open(resource_type_file, "r", encoding="utf8") as f: + data = json.load(f) + for id_ in data: + for x in data[id_]["children"]: + resource_name_list.append(x["name"]) except TimeoutError: logger.warning('原神资源查询信息初始化超时....') pass # 图标及位置资源 -async def download_resource_data(session: ClientSession, semaphore: Semaphore): +async def download_resource_data(semaphore: Semaphore): icon_path.mkdir(parents=True, exist_ok=True) resource_label_file.parent.mkdir(parents=True, exist_ok=True) try: - async with session.get(POINT_LIST_URL, timeout=5) as response: - if response.status == 200: - data = await response.json() - if data["message"] == "OK": - data = data["data"] - for lst in ["label_list", "point_list"]: - resource_data = {"CENTER_POINT": CENTER_POINT} - tasks = [] - file = ( - resource_label_file - if lst == "label_list" - else resource_point_file - ) - for x in data[lst]: - id_ = x["id"] - if lst == "label_list": - img_url = x["icon"] - tasks.append( - asyncio.ensure_future( - download_image( - img_url, - f"{icon_path}/{id_}.png", - session, - semaphore, - True, - ) + response = await AsyncHttpx.get(POINT_LIST_URL) + if response.status_code == 200: + data = response.json() + if data["message"] == "OK": + data = data["data"] + for lst in ["label_list", "point_list"]: + resource_data = {"CENTER_POINT": CENTER_POINT} + tasks = [] + file = ( + resource_label_file + if lst == "label_list" + else resource_point_file + ) + for x in data[lst]: + id_ = x["id"] + if lst == "label_list": + img_url = x["icon"] + tasks.append( + asyncio.ensure_future( + download_image( + img_url, + f"{icon_path}/{id_}.png", + semaphore, + True, ) ) - resource_data[id_] = x - await asyncio.gather(*tasks) - with open(file, "w", encoding="utf8") as f: - json.dump(resource_data, f, ensure_ascii=False, indent=4) - else: - logger.warning(f'获取原神资源失败 msg: {data["message"]}') + ) + resource_data[id_] = x + await asyncio.gather(*tasks) + with open(file, "w", encoding="utf8") as f: + json.dump(resource_data, f, ensure_ascii=False, indent=4) else: - logger.warning(f"获取原神资源失败 code:{response.status}") + logger.warning(f'获取原神资源失败 msg: {data["message"]}') + else: + logger.warning(f"获取原神资源失败 code:{response.status_code}") except TimeoutError: logger.warning("获取原神资源数据超时...已再次尝试...") - await download_resource_data(session, semaphore) + await download_resource_data(semaphore) # 下载原神地图并拼图 async def download_map_init( - session: ClientSession, semaphore: Semaphore, ratio: float = 1, flag: bool = False + semaphore: Semaphore, flag: bool = False ): global CENTER_POINT, MAP_RATIO map_path.mkdir(exist_ok=True, parents=True) _map = map_path / "map.png" if _map.exists() and os.path.getsize(_map) > 1024 * 1024 * 30: _map.unlink() - async with session.get(MAP_URL, timeout=5) as response: - if response.status == 200: - data = await response.json() - if data["message"] == "OK": - data = json.loads(data["data"]["info"]["detail"]) - CENTER_POINT = (data["origin"][0], data["origin"][1]) - if not _map.exists(): - # padding_w, padding_h = data['padding'] - data = data["slices"] - idx = 0 - for _map_data in data[0]: - map_url = _map_data['url'] - await download_image( - map_url, - f"{map_path}/{idx}.png", - session, - semaphore, - force_flag=flag, - ) - idx += 1 - _w, h = CreateImg(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size - w = _w * len(os.listdir(map_path)) - map_file = CreateImg(w, h, _w, h, ratio=MAP_RATIO) - for i in range(idx): - map_file.paste(CreateImg(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO)) - map_file.save(f"{map_path}/map.png") - else: - logger.warning(f'获取原神地图失败 msg: {data["message"]}') + response = await AsyncHttpx.get(MAP_URL) + if response.status_code == 200: + data = response.json() + if data["message"] == "OK": + data = json.loads(data["data"]["info"]["detail"]) + CENTER_POINT = (data["origin"][0], data["origin"][1]) + if not _map.exists(): + data = data["slices"] + idx = 0 + for _map_data in data[0]: + map_url = _map_data['url'] + await download_image( + map_url, + f"{map_path}/{idx}.png", + semaphore, + force_flag=flag, + ) + idx += 1 + _w, h = CreateImg(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size + w = _w * len(os.listdir(map_path)) + map_file = CreateImg(w, h, _w, h, ratio=MAP_RATIO) + for i in range(idx): + map_file.paste(CreateImg(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO)) + map_file.save(f"{map_path}/map.png") else: - logger.warning(f"获取原神地图失败 code:{response.status}") + logger.warning(f'获取原神地图失败 msg: {data["message"]}') + else: + logger.warning(f"获取原神地图失败 code:{response.status}") # 下载资源类型数据 -async def download_resource_type(session: ClientSession): +async def download_resource_type(): resource_type_file.parent.mkdir(parents=True, exist_ok=True) - async with session.get(LABEL_URL, timeout=5) as response: - if response.status == 200: - data = await response.json() - if data["message"] == "OK": - data = data["data"]["tree"] - resource_data = {} - for x in data: - id_ = x["id"] - resource_data[id_] = x - with open(resource_type_file, "w", encoding="utf8") as f: - json.dump(resource_data, f, ensure_ascii=False, indent=4) - logger.info(f"更新原神资源类型成功...") - else: - logger.warning(f'获取原神资源类型失败 msg: {data["message"]}') + response = await AsyncHttpx.get(LABEL_URL) + if response.status_code == 200: + data = response.json() + if data["message"] == "OK": + data = data["data"]["tree"] + resource_data = {} + for x in data: + id_ = x["id"] + resource_data[id_] = x + with open(resource_type_file, "w", encoding="utf8") as f: + json.dump(resource_data, f, ensure_ascii=False, indent=4) + logger.info(f"更新原神资源类型成功...") else: - logger.warning(f"获取原神资源类型失败 code:{response.status}") + logger.warning(f'获取原神资源类型失败 msg: {data["message"]}') + else: + logger.warning(f"获取原神资源类型失败 code:{response.status_code}") # 初始化资源图标 @@ -239,7 +232,6 @@ def gen_icon(icon: str): async def download_image( img_url: str, path: str, - session: ClientSession, semaphore: Semaphore, gen_flag: bool = False, force_flag: bool = False, @@ -247,15 +239,12 @@ async def download_image( async with semaphore: try: if not os.path.exists(path) or not is_valid or force_flag: - async with session.get(img_url, timeout=5) as response: - async with aiofiles.open(path, "wb") as f: - await f.write(await response.read()) - logger.info(f"下载原神资源图标:{img_url}") - if gen_flag: - gen_icon(path) - except TimeoutError: - logger.warning("下载原神资源图片超时...已再次尝试...") - await download_image(img_url, path, session, semaphore, gen_flag) + if await AsyncHttpx.download_file(img_url, path): + logger.info(f"下载原神资源图标:{img_url}") + if gen_flag: + gen_icon(path) + else: + logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...") except UnidentifiedImageError: logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}") if os.path.exists(path): diff --git a/plugins/shop/gold_redbag/__init__.py b/plugins/gold_redbag/__init__.py old mode 100644 new mode 100755 similarity index 98% rename from plugins/shop/gold_redbag/__init__.py rename to plugins/gold_redbag/__init__.py index 2c20c7a3..2ffae551 --- a/plugins/shop/gold_redbag/__init__.py +++ b/plugins/gold_redbag/__init__.py @@ -25,6 +25,7 @@ from nonebot.rule import to_me from datetime import datetime, timedelta from configs.config import NICKNAME from apscheduler.jobstores.base import JobLookupError +from nonebot.adapters.cqhttp.exception import ActionFailed import random import time @@ -320,7 +321,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): b64=await generate_send_redbag_pic(int(bot.self_id), greetings) ), ) - except AttributeError: + except ActionFailed: + logger.warning(f"节日红包 GROUP {g} 发送失败..") pass diff --git a/plugins/shop/gold_redbag/data_source.py b/plugins/gold_redbag/data_source.py old mode 100644 new mode 100755 similarity index 86% rename from plugins/shop/gold_redbag/data_source.py rename to plugins/gold_redbag/data_source.py index 46640295..44dd159f --- a/plugins/shop/gold_redbag/data_source.py +++ b/plugins/gold_redbag/data_source.py @@ -1,12 +1,11 @@ from models.bag_user import BagUser -from utils.utils import is_number, get_local_proxy +from utils.utils import is_number, get_local_proxy, get_user_avatar from utils.image_utils import CreateImg from utils.user_agent import get_user_agent from configs.path_config import IMAGE_PATH -from ..models.redbag_user import RedbagUser +from .model import RedbagUser import random import os -import aiohttp from io import BytesIO import asyncio @@ -47,7 +46,7 @@ async def open_redbag(user_id: int, group_id: int, redbag_data: dict): async def generate_send_redbag_pic(user_id: int, msg: str = '恭喜发财 大吉大利'): random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_2")) redbag = CreateImg(0, 0, font_size=38, background=f'{IMAGE_PATH}/prts/redbag_2/{random_redbag}') - ava = CreateImg(65, 65, background=BytesIO(await get_pic(user_id))) + ava = CreateImg(65, 65, background=BytesIO(await get_user_avatar(user_id))) await asyncio.get_event_loop().run_in_executor(None, ava.circle) redbag.text((int((redbag.size[0] - redbag.getsize(msg)[0]) / 2), 210), msg, (240, 218, 164)) redbag.paste(ava, (int((redbag.size[0] - ava.size[0])/2), 130), True) @@ -59,14 +58,6 @@ async def generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount return await asyncio.create_task(_generate_open_redbag_pic(user_id, send_user_nickname, amount, text)) -# 获取QQ头像 -async def get_pic(qq): - url = f'http://q1.qlogo.cn/g?b=qq&nk={qq}&s=160' - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: - return await response.read() - - # 开红包图片 async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str): send_user_nickname += '的红包' @@ -75,7 +66,7 @@ async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amoun size = CreateImg(0, 0, font_size=50).getsize(send_user_nickname) # QQ头像 ava_bk = CreateImg(100 + size[0], 66, color='white', font_size=50) - ava = CreateImg(66, 66, background=BytesIO(await get_pic(user_id))) + ava = CreateImg(66, 66, background=BytesIO(await get_user_avatar(user_id))) ava_bk.paste(ava) ava_bk.text((100, 7), send_user_nickname) # ava_bk.show() diff --git a/plugins/shop/models/redbag_user.py b/plugins/gold_redbag/model.py old mode 100644 new mode 100755 similarity index 100% rename from plugins/shop/models/redbag_user.py rename to plugins/gold_redbag/model.py diff --git a/plugins/group_last_chat/__init__.py b/plugins/group_last_chat/__init__.py old mode 100644 new mode 100755 index b741ae43..ab7a3e55 --- a/plugins/group_last_chat/__init__.py +++ b/plugins/group_last_chat/__init__.py @@ -1,35 +1,35 @@ -from nonebot import on_message -from nonebot.adapters.cqhttp.permission import GROUP -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -import time -from .data_source import cancel_all_notice, save_data, get_data, set_data_value -from services.log import logger - - -__zx_plugin_name__ = "群聊最后聊天时间记录 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -last_chat = on_message(priority=1, block=False, permission=GROUP) - - -@last_chat.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - time_data = await get_data() - set_data_value(event.group_id, time.time()) - if event.group_id in time_data["_group"]: - time_data["_group"].remove(event.group_id) - set_data_value("_group", time_data["_group"]) - for key in time_data.keys(): - if key not in ["check_time", "_group"]: - if key not in time_data["_group"]: - if time.time() - time_data[key] > 60 * 60 * 36: - await cancel_all_notice(key) - time_data["_group"].append(key) - set_data_value("_group", time_data["_group"]) - logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知") - if time.time() - time_data["check_time"] > 60 * 60 * 1: - set_data_value("check_time", time.time()) - save_data() +from nonebot import on_message +from nonebot.adapters.cqhttp.permission import GROUP +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from .data_source import cancel_all_notice, save_data, get_data, set_data_value +from services.log import logger +import time + + +__zx_plugin_name__ = "群聊最后聊天时间记录 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +last_chat = on_message(priority=1, block=False, permission=GROUP) + + +@last_chat.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + time_data = await get_data() + set_data_value(event.group_id, time.time()) + if event.group_id in time_data["_group"]: + time_data["_group"].remove(event.group_id) + set_data_value("_group", time_data["_group"]) + for key in time_data.keys(): + if key not in ["check_time", "_group"]: + if key not in time_data["_group"]: + if time.time() - time_data[key] > 60 * 60 * 36: + await cancel_all_notice(key) + time_data["_group"].append(key) + set_data_value("_group", time_data["_group"]) + logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知") + if time.time() - time_data["check_time"] > 60 * 60 * 1: + set_data_value("check_time", time.time()) + save_data() diff --git a/plugins/group_last_chat/data_source.py b/plugins/group_last_chat/data_source.py old mode 100644 new mode 100755 index 924f81d1..9984e20d --- a/plugins/group_last_chat/data_source.py +++ b/plugins/group_last_chat/data_source.py @@ -1,67 +1,67 @@ -from configs.path_config import DATA_PATH -from utils.utils import get_bot -from datetime import datetime -import time -from services.log import logger -from utils.manager import group_manager -try: - import ujson as json -except ModuleNotFoundError: - import json - - -time_data = {} - - -async def init(): - global time_data - bot = get_bot() - gl = await bot.get_group_list() - gl = [g["group_id"] for g in gl] - data = read_data("group_last_chat_time.json") - for g in gl: - if not data.get(g): - time_data[g] = time.time() - if not time_data.get("check_time"): - time_data["check_time"] = time.time() - if not time_data.get("_group"): - time_data["_group"] = [] - save_data() - return time_data - - -def read_data(file_name: str): - try: - with open(DATA_PATH + file_name, "r", encoding="utf8") as f: - return json.load(f) - except (ValueError, FileNotFoundError): - return {} - - -def save_data(): - with open(DATA_PATH + "group_last_chat_time.json", "w") as f: - json.dump(time_data, f, indent=4) - logger.info( - f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}' - ) - - -# 取消全部通知 -async def cancel_all_notice(group_id): - group_id = int(group_id) - for command in group_manager.get_task_data(): - if await group_manager.check_group_task_status(group_id, command): - await group_manager.close_group_task(group_id, command) - logger.info(f"关闭了 {group_id} 群的全部通知") - - -async def get_data(): - global time_data - if not time_data: - time_data = await init() - return time_data - - -def set_data_value(key, value): - global time_data - time_data[key] = value +from configs.path_config import DATA_PATH +from utils.utils import get_bot +from datetime import datetime +import time +from services.log import logger +from utils.manager import group_manager +try: + import ujson as json +except ModuleNotFoundError: + import json + + +time_data = {} + + +async def init(): + global time_data + bot = get_bot() + gl = await bot.get_group_list() + gl = [g["group_id"] for g in gl] + data = read_data("group_last_chat_time.json") + for g in gl: + if not data.get(g): + time_data[g] = time.time() + if not time_data.get("check_time"): + time_data["check_time"] = time.time() + if not time_data.get("_group"): + time_data["_group"] = [] + save_data() + return time_data + + +def read_data(file_name: str): + try: + with open(DATA_PATH + file_name, "r", encoding="utf8") as f: + return json.load(f) + except (ValueError, FileNotFoundError): + return {} + + +def save_data(): + with open(DATA_PATH + "group_last_chat_time.json", "w") as f: + json.dump(time_data, f, indent=4) + logger.info( + f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}' + ) + + +# 取消全部通知 +async def cancel_all_notice(group_id): + group_id = int(group_id) + for command in group_manager.get_task_data(): + if await group_manager.check_group_task_status(group_id, command): + await group_manager.close_group_task(group_id, command) + logger.info(f"关闭了 {group_id} 群的全部通知") + + +async def get_data(): + global time_data + if not time_data: + time_data = await init() + return time_data + + +def set_data_value(key, value): + global time_data + time_data[key] = value diff --git a/plugins/group_welcome_msg.py b/plugins/group_welcome_msg.py old mode 100644 new mode 100755 diff --git a/plugins/image_management/__init__.py b/plugins/image_management/__init__.py old mode 100644 new mode 100755 index 1e5ef28d..81121c6e --- a/plugins/image_management/__init__.py +++ b/plugins/image_management/__init__.py @@ -21,7 +21,7 @@ Config.add_plugin_config( ) Config.add_plugin_config( - "image_management:delete_img", + "image_management:delete_image", "DELETE_IMAGE_LEVEL [LEVEL]", 7, help_="删除图库图片需要的管理员等级", @@ -29,7 +29,7 @@ Config.add_plugin_config( ) Config.add_plugin_config( - "image_management:move_img", + "image_management:move_image", "MOVE_IMAGE_LEVEL [LEVEL]", 7, help_="移动图库图片需要的管理员等级", @@ -37,7 +37,7 @@ Config.add_plugin_config( ) Config.add_plugin_config( - "image_management:upload_img", + "image_management:upload_image", "UPLOAD_IMAGE_LEVEL [LEVEL]", 6, help_="上传图库图片需要的管理员等级", diff --git a/plugins/image_management/delete_img/__init__.py b/plugins/image_management/delete_image/__init__.py old mode 100644 new mode 100755 similarity index 97% rename from plugins/image_management/delete_img/__init__.py rename to plugins/image_management/delete_image/__init__.py index e7d9b70c..45b08da3 --- a/plugins/image_management/delete_img/__init__.py +++ b/plugins/image_management/delete_image/__init__.py @@ -1,96 +1,96 @@ -from configs.path_config import IMAGE_PATH, TEMP_PATH -from utils.message_builder import image -from services.log import logger -from nonebot import on_command -from nonebot.rule import to_me -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from utils.utils import is_number, cn2py, get_message_text -from configs.config import Config -from pathlib import Path -import os - -__zx_plugin_name__ = "删除图片 [Admin]" -__plugin_usage__ = """ -usage: - 删除图库指定图片 - 指令: - 删除图片 [图库] [id] - 查看图库 - 示例:删除图片 美图 666 -""".strip() -__plugin_des__ = "不好看的图片删掉删掉!" -__plugin_cmd__ = ["删除图片 [图库] [id]", "查看公开图库"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL") -} - - -delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True) - - -@delete_img.args_parser -async def parse(bot: Bot, event: MessageEvent, state: T_State): - if get_message_text(event.json()) in ["取消", "算了"]: - await delete_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ["path"]: - if get_message_text(event.json()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"): - await delete_img.reject("此目录不正确,请重新输入目录!") - state[state["_current_key"]] = get_message_text(event.json()) - if state["_current_key"] == "id": - if not is_number(get_message_text(event.json())): - await delete_img.reject("id不正确!请重新输入数字...") - state[state["_current_key"]] = get_message_text(event.json()) - - -@delete_img.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - raw_arg = get_message_text(event.json()).strip() - if raw_arg: - args = raw_arg.split(" ") - if args[0] in ["帮助"]: - await delete_img.finish(__plugin_usage__) - if len(args) >= 2 and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") and is_number(args[1]): - state["path"] = args[0] - state["id"] = args[1] - - -@delete_img.got("path", prompt="请输入要删除的目标图库?") -@delete_img.got("id", prompt="请输入要删除的图片id?") -async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): - path = cn2py(state["path"]) - img_id = state["id"] - # path = IMAGE_PATH + path - path = Path(IMAGE_PATH) / path - temp = Path(IMAGE_PATH) / "temp" - max_id = len(os.listdir(path)) - 1 - if int(img_id) > max_id or int(img_id) < 0: - await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) - try: - if os.path.exists(temp / "delete.jpg"): - os.remove(temp / "delete.jpg") - logger.info("删除图片 delete.jpg 成功") - except Exception as e: - logger.warning(f"删除图片 delete.jpg 失败 e{e}") - try: - os.rename(path / f"{img_id}.jpg", temp / "delete.jpg") - logger.info(f"移动 {path}/{img_id}.jpg 移动成功") - except Exception as e: - logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}") - if not os.path.exists(path / f"{img_id}.jpg"): - try: - if int(img_id) != max_id: - os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg") - except FileExistsError as e: - logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}") - logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功") - logger.info( - f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" - f" -> id: {img_id} 删除成功" - ) - await delete_img.finish( - f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True - ) - await delete_img.finish(f"id: {img_id} 删除失败!") +from configs.path_config import IMAGE_PATH, TEMP_PATH +from utils.message_builder import image +from services.log import logger +from nonebot import on_command +from nonebot.rule import to_me +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from utils.utils import is_number, cn2py, get_message_text +from configs.config import Config +from pathlib import Path +import os + +__zx_plugin_name__ = "删除图片 [Admin]" +__plugin_usage__ = """ +usage: + 删除图库指定图片 + 指令: + 删除图片 [图库] [id] + 查看图库 + 示例:删除图片 美图 666 +""".strip() +__plugin_des__ = "不好看的图片删掉删掉!" +__plugin_cmd__ = ["删除图片 [图库] [id]", "查看公开图库"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL") +} + + +delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True) + + +@delete_img.args_parser +async def parse(bot: Bot, event: MessageEvent, state: T_State): + if get_message_text(event.json()) in ["取消", "算了"]: + await delete_img.finish("已取消操作..", at_sender=True) + if state["_current_key"] in ["path"]: + if get_message_text(event.json()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"): + await delete_img.reject("此目录不正确,请重新输入目录!") + state[state["_current_key"]] = get_message_text(event.json()) + if state["_current_key"] == "id": + if not is_number(get_message_text(event.json())): + await delete_img.reject("id不正确!请重新输入数字...") + state[state["_current_key"]] = get_message_text(event.json()) + + +@delete_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + raw_arg = get_message_text(event.json()).strip() + if raw_arg: + args = raw_arg.split(" ") + if args[0] in ["帮助"]: + await delete_img.finish(__plugin_usage__) + if len(args) >= 2 and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") and is_number(args[1]): + state["path"] = args[0] + state["id"] = args[1] + + +@delete_img.got("path", prompt="请输入要删除的目标图库?") +@delete_img.got("id", prompt="请输入要删除的图片id?") +async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): + path = cn2py(state["path"]) + img_id = state["id"] + # path = IMAGE_PATH + path + path = Path(IMAGE_PATH) / path + temp = Path(IMAGE_PATH) / "temp" + max_id = len(os.listdir(path)) - 1 + if int(img_id) > max_id or int(img_id) < 0: + await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) + try: + if os.path.exists(temp / "delete.jpg"): + os.remove(temp / "delete.jpg") + logger.info("删除图片 delete.jpg 成功") + except Exception as e: + logger.warning(f"删除图片 delete.jpg 失败 e{e}") + try: + os.rename(path / f"{img_id}.jpg", temp / "delete.jpg") + logger.info(f"移动 {path}/{img_id}.jpg 移动成功") + except Exception as e: + logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}") + if not os.path.exists(path / f"{img_id}.jpg"): + try: + if int(img_id) != max_id: + os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg") + except FileExistsError as e: + logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}") + logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功") + logger.info( + f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" + f" -> id: {img_id} 删除成功" + ) + await delete_img.finish( + f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True + ) + await delete_img.finish(f"id: {img_id} 删除失败!") diff --git a/plugins/image_management/move_img/__init__.py b/plugins/image_management/move_image/__init__.py old mode 100644 new mode 100755 similarity index 97% rename from plugins/image_management/move_img/__init__.py rename to plugins/image_management/move_image/__init__.py index 90a177f2..8522f213 --- a/plugins/image_management/move_img/__init__.py +++ b/plugins/image_management/move_image/__init__.py @@ -1,105 +1,105 @@ -import os -from services.log import logger -from nonebot import on_command -from nonebot.rule import to_me -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from configs.config import Config -from utils.utils import is_number, cn2py -from configs.path_config import IMAGE_PATH -from pathlib import Path - - -__zx_plugin_name__ = "移动图片 [Admin]" -__plugin_usage__ = """ -usage: - 图库间的图片移动操作 - 指令: - 移动图片 [源图库] [目标图库] [id] - 查看图库 - 示例:移动图片 萝莉 美图 234 -""".strip() -__plugin_des__ = "图库间的图片移动操作" -__plugin_cmd__ = ["移动图片 [源图库] [目标图库] [id]", "查看公开图库"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "admin_level": Config.get_config("image_management", "MOVE_IMAGE_LEVEL") -} - - -move_img = on_command("移动图片", priority=5, rule=to_me(), block=True) - - -@move_img.args_parser -async def parse(bot: Bot, event: MessageEvent, state: T_State): - if str(event.get_message()) in ["取消", "算了"]: - await move_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ["source_path", "destination_path"]: - if str(event.get_message()) not in Config.get_config( - "image_management", "IMAGE_DIR_LIST" - ): - await move_img.reject("此目录不正确,请重新输入目录!") - state[state["_current_key"]] = str(event.get_message()) - if state["_current_key"] == "id": - if not is_number(str(event.get_message())): - await move_img.reject("id不正确!请重新输入数字...") - state[state["_current_key"]] = str(event.get_message()) - - -@move_img.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - raw_arg = str(event.get_message()).strip() - if raw_arg: - args = raw_arg.split(" ") - if args[0] in ["帮助"]: - await move_img.finish(__plugin_usage__) - if ( - len(args) >= 3 - and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") - and args[1] in Config.get_config("image_management", "IMAGE_DIR_LIST") - and is_number(args[2]) - ): - state["source_path"] = args[0] - state["destination_path"] = args[1] - state["id"] = args[2] - else: - await move_img.finish("参数错误,请重试", at_sender=True) - - -@move_img.got("source_path", prompt="要从哪个图库移出?") -@move_img.got("destination_path", prompt="要移动到哪个图库?") -@move_img.got("id", prompt="要移动的图片id是?") -async def _(bot: Bot, event: MessageEvent, state: T_State): - img_id = state["id"] - source_path = Path(IMAGE_PATH) / cn2py(state["source_path"]) - destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"]) - destination_path.mkdir(parents=True, exist_ok=True) - max_id = len(os.listdir(source_path)) - 1 - des_max_id = len(os.listdir(destination_path)) - if int(img_id) > max_id or int(img_id) < 0: - await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) - try: - os.rename(source_path / f"{img_id}.jpg", destination_path / f"{des_max_id}.jpg") - logger.info( - f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功" - ) - except Exception as e: - logger.warning( - f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}" - ) - await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True) - if max_id > 0: - try: - os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg") - logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功") - except Exception as e: - logger.warning( - f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}" - ) - await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True) - logger.info( - f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->" - f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功" - ) - await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True) +import os +from services.log import logger +from nonebot import on_command +from nonebot.rule import to_me +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from configs.config import Config +from utils.utils import is_number, cn2py +from configs.path_config import IMAGE_PATH +from pathlib import Path + + +__zx_plugin_name__ = "移动图片 [Admin]" +__plugin_usage__ = """ +usage: + 图库间的图片移动操作 + 指令: + 移动图片 [源图库] [目标图库] [id] + 查看图库 + 示例:移动图片 萝莉 美图 234 +""".strip() +__plugin_des__ = "图库间的图片移动操作" +__plugin_cmd__ = ["移动图片 [源图库] [目标图库] [id]", "查看公开图库"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "admin_level": Config.get_config("image_management", "MOVE_IMAGE_LEVEL") +} + + +move_img = on_command("移动图片", priority=5, rule=to_me(), block=True) + + +@move_img.args_parser +async def parse(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["取消", "算了"]: + await move_img.finish("已取消操作..", at_sender=True) + if state["_current_key"] in ["source_path", "destination_path"]: + if str(event.get_message()) not in Config.get_config( + "image_management", "IMAGE_DIR_LIST" + ): + await move_img.reject("此目录不正确,请重新输入目录!") + state[state["_current_key"]] = str(event.get_message()) + if state["_current_key"] == "id": + if not is_number(str(event.get_message())): + await move_img.reject("id不正确!请重新输入数字...") + state[state["_current_key"]] = str(event.get_message()) + + +@move_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + raw_arg = str(event.get_message()).strip() + if raw_arg: + args = raw_arg.split(" ") + if args[0] in ["帮助"]: + await move_img.finish(__plugin_usage__) + if ( + len(args) >= 3 + and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") + and args[1] in Config.get_config("image_management", "IMAGE_DIR_LIST") + and is_number(args[2]) + ): + state["source_path"] = args[0] + state["destination_path"] = args[1] + state["id"] = args[2] + else: + await move_img.finish("参数错误,请重试", at_sender=True) + + +@move_img.got("source_path", prompt="要从哪个图库移出?") +@move_img.got("destination_path", prompt="要移动到哪个图库?") +@move_img.got("id", prompt="要移动的图片id是?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + img_id = state["id"] + source_path = Path(IMAGE_PATH) / cn2py(state["source_path"]) + destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"]) + destination_path.mkdir(parents=True, exist_ok=True) + max_id = len(os.listdir(source_path)) - 1 + des_max_id = len(os.listdir(destination_path)) + if int(img_id) > max_id or int(img_id) < 0: + await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) + try: + os.rename(source_path / f"{img_id}.jpg", destination_path / f"{des_max_id}.jpg") + logger.info( + f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功" + ) + except Exception as e: + logger.warning( + f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}" + ) + await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True) + if max_id > 0: + try: + os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg") + logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功") + except Exception as e: + logger.warning( + f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}" + ) + await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True) + logger.info( + f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->" + f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功" + ) + await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True) diff --git a/plugins/image_management/send_img/__init__.py b/plugins/image_management/send_image/__init__.py old mode 100644 new mode 100755 similarity index 97% rename from plugins/image_management/send_img/__init__.py rename to plugins/image_management/send_image/__init__.py index 57f9e1f9..26e20294 --- a/plugins/image_management/send_img/__init__.py +++ b/plugins/image_management/send_image/__init__.py @@ -1,124 +1,124 @@ -from nonebot import on_command, on_keyword, on_regex -from configs.path_config import IMAGE_PATH -from utils.message_builder import image -from utils.utils import get_message_text, is_number -from services.log import logger -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from utils.utils import FreqLimiter, cn2py -from configs.config import Config -from utils.manager import group_manager, withdraw_message_manager -import random -import os - -try: - import ujson as json -except ModuleNotFoundError: - import json - -__zx_plugin_name__ = "发送本地图库图片" -__plugin_usage__ = f""" -usage: - 发送指定图库下的随机或指定id图片 - 指令: - {Config.get_config("image_management", "IMAGE_DIR_LIST")} ?[id] - 示例:美图 - 示例: 萝莉 2 -""".strip() -__plugin_des__ = "让看看我的私藏,指[图片]" -__plugin_cmd__ = Config.get_config("image_management", "IMAGE_DIR_LIST") -__plugin_type__ = ("来点好康的",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"), -} -__plugin_task__ = {"pa": "丢人爬"} -__plugin_resources__ = { - "pa": IMAGE_PATH -} - -_flmt = FreqLimiter(1) - -cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST")) - -# print(cmd) - -send_img = on_command("img", aliases=cmd, priority=5, block=True) -pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True) -pa_reg = on_regex("^爬$", priority=5, block=True) - -search_url = "https://api.fantasyzone.cc/tu/search.php" - - -@send_img.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - img_id = get_message_text(event.json()) - path = cn2py(state["_prefix"]["raw_command"]) + "/" - if state["_prefix"]["raw_command"] in Config.get_config( - "image_management", "IMAGE_DIR_LIST" - ): - if not os.path.exists(f"{IMAGE_PATH}/{path}/"): - os.mkdir(f"{IMAGE_PATH}/{path}/") - length = len(os.listdir(IMAGE_PATH + path)) - if length == 0: - logger.warning(f"图库 {path} 为空,调用取消!") - await send_img.finish("该图库中没有图片噢") - index = img_id if img_id else str(random.randint(0, length)) - if not is_number(index): - return - if int(index) > length - 1 or int(index) < 0: - await send_img.finish(f"超过当前上下限!({length - 1})") - result = image(f"{index}.jpg", path) - if result: - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送{path}:" - + result - ) - msg_id = await send_img.send(f"id:{index}" + result) - withdraw_message_manager.withdraw_message( - event, - msg_id, - Config.get_config("image_management", "WITHDRAW_IMAGE_MESSAGE"), - ) - else: - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送 {path} 失败" - ) - await send_img.finish(f"不想给你看Ov|") - - -@pa.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - if ( - isinstance(event, GroupMessageEvent) - and not await group_manager.check_group_task_status(event.group_id, "pa") - or get_message_text(event.json()).startswith("开启") - or get_message_text(event.json()).startswith("关闭") - ): - return - msg = get_message_text(event.json()) - if not msg or str(event.get_message()[:2]) in ["开启", "关闭"]: - return - if _flmt.check(event.user_id): - _flmt.start_cd(event.user_id) - await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa")) - - -@pa_reg.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - if ( - isinstance(event, GroupMessageEvent) - and not await group_manager.check_group_task_status(event.group_id, "pa") - or get_message_text(event.json()).startswith("开启") - or get_message_text(event.json()).startswith("关闭") - ): - return - if _flmt.check(event.user_id): - _flmt.start_cd(event.user_id) - await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa")) +from nonebot import on_command, on_keyword, on_regex +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from utils.utils import get_message_text, is_number +from services.log import logger +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from utils.utils import FreqLimiter, cn2py +from configs.config import Config +from utils.manager import group_manager, withdraw_message_manager +import random +import os + +try: + import ujson as json +except ModuleNotFoundError: + import json + +__zx_plugin_name__ = "发送本地图库图片" +__plugin_usage__ = f""" +usage: + 发送指定图库下的随机或指定id图片 + 指令: + {Config.get_config("image_management", "IMAGE_DIR_LIST")} ?[id] + 示例:美图 + 示例: 萝莉 2 +""".strip() +__plugin_des__ = "让看看我的私藏,指[图片]" +__plugin_cmd__ = Config.get_config("image_management", "IMAGE_DIR_LIST") +__plugin_type__ = ("来点好康的",) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"), +} +__plugin_task__ = {"pa": "丢人爬"} +__plugin_resources__ = { + "pa": IMAGE_PATH +} + +_flmt = FreqLimiter(1) + +cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST")) + +# print(cmd) + +send_img = on_command("img", aliases=cmd, priority=5, block=True) +pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True) +pa_reg = on_regex("^爬$", priority=5, block=True) + +search_url = "https://api.fantasyzone.cc/tu/search.php" + + +@send_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + img_id = get_message_text(event.json()) + path = cn2py(state["_prefix"]["raw_command"]) + "/" + if state["_prefix"]["raw_command"] in Config.get_config( + "image_management", "IMAGE_DIR_LIST" + ): + if not os.path.exists(f"{IMAGE_PATH}/{path}/"): + os.mkdir(f"{IMAGE_PATH}/{path}/") + length = len(os.listdir(IMAGE_PATH + path)) + if length == 0: + logger.warning(f"图库 {path} 为空,调用取消!") + await send_img.finish("该图库中没有图片噢") + index = img_id if img_id else str(random.randint(0, length)) + if not is_number(index): + return + if int(index) > length - 1 or int(index) < 0: + await send_img.finish(f"超过当前上下限!({length - 1})") + result = image(f"{index}.jpg", path) + if result: + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送{path}:" + + result + ) + msg_id = await send_img.send(f"id:{index}" + result) + withdraw_message_manager.withdraw_message( + event, + msg_id, + Config.get_config("image_management", "WITHDRAW_IMAGE_MESSAGE"), + ) + else: + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送 {path} 失败" + ) + await send_img.finish(f"不想给你看Ov|") + + +@pa.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if ( + isinstance(event, GroupMessageEvent) + and not await group_manager.check_group_task_status(event.group_id, "pa") + or get_message_text(event.json()).startswith("开启") + or get_message_text(event.json()).startswith("关闭") + ): + return + msg = get_message_text(event.json()) + if not msg or str(event.get_message()[:2]) in ["开启", "关闭"]: + return + if _flmt.check(event.user_id): + _flmt.start_cd(event.user_id) + await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa")) + + +@pa_reg.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if ( + isinstance(event, GroupMessageEvent) + and not await group_manager.check_group_task_status(event.group_id, "pa") + or get_message_text(event.json()).startswith("开启") + or get_message_text(event.json()).startswith("关闭") + ): + return + if _flmt.check(event.user_id): + _flmt.start_cd(event.user_id) + await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa")) diff --git a/plugins/image_management/upload_img/__init__.py b/plugins/image_management/upload_image/__init__.py old mode 100644 new mode 100755 similarity index 97% rename from plugins/image_management/upload_img/__init__.py rename to plugins/image_management/upload_image/__init__.py index cc2f5908..dbe3ce82 --- a/plugins/image_management/upload_img/__init__.py +++ b/plugins/image_management/upload_image/__init__.py @@ -1,127 +1,126 @@ -from nonebot import on_command -from nonebot.rule import to_me -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent - -from configs.config import Config -from utils.utils import get_message_imgs, get_message_text -from .data_source import upload_image_to_local - - -__zx_plugin_name__ = "上传图片 [Admin]" -__plugin_usage__ = """ -usage: - 上传图片至指定图库 - 指令: - 查看图库 - 上传图片 [图库] [图片] - 连续上传图片 [图库] - 示例:上传图片 美图 [图片] - * 连续上传图片可以通过发送 “stop” 表示停止收集发送的图片,可以开始上传 * -""".strip() -__plugin_des__ = "指定图库图片上传" -__plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = {"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")} - -upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True) - -continuous_upload_img = on_command("连续上传图片", rule=to_me(), priority=5, block=True) - -show_gallery = on_command("查看公开图库", priority=1, block=True) - - -@show_gallery.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - x = '公开图库列表:\n' - for i, e in enumerate(Config.get_config("image_management", "IMAGE_DIR_LIST")): - x += f'\t{i+1}.{e}\n' - await show_gallery.send(x[:-1]) - - -@upload_img.args_parser -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if msg in ["取消", "算了"]: - await upload_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ["path"]: - if msg not in Config.get_config("image_management", "IMAGE_DIR_LIST"): - await upload_img.reject("此目录不正确,请重新输入目录!") - state["path"] = msg - if state["_current_key"] in ["imgs"]: - if not get_message_imgs(event.json()): - await upload_img.reject("图呢图呢图呢图呢!GKD!") - state["imgs"] = get_message_imgs(event.json()) - - -@upload_img.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - raw_arg = get_message_text(event.json()) - img_list = get_message_imgs(event.json()) - if raw_arg: - if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"): - state["path"] = raw_arg - if img_list: - state["imgs"] = img_list - - -@upload_img.got("path", prompt="要将图片上传至什么图库呢?") -async def _(bot: Bot, event: MessageEvent, state: T_State): - pass - - -@upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") -async def _(bot: Bot, event: MessageEvent, state: T_State): - path = state["path"] - img_list = state["imgs"] - group_id = 0 - if isinstance(event, GroupMessageEvent): - group_id = event.group_id - await upload_img.send( - await upload_image_to_local(img_list, path, event.user_id, group_id) - ) - - -@continuous_upload_img.args_parser -async def _(bot: Bot, event: MessageEvent, state: T_State): - if str(event.get_message()) in ["取消", "算了"]: - await continuous_upload_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ["path"]: - if str(event.get_message()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"): - await continuous_upload_img.reject("此目录不正确,请重新输入目录!") - state[state["_current_key"]] = str(event.get_message()) - else: - if get_message_text(event.json()) not in ["stop"]: - img = get_message_imgs(event.json()) - if img: - state["tmp"].extend(img) - await continuous_upload_img.reject("图再来!!") - else: - state["imgs"] = state["tmp"] - - -@continuous_upload_img.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - path = get_message_imgs(event.json()) - if path in Config.get_config("image_management", "IMAGE_DIR_LIST"): - state["path"] = path - await continuous_upload_img.send("图来!!") - state["tmp"] = [] - - -@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?") -async def _(bot: Bot, event: MessageEvent, state: T_State): - pass - - -@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") -async def _(bot: Bot, event: MessageEvent, state: T_State): - path = state["path"] - img_list = state["imgs"] - group_id = 0 - if isinstance(event, GroupMessageEvent): - group_id = event.group_id - await continuous_upload_img.send( - await upload_image_to_local(img_list, path, event.user_id, group_id) - ) +from nonebot import on_command +from nonebot.rule import to_me +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from configs.config import Config +from utils.utils import get_message_imgs, get_message_text +from .data_source import upload_image_to_local + + +__zx_plugin_name__ = "上传图片 [Admin]" +__plugin_usage__ = """ +usage: + 上传图片至指定图库 + 指令: + 查看图库 + 上传图片 [图库] [图片] + 连续上传图片 [图库] + 示例:上传图片 美图 [图片] + * 连续上传图片可以通过发送 “stop” 表示停止收集发送的图片,可以开始上传 * +""".strip() +__plugin_des__ = "指定图库图片上传" +__plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = {"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")} + +upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True) + +continuous_upload_img = on_command("连续上传图片", rule=to_me(), priority=5, block=True) + +show_gallery = on_command("查看公开图库", priority=1, block=True) + + +@show_gallery.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + x = '公开图库列表:\n' + for i, e in enumerate(Config.get_config("image_management", "IMAGE_DIR_LIST")): + x += f'\t{i+1}.{e}\n' + await show_gallery.send(x[:-1]) + + +@upload_img.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if msg in ["取消", "算了"]: + await upload_img.finish("已取消操作..", at_sender=True) + if state["_current_key"] in ["path"]: + if msg not in Config.get_config("image_management", "IMAGE_DIR_LIST"): + await upload_img.reject("此目录不正确,请重新输入目录!") + state["path"] = msg + if state["_current_key"] in ["imgs"]: + if not get_message_imgs(event.json()): + await upload_img.reject("图呢图呢图呢图呢!GKD!") + state["imgs"] = get_message_imgs(event.json()) + + +@upload_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + raw_arg = get_message_text(event.json()) + img_list = get_message_imgs(event.json()) + if raw_arg: + if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"): + state["path"] = raw_arg + if img_list: + state["imgs"] = img_list + + +@upload_img.got("path", prompt="要将图片上传至什么图库呢?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + pass + + +@upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") +async def _(bot: Bot, event: MessageEvent, state: T_State): + path = state["path"] + img_list = state["imgs"] + group_id = 0 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + await upload_img.send( + await upload_image_to_local(img_list, path, event.user_id, group_id) + ) + + +@continuous_upload_img.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["取消", "算了"]: + await continuous_upload_img.finish("已取消操作..", at_sender=True) + if state["_current_key"] in ["path"]: + if str(event.get_message()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"): + await continuous_upload_img.reject("此目录不正确,请重新输入目录!") + state[state["_current_key"]] = str(event.get_message()) + else: + if get_message_text(event.json()) not in ["stop"]: + img = get_message_imgs(event.json()) + if img: + state["tmp"].extend(img) + await continuous_upload_img.reject("图再来!!") + else: + state["imgs"] = state["tmp"] + + +@continuous_upload_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + path = get_message_imgs(event.json()) + if path in Config.get_config("image_management", "IMAGE_DIR_LIST"): + state["path"] = path + await continuous_upload_img.send("图来!!") + state["tmp"] = [] + + +@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + pass + + +@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") +async def _(bot: Bot, event: MessageEvent, state: T_State): + path = state["path"] + img_list = state["imgs"] + group_id = 0 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + await continuous_upload_img.send( + await upload_image_to_local(img_list, path, event.user_id, group_id) + ) diff --git a/plugins/image_management/upload_img/data_source.py b/plugins/image_management/upload_image/data_source.py old mode 100644 new mode 100755 similarity index 50% rename from plugins/image_management/upload_img/data_source.py rename to plugins/image_management/upload_image/data_source.py index 7123d04b..3ce0a399 --- a/plugins/image_management/upload_img/data_source.py +++ b/plugins/image_management/upload_image/data_source.py @@ -1,51 +1,42 @@ -from configs.config import NICKNAME -from typing import List -from configs.path_config import IMAGE_PATH -from services.log import logger -from utils.utils import cn2py -from pathlib import Path -import aiofiles -import aiohttp -import os - - -async def upload_image_to_local( - img_list: List[str], path: str, user_id: int, group_id: int = 0 -) -> str: - _path = path - path = Path(IMAGE_PATH) / cn2py(path) - path.mkdir(parents=True, exist_ok=True) - img_id = len(os.listdir(path)) - failed_list = [] - success_id = "" - async with aiohttp.ClientSession() as session: - for img_url in img_list: - try: - async with session.get(img_url, timeout=7) as response: - if response.status == 200: - async with aiofiles.open(path / f"{img_id}.jpg", "wb") as f: - await f.write(await response.read()) - success_id += str(img_id) + "," - img_id += 1 - else: - failed_list.append(img_url) - logger.warning(f"图片:{img_url} 下载失败....") - except TimeoutError as e: - logger.warning(f"图片:{img_url} 下载超时....e:{e}") - if img_url not in failed_list: - failed_list.append(img_url) - failed_result = "" - for img in failed_list: - failed_result += str(img) + "\n" - logger.info( - f"USER {user_id} GROUP {group_id}" - f" 上传图片至 {_path} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}" - ) - if failed_list: - return ( - f"这次一共为 {_path}库 添加了 {len(img_list) - len(failed_list)} 张图片\n" - f"依次的Id为:{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" - ) - else: - return f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为:" \ - f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" +from configs.config import NICKNAME +from typing import List +from configs.path_config import IMAGE_PATH +from services.log import logger +from utils.utils import cn2py +from pathlib import Path +from utils.http_utils import AsyncHttpx +import os + + +async def upload_image_to_local( + img_list: List[str], path: str, user_id: int, group_id: int = 0 +) -> str: + _path = path + path = Path(IMAGE_PATH) / cn2py(path) + path.mkdir(parents=True, exist_ok=True) + img_id = len(os.listdir(path)) + failed_list = [] + success_id = "" + for img_url in img_list: + if await AsyncHttpx.download_file(img_url, path / f"{img_id}.jpg"): + success_id += str(img_id) + "," + img_id += 1 + else: + failed_list.append(img_url) + failed_result = "" + for img in failed_list: + failed_result += str(img) + "\n" + logger.info( + f"USER {user_id} GROUP {group_id}" + f" 上传图片至 {_path} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}" + ) + if failed_list: + return ( + f"这次一共为 {_path}库 添加了 {len(img_list) - len(failed_list)} 张图片\n" + f"依次的Id为:{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" + ) + else: + return ( + f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为:" + f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" + ) diff --git a/plugins/jitang.py b/plugins/jitang.py deleted file mode 100644 index a3663741..00000000 --- a/plugins/jitang.py +++ /dev/null @@ -1,52 +0,0 @@ -from nonebot import on_command -from utils.user_agent import get_user_agent -from services.log import logger -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.typing import T_State -import aiohttp -from asyncio.exceptions import TimeoutError -from configs.config import Config - - -__zx_plugin_name__ = "鸡汤" -__plugin_usage__ = """ -usage: - 不喝点什么感觉有点不舒服 - 指令: - 鸡汤 -""".strip() -__plugin_des__ = "喏,亲手为你煮的鸡汤" -__plugin_cmd__ = ["鸡汤"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["鸡汤", "毒鸡汤"], -} - -url = "https://v2.alapi.cn/api/soul" - - -jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True) - - -@jitang.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - params = {"format": "json", "token": f"{Config.get_config('alapi', 'ALAPI_TOKEN')}"} - try: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, timeout=7, params=params) as response: - if response.status == 200: - data = await response.json() - await jitang.send(data["data"]["content"]) - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送鸡汤:" + data["data"]["content"] - ) - else: - await jitang.send("鸡汤煮坏掉了...") - except TimeoutError: - await jitang.send("鸡汤煮超时了##", at_sender=True) diff --git a/plugins/luxun/__init__.py b/plugins/luxun/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/mute.py b/plugins/mute.py old mode 100644 new mode 100755 index 0cd87fc7..4cf1d569 --- a/plugins/mute.py +++ b/plugins/mute.py @@ -1,17 +1,16 @@ from nonebot import on_message, on_command from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp.permission import GROUP -from utils.utils import get_message_text, is_number, get_message_imgs, get_local_proxy +from utils.utils import get_message_text, is_number, get_message_imgs from nonebot.typing import T_State -from asyncio.exceptions import TimeoutError -import time from nonebot.adapters.cqhttp.exception import ActionFailed -from configs.path_config import DATA_PATH, IMAGE_PATH +from configs.path_config import DATA_PATH, TEMP_PATH from utils.image_utils import get_img_hash from services.log import logger from configs.config import NICKNAME, Config -import aiohttp -import aiofiles +from utils.http_utils import AsyncHttpx +from pathlib import Path +import time try: import ujson as json @@ -36,25 +35,13 @@ __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = {"admin_level": Config.get_config("mute", "MUTE_LEVEL")} __plugin_configs__ = { - "MUTE_LEVEL [LEVEL]": { - "value": 5, - "help": "更改禁言设置的管理权限", - "default_value": 5 - }, - "MUTE_DEFAULT_COUNT": { - "value": 10, - "help": "刷屏禁言默认检测次数", - "default_value": 10 - }, - "MUTE_DEFAULT_TIME": { - "value": 7, - "help": "刷屏检测默认规定时间", - "default_value": 7 - }, + "MUTE_LEVEL [LEVEL]": {"value": 5, "help": "更改禁言设置的管理权限", "default_value": 5}, + "MUTE_DEFAULT_COUNT": {"value": 10, "help": "刷屏禁言默认检测次数", "default_value": 10}, + "MUTE_DEFAULT_TIME": {"value": 7, "help": "刷屏检测默认规定时间", "default_value": 7}, "MUTE_DEFAULT_DURATION": { "value": 10, "help": "刷屏检测默禁言时长(分钟)", - "default_value": 10 + "default_value": 10, }, } @@ -85,18 +72,11 @@ def save_data(): async def download_img_and_hash(url, group_id): - try: - async with aiohttp.ClientSession() as session: - async with session.get( - url, proxy=get_local_proxy(), timeout=10 - ) as response: - async with aiofiles.open( - IMAGE_PATH + f"temp/mute_{group_id}_img.jpg", "wb" - ) as f: - await f.write(await response.read()) - return str(get_img_hash(IMAGE_PATH + f"temp/mute_{group_id}_img.jpg")) - except TimeoutError: - return "" + if await AsyncHttpx.download_file( + url, Path(TEMP_PATH) / f"mute_{group_id}_img.jpg" + ): + return str(get_img_hash(Path(TEMP_PATH) / f"mute_{group_id}_img.jpg")) + return "" mute_dict = {} diff --git a/plugins/my_info.py b/plugins/my_info.py old mode 100644 new mode 100755 diff --git a/plugins/nbnhhsh.py b/plugins/nbnhhsh.py old mode 100644 new mode 100755 index 2d0da4cc..469e620a --- a/plugins/nbnhhsh.py +++ b/plugins/nbnhhsh.py @@ -1,66 +1,63 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from utils.utils import get_message_text -from services.log import logger -import ujson as json -import aiohttp - - -__zx_plugin_name__ = "能不能好好说话" -__plugin_usage__ = """ -usage: - 说人话 - 指令: - nbnhhsh [文本] -""".strip() -__plugin_des__ = "能不能好好说话,说人话" -__plugin_cmd__ = ["nbnhhsh [文本]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["能不能好好说话", "nbnhhsh"], -} - -HHSH_GUESS_URL = "https://lab.magiconch.com/api/nbnhhsh/guess" - -nbnhhsh = on_command("nbnhhsh", aliases={"能不能好好说话"}, priority=5, block=True) - - -@nbnhhsh.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if not msg: - await nbnhhsh.finish('没话说就别说话!') - async with aiohttp.ClientSession( - headers={"content-type": "application/json"} - ) as session: - async with session.post( - HHSH_GUESS_URL, data=json.dumps({"text": msg}), timeout=5 - ) as response: - if response.status == 200: - try: - data = await response.json() - tmp = "" - rst = "" - for x in data: - trans = "" - if x.get("trans"): - trans = x["trans"][0] - elif x.get("inputting"): - trans = ",".join(x["inputting"]) - tmp += f'{x["name"]} -> {trans}\n' - rst += trans - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送能不能好好说话: {msg} -> {rst}" - ) - await nbnhhsh.send(f"{tmp}={rst}", at_sender=True) - except (IndexError, KeyError): - await nbnhhsh.finish("没有找到对应的翻译....") - else: - await nbnhhsh.finish("网络访问失败了....") +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from utils.utils import get_message_text +from utils.http_utils import AsyncHttpx +from services.log import logger +import ujson as json + + +__zx_plugin_name__ = "能不能好好说话" +__plugin_usage__ = """ +usage: + 说人话 + 指令: + nbnhhsh [文本] +""".strip() +__plugin_des__ = "能不能好好说话,说人话" +__plugin_cmd__ = ["nbnhhsh [文本]"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["能不能好好说话", "nbnhhsh"], +} + +HHSH_GUESS_URL = "https://lab.magiconch.com/api/nbnhhsh/guess" + +nbnhhsh = on_command("nbnhhsh", aliases={"能不能好好说话"}, priority=5, block=True) + + +@nbnhhsh.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await nbnhhsh.finish("没话说就别说话!") + response = await AsyncHttpx.post( + HHSH_GUESS_URL, + data=json.dumps({"text": msg}), + timeout=5, + headers={"content-type": "application/json"}, + ) + try: + data = response.json() + tmp = "" + rst = "" + for x in data: + trans = "" + if x.get("trans"): + trans = x["trans"][0] + elif x.get("inputting"): + trans = ",".join(x["inputting"]) + tmp += f'{x["name"]} -> {trans}\n' + rst += trans + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送能不能好好说话: {msg} -> {rst}" + ) + await nbnhhsh.send(f"{tmp}={rst}", at_sender=True) + except (IndexError, KeyError): + await nbnhhsh.finish("没有找到对应的翻译....") diff --git a/plugins/nonebot_plugin_picsearcher/__init__.py b/plugins/nonebot_plugin_picsearcher/__init__.py old mode 100644 new mode 100755 index 9810adac..21eb8617 --- a/plugins/nonebot_plugin_picsearcher/__init__.py +++ b/plugins/nonebot_plugin_picsearcher/__init__.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from typing import Dict -from aiohttp.client_exceptions import ClientError from nonebot.plugin import on_command, on_message from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State @@ -127,8 +126,8 @@ async def get_setu(bot: Bot, event: MessageEvent, state: T_State): except IndexError: # await bot.send(event, traceback.format_exc()) await setu.finish("参数错误") - except ClientError: - await setu.finish("连接失败") + # except ClientError: + # await setu.finish("连接失败") pic_map: Dict[str, str] = {} # 保存这个群的其阿金一张色图 {"123456":http://xxx"} @@ -174,7 +173,8 @@ async def handle_previous(bot: Bot, event: GroupMessageEvent, state: T_State): idx += 1 except IndexError: await previous.finish("参数错误") - except ClientError: - await previous.finish("连接错误") except KeyError: await previous.finish("没有图啊QAQ") + except Exception as e: + logger.error(f"识图未知错误 {type(e)}:{e}") + await previous.finish("未知错误...") diff --git a/plugins/nonebot_plugin_picsearcher/ascii2d.py b/plugins/nonebot_plugin_picsearcher/ascii2d.py old mode 100644 new mode 100755 diff --git a/plugins/nonebot_plugin_picsearcher/ex.py b/plugins/nonebot_plugin_picsearcher/ex.py old mode 100644 new mode 100755 diff --git a/plugins/nonebot_plugin_picsearcher/formdata.py b/plugins/nonebot_plugin_picsearcher/formdata.py old mode 100644 new mode 100755 diff --git a/plugins/nonebot_plugin_picsearcher/iqdb.py b/plugins/nonebot_plugin_picsearcher/iqdb.py old mode 100644 new mode 100755 diff --git a/plugins/nonebot_plugin_picsearcher/saucenao.py b/plugins/nonebot_plugin_picsearcher/saucenao.py old mode 100644 new mode 100755 index 8b60838a..f359b19d --- a/plugins/nonebot_plugin_picsearcher/saucenao.py +++ b/plugins/nonebot_plugin_picsearcher/saucenao.py @@ -79,7 +79,7 @@ async def get_pic_from_url(url: str): data = FormData(boundary="----WebKitFormBoundaryPpuR3EZ1Ap2pXv8W") data.add_field(name="file", value=content, content_type="image/jpeg", filename="blob") - async with session.post("https://saucenao.com/search.php", data=data, headers=header) as res: + async with session.post("https://saucenao.com/search.php", proxy=get_local_proxy(), data=data, headers=header) as res: html = await res.text() image_data = [each for each in parse_html(html)] return image_data diff --git a/plugins/nonebot_plugin_picsearcher/trace.py b/plugins/nonebot_plugin_picsearcher/trace.py old mode 100644 new mode 100755 diff --git a/plugins/nonebot_plugin_picsearcher/yandex.py b/plugins/nonebot_plugin_picsearcher/yandex.py old mode 100644 new mode 100755 diff --git a/plugins/one_friend/__init__.py b/plugins/one_friend/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/open_cases/__init__.py b/plugins/open_cases/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/open_cases/config.py b/plugins/open_cases/config.py old mode 100644 new mode 100755 diff --git a/plugins/open_cases/models/__init__.py b/plugins/open_cases/models/__init__.py old mode 100644 new mode 100755 index a944b2fd..acf2ed1e --- a/plugins/open_cases/models/__init__.py +++ b/plugins/open_cases/models/__init__.py @@ -1,2 +1,2 @@ -from .open_cases_user import * -from .buff_prices import * +from .open_cases_user import * +from .buff_prices import * diff --git a/plugins/open_cases/models/buff_prices.py b/plugins/open_cases/models/buff_prices.py old mode 100644 new mode 100755 diff --git a/plugins/open_cases/models/open_cases_user.py b/plugins/open_cases/models/open_cases_user.py old mode 100644 new mode 100755 diff --git a/plugins/open_cases/open_cases_c.py b/plugins/open_cases/open_cases_c.py old mode 100644 new mode 100755 diff --git a/plugins/open_cases/utils.py b/plugins/open_cases/utils.py old mode 100644 new mode 100755 index 5c534a62..cb5b4d71 --- a/plugins/open_cases/utils.py +++ b/plugins/open_cases/utils.py @@ -1,10 +1,8 @@ from .models.buff_prices import BuffPrice from services.db_context import db from datetime import datetime, timedelta -from utils.user_agent import get_user_agent from configs.path_config import IMAGE_PATH -import aiohttp -import aiofiles +from utils.http_utils import AsyncHttpx from .models.open_cases_user import OpenCasesUser import os from services.log import logger @@ -43,127 +41,119 @@ async def util_get_buff_price(case_name: str = "狂牙大行动") -> str: CASE_PINK = eval(case + "_CASE_PINK") CASE_PURPLE = eval(case + "_CASE_PURPLE") CASE_BLUE = eval(case + "_CASE_BLUE") - async with aiohttp.ClientSession( - cookies=cookie, headers=get_user_agent() - ) as session: - for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]: - for skin in total_list: - if skin in [ - "蝴蝶刀 | 无涂装", - "求生匕首 | 无涂装", - "流浪者匕首 | 无涂装", - "系绳匕首 | 无涂装", - "骷髅匕首 | 无涂装", - ]: - skin = skin.split("|")[0].strip() - async with db.transaction(): - name_list = [] - price_list = [] - parameter = {"game": "csgo", "page_num": "1", "search": skin} - try: - async with session.get( - url, - proxy=Config.get_config("open_cases", "BUFF_PROXY"), - params=parameter, - timeout=20, - ) as response: - if response.status == 200: - data = (await response.json())["data"] - total_page = data["total_page"] - data = data["items"] - flag = False - if ( - skin.find("|") == -1 - ): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: - for i in range(1, total_page + 1): - name_list = [] - price_list = [] - parameter = { - "game": "csgo", - "page_num": f"{i}", - "search": skin, - } - async with session.get( - url, params=parameter, timeout=20 - ) as res: - data = (await response.json())["data"][ - "items" - ] - for j in range(len(data)): - if data[j]["name"] in [f"{skin}(★)"]: - name = data[j]["name"] - price = data[j][ - "sell_reference_price" - ] - name_list.append( - name.split("(")[0].strip() - + " | 无涂装" - ) - price_list.append(price) - flag = True - break - if flag: - break - else: - try: - for _ in range(total_page): - for i in range(len(data)): - name = data[i]["name"] - price = data[i]["sell_reference_price"] - name_list.append(name) - price_list.append(price) - except Exception as e: - failed_list.append(skin) - logger.warning(f"{skin}更新失败") - else: + for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]: + for skin in total_list: + if skin in [ + "蝴蝶刀 | 无涂装", + "求生匕首 | 无涂装", + "流浪者匕首 | 无涂装", + "系绳匕首 | 无涂装", + "骷髅匕首 | 无涂装", + ]: + skin = skin.split("|")[0].strip() + async with db.transaction(): + name_list = [] + price_list = [] + parameter = {"game": "csgo", "page_num": "1", "search": skin} + try: + response = await AsyncHttpx.get(url, proxy=Config.get_config("open_cases", "BUFF_PROXY"), + params=parameter, + cookies=cookie,) + if response.status_code == 200: + data = response.json()["data"] + total_page = data["total_page"] + data = data["items"] + flag = False + if ( + skin.find("|") == -1 + ): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: + for i in range(1, total_page + 1): + name_list = [] + price_list = [] + parameter = { + "game": "csgo", + "page_num": f"{i}", + "search": skin, + } + res = await AsyncHttpx.get(url, params=parameter) + data = res.json()["data"][ + "items" + ] + for j in range(len(data)): + if data[j]["name"] in [f"{skin}(★)"]: + name = data[j]["name"] + price = data[j][ + "sell_reference_price" + ] + name_list.append( + name.split("(")[0].strip() + + " | 无涂装" + ) + price_list.append(price) + flag = True + break + if flag: + break + else: + try: + for _ in range(total_page): + for i in range(len(data)): + name = data[i]["name"] + price = data[i]["sell_reference_price"] + name_list.append(name) + price_list.append(price) + except Exception as e: failed_list.append(skin) logger.warning(f"{skin}更新失败") - except Exception: + else: failed_list.append(skin) logger.warning(f"{skin}更新失败") + except Exception: + failed_list.append(skin) + logger.warning(f"{skin}更新失败") + continue + for i in range(len(name_list)): + name = name_list[i].strip() + price = float(price_list[i]) + if name.find("(★)") != -1: + name = name[: name.find("(")] + name[name.find(")") + 1 :] + if name.find("消音") != -1 and name.find("(S") != -1: + name = name.split("(")[0][:-4] + "(" + name.split("(")[1] + name = ( + name.split("|")[0].strip() + + " | " + + name.split("|")[1].strip() + ) + elif name.find("消音") != -1: + name = ( + name.split("|")[0][:-5].strip() + + " | " + + name.split("|")[1].strip() + ) + if name.find(" 18 ") != -1 and name.find("(S") != -1: + name = name.split("(")[0][:-5] + "(" + name.split("(")[1] + name = ( + name.split("|")[0].strip() + + " | " + + name.split("|")[1].strip() + ) + elif name.find(" 18 ") != -1: + name = ( + name.split("|")[0][:-6].strip() + + " | " + + name.split("|")[1].strip() + ) + dbskin = await BuffPrice.ensure(name, True) + if ( + dbskin.update_date + timedelta(8) + ).date() == datetime.now().date(): continue - for i in range(len(name_list)): - name = name_list[i].strip() - price = float(price_list[i]) - if name.find("(★)") != -1: - name = name[: name.find("(")] + name[name.find(")") + 1 :] - if name.find("消音") != -1 and name.find("(S") != -1: - name = name.split("(")[0][:-4] + "(" + name.split("(")[1] - name = ( - name.split("|")[0].strip() - + " | " - + name.split("|")[1].strip() - ) - elif name.find("消音") != -1: - name = ( - name.split("|")[0][:-5].strip() - + " | " - + name.split("|")[1].strip() - ) - if name.find(" 18 ") != -1 and name.find("(S") != -1: - name = name.split("(")[0][:-5] + "(" + name.split("(")[1] - name = ( - name.split("|")[0].strip() - + " | " - + name.split("|")[1].strip() - ) - elif name.find(" 18 ") != -1: - name = ( - name.split("|")[0][:-6].strip() - + " | " - + name.split("|")[1].strip() - ) - dbskin = await BuffPrice.ensure(name, True) - if ( - dbskin.update_date + timedelta(8) - ).date() == datetime.now().date(): - continue - await dbskin.update( - case_id=case_id, - skin_price=price, - update_date=datetime.now(), - ).apply() - logger.info(f"{name_list[i]}---------->成功更新") + await dbskin.update( + case_id=case_id, + skin_price=price, + update_date=datetime.now(), + ).apply() + logger.info(f"{name_list[i]}---------->成功更新") result = None if failed_list: result = "" @@ -187,112 +177,88 @@ async def util_get_buff_img(case_name: str = "狂牙大行动") -> str: CASE_PINK = eval(case + "_CASE_PINK") CASE_PURPLE = eval(case + "_CASE_PURPLE") CASE_BLUE = eval(case + "_CASE_BLUE") - async with aiohttp.ClientSession( - cookies=cookie, headers=get_user_agent() - ) as session: - for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]: - for skin in total_list: - parameter = {"game": "csgo", "page_num": "1", "search": skin} - if skin in [ - "蝴蝶刀 | 无涂装", - "求生匕首 | 无涂装", - "流浪者匕首 | 无涂装", - "系绳匕首 | 无涂装", - "骷髅匕首 | 无涂装", - ]: - skin = skin.split("|")[0].strip() - logger.info(f"开始更新----->{skin}") - skin_name = "" - # try: - async with session.get( - url, - proxy=Config.get_config("open_cases", "BUFF_PROXY"), - params=parameter, - timeout=20, - ) as response: - if response.status == 200: - data = (await response.json())["data"] - total_page = data["total_page"] - flag = False - if ( - skin.find("|") == -1 - ): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: - for i in range(1, total_page + 1): - async with session.get( - url, params=parameter, timeout=20 - ) as res: - data = (await response.json())["data"]["items"] - for j in range(len(data)): - if data[j]["name"] in [f"{skin}(★)"]: - img_url = data[j]["goods_info"]["icon_url"] - for k in pypinyin.pinyin( - skin + "无涂装", style=pypinyin.NORMAL - ): - skin_name += "".join(k) - async with aiofiles.open( - IMAGE_PATH + path + skin_name + ".png", - "wb", - ) as f: - logger.info(f"------->开始写入{skin}") - async with session.get( - img_url, timeout=7 - ) as res: - await f.write(await res.read()) - flag = True - break - if flag: - break - else: - img_url = (await response.json())["data"]["items"][0][ - "goods_info" - ]["icon_url"] - for i in pypinyin.pinyin( - skin.replace("|", "-").strip(), style=pypinyin.NORMAL - ): - skin_name += "".join(i) - async with aiofiles.open( - IMAGE_PATH + path + skin_name + ".png", "wb" - ) as f: - logger.info(f"------->开始写入 {skin}") - async with session.get(img_url, timeout=7) as res: - await f.write(await res.read()) + for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]: + for skin in total_list: + parameter = {"game": "csgo", "page_num": "1", "search": skin} + if skin in [ + "蝴蝶刀 | 无涂装", + "求生匕首 | 无涂装", + "流浪者匕首 | 无涂装", + "系绳匕首 | 无涂装", + "骷髅匕首 | 无涂装", + ]: + skin = skin.split("|")[0].strip() + logger.info(f"开始更新----->{skin}") + skin_name = "" + # try: + response = await AsyncHttpx.get(url, proxy=Config.get_config("open_cases", "BUFF_PROXY"), params=parameter) + if response.status_code == 200: + data = response.json()["data"] + total_page = data["total_page"] + flag = False + if ( + skin.find("|") == -1 + ): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: + for i in range(1, total_page + 1): + res = await AsyncHttpx.get(url, params=parameter) + data = res.json()["data"]["items"] + for j in range(len(data)): + if data[j]["name"] in [f"{skin}(★)"]: + img_url = data[j]["goods_info"]["icon_url"] + for k in pypinyin.pinyin( + skin + "无涂装", style=pypinyin.NORMAL + ): + skin_name += "".join(k) + await AsyncHttpx.download_file(img_url, IMAGE_PATH + path + skin_name + ".png") + flag = True + break + if flag: + break + else: + img_url = (await response.json())["data"]["items"][0][ + "goods_info" + ]["icon_url"] + for i in pypinyin.pinyin( + skin.replace("|", "-").strip(), style=pypinyin.NORMAL + ): + skin_name += "".join(i) + if await AsyncHttpx.download_file(img_url, IMAGE_PATH + path + skin_name + ".png"): + logger.info(f"------->写入 {skin} 成功") + else: + logger.info(f"------->写入 {skin} 失败") result = None if error_list: result = "" - for errskin in error_list: - result += errskin + "\n" + for err_skin in error_list: + result += err_skin + "\n" return result[:-1] if result else "更新图片成功" -async def get_price(dname): +async def get_price(d_name): cookie = {"session": Config.get_config("open_cases", "COOKIE")} name_list = [] price_list = [] - parameter = {"game": "csgo", "page_num": "1", "search": dname} + parameter = {"game": "csgo", "page_num": "1", "search": d_name} try: - async with aiohttp.ClientSession( - cookies=cookie, headers=get_user_agent() - ) as session: - async with session.get(url, params=parameter, timeout=7) as response: - if response.status == 200: - try: - data = (await response.json())["data"] - total_page = data["total_page"] - data = data["items"] - for _ in range(total_page): - for i in range(len(data)): - name = data[i]["name"] - price = data[i]["sell_reference_price"] - name_list.append(name) - price_list.append(price) - except Exception as e: - return "没有查询到...", 998 - else: - return "访问失败!", response.status + response = await AsyncHttpx.get(url, cookies=cookie, params=parameter) + if response.status_code == 200: + try: + data = response.json()["data"] + total_page = data["total_page"] + data = data["items"] + for _ in range(total_page): + for i in range(len(data)): + name = data[i]["name"] + price = data[i]["sell_reference_price"] + name_list.append(name) + price_list.append(price) + except Exception as e: + return "没有查询到...", 998 + else: + return "访问失败!", response.status_code except TimeoutError as e: return "访问超时! 请重试或稍后再试!", 997 - result = f"皮肤: {dname}({len(name_list)})\n" - # result = "皮肤: " + dname + "\n" + result = f"皮肤: {d_name}({len(name_list)})\n" for i in range(len(name_list)): result += name_list[i] + ": " + price_list[i] + "\n" return result[:-1], 999 diff --git a/plugins/parse_bilibili_json.py b/plugins/parse_bilibili_json.py old mode 100644 new mode 100755 index 34776f38..82172717 --- a/plugins/parse_bilibili_json.py +++ b/plugins/parse_bilibili_json.py @@ -11,9 +11,9 @@ from nonebot.adapters.cqhttp.exception import ActionFailed from utils.image_utils import CreateImg from utils.browser import get_browser from configs.path_config import IMAGE_PATH +from utils.http_utils import AsyncHttpx import asyncio import time -import aiohttp from bilibili_api import settings from utils.manager import group_manager import ujson as json @@ -51,17 +51,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if data: # 转发视频 if data.get("desc") == "哔哩哔哩": - async with aiohttp.ClientSession( - headers=get_user_agent() - ) as session: - async with session.get( - data["meta"]["detail_1"]["qqdocurl"], - proxy=get_local_proxy(), - timeout=7, - ) as response: - url = str(response.url).split("?")[0] - bvid = url.split("/")[-1] - vd_info = await video.Video(bvid=bvid).get_info() + response = await AsyncHttpx.get( + data["meta"]["detail_1"]["qqdocurl"], timeout=7 + ) + url = str(response.url).split("?")[0] + bvid = url.split("/")[-1] + vd_info = await video.Video(bvid=bvid).get_info() # 转发专栏 if ( data.get("meta") diff --git a/plugins/pid_search.py b/plugins/pid_search.py old mode 100644 new mode 100755 index 80e6701f..43bf19b8 --- a/plugins/pid_search.py +++ b/plugins/pid_search.py @@ -1,124 +1,106 @@ -from asyncio.exceptions import TimeoutError - -import aiofiles -import aiohttp -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent -from nonebot.typing import T_State - -from configs.path_config import IMAGE_PATH -from services.log import logger -from utils.message_builder import image -from utils.utils import get_message_text, is_number -from utils.manager import withdraw_message_manager - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -__zx_plugin_name__ = "pid搜索" -__plugin_usage__ = """ -usage: - 通过 pid 搜索图片 - 指令: - p搜 [pid] -""".strip() -__plugin_des__ = "通过 pid 搜索图片" -__plugin_cmd__ = ["p搜 [pid]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["p搜"], -} - -pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True) - -url = "https://api.fantasyzone.cc/tu/search.php" - - -@pid_search.args_parser -async def _(bot: Bot, event: MessageEvent, state: T_State): - pid = get_message_text(event.json()) - if pid: - if pid in ["取消", "算了"]: - await pid_search.finish("已取消操作...") - if not is_number(pid): - await pid_search.reject("笨蛋,重新输入数!字!", at_sender=True) - state["pid"] = pid - - -@pid_search.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - pid = get_message_text(event.json()) - if pid: - state["pid"] = pid - - -@pid_search.got("pid", prompt="需要查询的图片PID是?") -async def _(bot: Bot, event: MessageEvent, state: T_State): - pid = state["pid"] - params = { - "id": pid, - "p": 1, - } - async with aiohttp.ClientSession() as session: - for _ in range(10): - try: - async with session.get(url, timeout=2, params=params) as response: - data = json.loads(await response.text()) - except TimeoutError: - pass - else: - if not data["width"] and not data["height"]: - await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True) - pid = data["id"] - title = data["title"] - author = data["userName"] - author_id = data["userId"] - img_url = data["url"] - for _ in range(5): - try: - await download_pic(img_url, event.user_id) - except TimeoutError: - pass - else: - break - else: - await pid_search.finish("图片下载失败了....", at_sender=True) - tmp = "" - if isinstance(event, GroupMessageEvent): - tmp = "\n【注】将在30后撤回......" - msg_id = await pid_search.send( - Message( - f"title:{title}\n" - f"pid:{pid}\n" - f"author:{author}\n" - f"author_id:{author_id}\n" - f'{image(f"pid_search_{event.user_id}.png", "temp")}' - f"{tmp}" - ) - ) - logger.info( - f"(USER {event.user_id}, " - f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查询图片 PID:{pid}" - ) - if isinstance(event, GroupMessageEvent): - withdraw_message_manager.append((msg_id, 30)) - break - else: - await pid_search.finish("图片下载失败了....", at_sender=True) - - -async def download_pic(img_url: str, user_id: int): - async with aiohttp.ClientSession() as session: - async with session.get(img_url, timeout=2) as res: - async with aiofiles.open( - f"{IMAGE_PATH}/temp/pid_search_{user_id}.png", "wb" - ) as f: - await f.write(await res.read()) +from asyncio.exceptions import TimeoutError +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent +from nonebot.typing import T_State +from configs.path_config import TEMP_PATH +from services.log import logger +from utils.message_builder import image +from utils.utils import get_message_text, is_number +from utils.manager import withdraw_message_manager +from utils.http_utils import AsyncHttpx +from pathlib import Path + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +__zx_plugin_name__ = "pid搜索" +__plugin_usage__ = """ +usage: + 通过 pid 搜索图片 + 指令: + p搜 [pid] +""".strip() +__plugin_des__ = "通过 pid 搜索图片" +__plugin_cmd__ = ["p搜 [pid]"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["p搜"], +} + +pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True) + +url = "https://api.fantasyzone.cc/tu/search.php" + + +@pid_search.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid = get_message_text(event.json()) + if pid: + if pid in ["取消", "算了"]: + await pid_search.finish("已取消操作...") + if not is_number(pid): + await pid_search.reject("笨蛋,重新输入数!字!", at_sender=True) + state["pid"] = pid + + +@pid_search.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid = get_message_text(event.json()) + if pid: + state["pid"] = pid + + +@pid_search.got("pid", prompt="需要查询的图片PID是?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid = state["pid"] + params = { + "id": pid, + "p": 1, + } + for _ in range(10): + try: + data = (await AsyncHttpx.get(url, params=params, timeout=5)).json() + except TimeoutError: + pass + else: + if not data["width"] and not data["height"]: + await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True) + pid = data["id"] + title = data["title"] + author = data["userName"] + author_id = data["userId"] + img_url = data["url"] + if not await AsyncHttpx.download_file( + img_url, Path(TEMP_PATH) / f"pid_search_{event.user_id}.png" + ): + await pid_search.finish("图片下载失败了....", at_sender=True) + tmp = "" + if isinstance(event, GroupMessageEvent): + tmp = "\n【注】将在30后撤回......" + msg_id = await pid_search.send( + Message( + f"title:{title}\n" + f"pid:{pid}\n" + f"author:{author}\n" + f"author_id:{author_id}\n" + f'{image(f"pid_search_{event.user_id}.png", "temp")}' + f"{tmp}" + ) + ) + logger.info( + f"(USER {event.user_id}, " + f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查询图片 PID:{pid}" + ) + if isinstance(event, GroupMessageEvent): + withdraw_message_manager.append((msg_id, 30)) + break + else: + await pid_search.finish("图片下载失败了....", at_sender=True) diff --git a/plugins/pix_gallery/__init__.py b/plugins/pix_gallery/__init__.py old mode 100644 new mode 100755 index 15e42178..54bd7414 --- a/plugins/pix_gallery/__init__.py +++ b/plugins/pix_gallery/__init__.py @@ -1,62 +1,135 @@ -from configs.config import Config -import nonebot - - -__zx_plugin_name__ = "更新扩展图库Omega [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -Config.add_plugin_config( - "hibiapi", - "HIBIAPI", - "https://api.obfs.dev", - help_="如果没有自建或其他hibiapi请不要修改", - default_value="https://api.obfs.dev", -) -Config.add_plugin_config( - "pixiv", - "PIXIV_NGINX_URL", - "i.pixiv.re", - help_="Pixiv反向代理" -) -Config.add_plugin_config( - "pix", - "PIX_IMAGE_SIZE", - "master", - name="PIX图库", - help_="PIX图库下载的画质 可能的值:original:原图,master:缩略图(加快发送速度)", - default_value="master" -) -Config.add_plugin_config( - "pix", - "SEARCH_HIBIAPI_BOOKMARKS", - 5000, - help_="最低收藏,PIX使用HIBIAPI搜索图片时达到最低收藏才会添加至图库", - default_value=5000 -) -Config.add_plugin_config( - "pix", - "WITHDRAW_PIX_MESSAGE", - (0, 1), - help_="自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)", - default_value=(0, 1) -) -Config.add_plugin_config( - "pix", - "PIX_OMEGA_PIXIV_RATIO", - (10, 0), - help_="PIX图库 与 额外图库OmegaPixivIllusts 混合搜索的比例 参1:PIX图库 参2:OmegaPixivIllusts扩展图库(没有此图库请设置为0)", - default_value=(10, 0) -) -Config.add_plugin_config( - "pix", - "TIMEOUT", - 10, - help_="下载图片超时限制(秒)", - default_value=10 -) - -nonebot.load_plugins("plugins/pix_gallery") - - +from configs.config import Config +from services.log import logger +from .model.omega_pixiv_illusts import OmegaPixivIllusts +from pathlib import Path +from nonebot import Driver +from typing import List +from datetime import datetime +import nonebot +import asyncio +import os + + +__zx_plugin_name__ = "更新扩展图库Omega [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +Config.add_plugin_config( + "hibiapi", + "HIBIAPI", + "https://api.obfs.dev", + help_="如果没有自建或其他hibiapi请不要修改", + default_value="https://api.obfs.dev", +) +Config.add_plugin_config( + "pixiv", + "PIXIV_NGINX_URL", + "i.pixiv.re", + help_="Pixiv反向代理" +) +Config.add_plugin_config( + "pix", + "PIX_IMAGE_SIZE", + "master", + name="PIX图库", + help_="PIX图库下载的画质 可能的值:original:原图,master:缩略图(加快发送速度)", + default_value="master" +) +Config.add_plugin_config( + "pix", + "SEARCH_HIBIAPI_BOOKMARKS", + 5000, + help_="最低收藏,PIX使用HIBIAPI搜索图片时达到最低收藏才会添加至图库", + default_value=5000 +) +Config.add_plugin_config( + "pix", + "WITHDRAW_PIX_MESSAGE", + (0, 1), + help_="自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)", + default_value=(0, 1) +) +Config.add_plugin_config( + "pix", + "PIX_OMEGA_PIXIV_RATIO", + (10, 0), + help_="PIX图库 与 额外图库OmegaPixivIllusts 混合搜索的比例 参1:PIX图库 参2:OmegaPixivIllusts扩展图库(没有此图库请设置为0)", + default_value=(10, 0) +) +Config.add_plugin_config( + "pix", + "TIMEOUT", + 10, + help_="下载图片超时限制(秒)", + default_value=10 +) + +nonebot.load_plugins("plugins/pix_gallery") + +driver: Driver = nonebot.get_driver() + + +@driver.on_startup +async def _init_omega_pixiv_illusts(): + omega_pixiv_illusts = None + for file in os.listdir("."): + if "omega_pixiv_illusts" in file and ".sql" in file: + omega_pixiv_illusts = Path() / file + if omega_pixiv_illusts: + with open(omega_pixiv_illusts, "r", encoding="utf8") as f: + lines = f.readlines() + tasks = [] + length = len([x for x in lines if "INSERT INTO" in x.upper()]) + all_pid = await OmegaPixivIllusts.get_all_pid() + index = 0 + logger.info("检测到OmegaPixivIllusts数据库,准备开始更新....") + for line in lines: + if "INSERT INTO" in line.upper(): + index += 1 + tasks.append( + asyncio.ensure_future(_tasks(line, all_pid, length, index)) + ) + await asyncio.gather(*tasks) + omega_pixiv_illusts.unlink() + + +async def _tasks(line: str, all_pid: List[int], length: int, index: int): + data = line.split("VALUES", maxsplit=1)[-1].strip() + if data.startswith("("): + data = data[1:] + if data.endswith(");"): + data = data[:-2] + x = data.split(maxsplit=3) + pid = int(x[1][:-1].strip()) + if pid in all_pid: + logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}") + return + uid = int(x[2][:-1].strip()) + x = x[3].split(", '") + title = x[0].strip()[1:-1] + tmp = x[1].split(", ") + author = tmp[0].strip()[:-1] + nsfw_tag = int(tmp[1]) + width = int(tmp[2]) + height = int(tmp[3]) + tags = x[2][:-1] + url = x[3][:-1] + if await OmegaPixivIllusts.add_image_data( + pid, + title, + width, + height, + url, + uid, + author, + nsfw_tag, + tags, + datetime.min, + datetime.min, + ): + logger.info( + f"成功添加OmegaPixivIllusts图库数据 pid:{pid} 本次预计存储 {length} 张,已更新第 {index} 张" + ) + else: + logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}") diff --git a/plugins/pix_gallery/data_source.py b/plugins/pix_gallery/data_source.py old mode 100644 new mode 100755 index a85e8bde..02788b8b --- a/plugins/pix_gallery/data_source.py +++ b/plugins/pix_gallery/data_source.py @@ -1,452 +1,392 @@ -from aiohttp.client_exceptions import ( - ClientOSError, - ServerDisconnectedError, - ClientConnectorError, -) -from asyncpg.exceptions import UniqueViolationError -from .model.omega_pixiv_illusts import OmegaPixivIllusts -from asyncio.locks import Semaphore -from aiohttp import ClientPayloadError -from aiohttp.client import ClientSession -from asyncio.exceptions import TimeoutError -from .model.pixiv import Pixiv -from typing import List -from utils.utils import get_local_proxy, change_picture_links -from utils.image_utils import CreateImg -from services.log import logger -from configs.config import Config -from configs.path_config import TEMP_PATH -import platform -import aiohttp -import asyncio -import aiofiles -import math - -try: - import ujson as json -except ModuleNotFoundError: - import json - -if str(platform.system()).lower() == "windows": - policy = asyncio.WindowsSelectorEventLoopPolicy() - asyncio.set_event_loop_policy(policy) - -headers = { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" - " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", - "Referer": "https://www.pixiv.net", -} - -HIBIAPI = None - - -async def start_update_image_url( - current_keyword: List[str], black_pid: List[str] -) -> "int, int": - """ - 开始更新图片url - :param current_keyword: 关键词 - :param black_pid: 黑名单pid - :return: pid数量和图片数量 - """ - global HIBIAPI - pid_count = 0 - pic_count = 0 - tasks = [] - semaphore = asyncio.Semaphore(10) - if not HIBIAPI: - HIBIAPI = Config.get_config("hibiapi", "HIBIAPI") - HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI - async with aiohttp.ClientSession(headers=headers) as session: - for keyword in current_keyword: - for page in range(1, 110): - if keyword.startswith("uid:"): - url = f"{HIBIAPI}/api/pixiv/member_illust" - params = {"id": keyword[4:], "page": page} - if page == 30: - break - elif keyword.startswith("pid:"): - url = f"{HIBIAPI}/api/pixiv/illust" - params = {"id": keyword[4:]} - else: - url = f"{HIBIAPI}/api/pixiv/search" - params = {"word": keyword, "page": page} - tasks.append( - asyncio.ensure_future( - search_image( - url, keyword, params, semaphore, session, page, black_pid - ) - ) - ) - if keyword.startswith("pid:"): - break - result = await asyncio.gather(*tasks) - for x in result: - pid_count += x[0] - pic_count += x[1] - return pid_count, pic_count - - -async def search_image( - url: str, - keyword: str, - params: dict, - semaphore: Semaphore, - session: ClientSession, - page: int = 1, - black: List[str] = None, -) -> "int, int": - """ - 搜索图片 - :param url: 搜索url - :param keyword: 关键词 - :param params: params参数 - :param semaphore: semaphore - :param session: session - :param page: 页面 - :param black: pid黑名单 - :return: pid数量和图片数量 - """ - tmp_pid = [] - pic_count = 0 - pid_count = 0 - async with semaphore: - try: - async with session.get( - url, - params=params, - proxy=get_local_proxy(), - ) as response: - data = await response.json() - if ( - not data - or data.get("error") - or (not data.get("illusts") and not data.get("illust")) - ): - return 0, 0 - if url != f"{HIBIAPI}/api/pixiv/illust": - logger.info(f'{keyword}: 获取数据成功...数据总量:{len(data["illusts"])}') - data = data["illusts"] - else: - logger.info(f'获取数据成功...PID:{params.get("id")}') - data = [data["illust"]] - img_data = {} - for x in data: - pid = x["id"] - title = x["title"] - width = x["width"] - height = x["height"] - view = x["total_view"] - bookmarks = x["total_bookmarks"] - uid = x["user"]["id"] - author = x["user"]["name"] - tags = [] - for tag in x["tags"]: - for i in tag: - if tag[i]: - tags.append(tag[i]) - img_urls = [] - if x["page_count"] == 1: - img_urls.append(x["meta_single_page"]["original_image_url"]) - else: - for urls in x["meta_pages"]: - img_urls.append(urls["image_urls"]["original"]) - if ( - ( - bookmarks - >= Config.get_config("pix", "SEARCH_HIBIAPI_BOOKMARKS") - or ( - url == f"{HIBIAPI}/api/pixiv/member_illust" - and bookmarks >= 1500 - ) - or (url == f"{HIBIAPI}/api/pixiv/illust") - ) - and len(img_urls) < 10 - and _check_black(img_urls, black) - ): - img_data[pid] = { - "pid": pid, - "title": title, - "width": width, - "height": height, - "view": view, - "bookmarks": bookmarks, - "img_urls": img_urls, - "uid": uid, - "author": author, - "tags": tags, - } - else: - continue - for x in img_data.keys(): - data = img_data[x] - for img_url in data["img_urls"]: - img_p = img_url[img_url.rfind("_") + 1 : img_url.rfind(".")] - try: - if await Pixiv.add_image_data( - data["pid"], - data["title"], - data["width"], - data["height"], - data["view"], - data["bookmarks"], - img_url, - img_p, - data["uid"], - data["author"], - ",".join(data["tags"]), - ): - if data["pid"] not in tmp_pid: - pid_count += 1 - tmp_pid.append(data["pid"]) - pic_count += 1 - logger.info(f'存储图片PID:{data["pid"]} IMG_P:{img_p}') - except UniqueViolationError: - logger.warning(f'{data["pid"]} | {img_url} 已存在...') - except (ServerDisconnectedError, ClientConnectorError, ClientOSError): - logger.warning("搜索图片服务被关闭,再次调用....") - await search_image(url, keyword, params, semaphore, session, page, black) - return pid_count, pic_count - - -# 下载图片 -async def download_image(img_url: str, session: ClientSession, _count: int = 1): - """ - 下载图片 - :param img_url: 图片url - :param session: session - :param _count: 次数 - """ - try: - async with session.get(img_url, proxy=get_local_proxy()) as response: - logger.info(f"下载图片 --> {img_url}") - async with aiofiles.open(f'tmp/{img_url.split("/")[-1]}', "wb") as f: - await f.write(await response.read()) - except ServerDisconnectedError: - logger.warning(f"下载图片服务被关闭,第 {_count} 次调用....") - await download_image(img_url, session, _count + 1) - except ClientOSError: - logger.warning(f"远程连接被关闭,第 {_count} 次调用....") - ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") - if ws_url: - img_url = img_url.replace("i.pximg.net", ws_url) - await download_image(img_url, session, _count + 1) - except TimeoutError: - logger.warning(f"下载或写入超时,第 {_count} 次调用....") - await download_image(img_url, session, _count + 1) - except ClientPayloadError: - pass - - -async def get_image(img_url: str, user_id: int) -> str: - """ - 下载图片 - :param img_url: - :param user_id: - :return: 图片名称 - """ - global HIBIAPI - if not HIBIAPI: - HIBIAPI = Config.get_config("hibiapi", "HIBIAPI") - HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI - async with aiohttp.ClientSession(headers=headers) as session: - if "https://www.pixiv.net/artworks" in img_url: - pid = img_url.rsplit("/", maxsplit=1)[-1] - params = {"id": pid} - for _ in range(3): - try: - async with session.get( - f"{HIBIAPI}/api/pixiv/illust", - params=params, - proxy=get_local_proxy(), - ) as response: - if response.status == 200: - data = await response.json() - if data.get("illust"): - if data["illust"]["page_count"] == 1: - img_url = data["illust"]["meta_single_page"][ - "original_image_url" - ] - else: - img_url = data["illust"]["meta_pages"][0][ - "image_urls" - ]["original"] - break - except (ClientConnectorError, TimeoutError): - pass - old_img_url = img_url - img_url = change_picture_links( - img_url, Config.get_config("pix", "PIX_IMAGE_SIZE") - ) - ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") - if ws_url: - if ws_url.startswith("http"): - ws_url = ws_url.split("//")[-1] - img_url = img_url.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url) - for _ in range(3): - try: - async with session.get( - img_url, - proxy=get_local_proxy(), - timeout=Config.get_config("pix", "TIMEOUT"), - ) as response: - if response.status == 404: - img_url = old_img_url - continue - async with aiofiles.open( - f"{TEMP_PATH}/pix_{user_id}_{img_url[-10:-4]}.jpg", "wb" - ) as f: - await f.write(await response.read()) - return f"pix_{user_id}_{img_url[-10:-4]}.jpg" - except (ClientConnectorError, TimeoutError): - pass - - -async def uid_pid_exists(id_: str) -> bool: - """ - 检测 pid/uid 是否有效 - :param id_: pid/uid - """ - if id_.startswith("uid:"): - url = f"{HIBIAPI}/api/pixiv/member" - elif id_.startswith("pid:"): - url = f"{HIBIAPI}/api/pixiv/illust" - else: - return False - params = {"id": int(id_[4:])} - async with aiohttp.ClientSession(headers=headers) as session: - async with session.get(url, params=params, proxy=get_local_proxy()) as response: - data = await response.json() - if data.get("error"): - return False - return True - - -async def get_keyword_num(keyword: str) -> "int, int, int, int, int": - """ - 查看图片相关 tag 数量 - :param keyword: 关键词tag - """ - count, r18_count = await Pixiv.get_keyword_num(keyword.split()) - count_, setu_count, r18_count_ = await OmegaPixivIllusts.get_keyword_num( - keyword.split() - ) - return count, r18_count, count_, setu_count, r18_count_ - - -async def remove_image(pid: int, img_p: str) -> bool: - """ - 删除置顶图片 - :param pid: pid - :param img_p: 图片 p 如 p0,p1 等 - """ - if img_p: - if "p" not in img_p: - img_p = f"p{img_p}" - return await Pixiv.remove_image_data(pid, img_p) - - -def gen_keyword_pic( - _pass_keyword: List[str], not_pass_keyword: List[str], is_superuser: bool -): - """ - 已通过或未通过的所有关键词/uid/pid - :param _pass_keyword: 通过列表 - :param not_pass_keyword: 未通过列表 - :param is_superuser: 是否超级用户 - """ - _keyword = [ - x - for x in _pass_keyword - if not x.startswith("uid:") - and not x.startswith("pid:") - and not x.startswith("black:") - ] - _uid = [x for x in _pass_keyword if x.startswith("uid:")] - _pid = [x for x in _pass_keyword if x.startswith("pid:")] - _n_keyword = [ - x - for x in not_pass_keyword - if not x.startswith("uid:") - and not x.startswith("pid:") - and not x.startswith("black:") - ] - _n_uid = [ - x - for x in not_pass_keyword - if x.startswith("uid:") and not x.startswith("black:") - ] - _n_pid = [ - x - for x in not_pass_keyword - if x.startswith("pid:") and not x.startswith("black:") - ] - img_width = 0 - img_data = { - "_keyword": {"width": 0, "data": _keyword}, - "_uid": {"width": 0, "data": _uid}, - "_pid": {"width": 0, "data": _pid}, - "_n_keyword": {"width": 0, "data": _n_keyword}, - "_n_uid": {"width": 0, "data": _n_uid}, - "_n_pid": {"width": 0, "data": _n_pid}, - } - for x in list(img_data.keys()): - img_data[x]["width"] = math.ceil(len(img_data[x]["data"]) / 40) - img_width += img_data[x]["width"] * 200 - if not is_superuser: - img_width = ( - img_width - - ( - img_data["_n_keyword"]["width"] - + img_data["_n_uid"]["width"] - + img_data["_n_pid"]["width"] - ) - * 200 - ) - del img_data["_n_keyword"] - del img_data["_n_pid"] - del img_data["_n_uid"] - current_width = 0 - A = CreateImg(img_width, 1100) - for x in list(img_data.keys()): - if img_data[x]["data"]: - img = CreateImg(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40) - start_index = 0 - end_index = 40 - total_index = img_data[x]["width"] * 40 - for _ in range(img_data[x]["width"]): - tmp = CreateImg(198, 1100, font_size=20) - text_img = CreateImg(198, 100, font_size=50) - key_str = "\n".join( - [key for key in img_data[x]["data"][start_index:end_index]] - ) - tmp.text((10, 100), key_str) - if x.find("_n") == -1: - text_img.text((24, 24), "已收录") - else: - text_img.text((24, 24), "待收录") - tmp.paste(text_img, (0, 0)) - start_index += 40 - end_index = ( - end_index + 40 if end_index + 40 <= total_index else total_index - ) - background_img = CreateImg(200, 1100, color="#FFE4C4") - background_img.paste(tmp, (1, 1)) - img.paste(background_img) - A.paste(img, (current_width, 0)) - current_width += img_data[x]["width"] * 200 - return A.pic2bs4() - - -def _check_black(img_urls: List[str], black: List[str]) -> bool: - """ - 检测pid是否在黑名单中 - :param img_urls: 图片img列表 - :param black: 黑名单 - :return: - """ - for b in black: - for img_url in img_urls: - if b in img_url: - return False - return True +from asyncpg.exceptions import UniqueViolationError +from .model.omega_pixiv_illusts import OmegaPixivIllusts +from asyncio.locks import Semaphore +from asyncio.exceptions import TimeoutError +from .model.pixiv import Pixiv +from typing import List, Optional +from utils.utils import change_pixiv_image_links +from utils.image_utils import CreateImg +from utils.http_utils import AsyncHttpx +from services.log import logger +from configs.config import Config +from configs.path_config import TEMP_PATH +import aiofiles +import platform +import asyncio +import math + +try: + import ujson as json +except ModuleNotFoundError: + import json + +if str(platform.system()).lower() == "windows": + policy = asyncio.WindowsSelectorEventLoopPolicy() + asyncio.set_event_loop_policy(policy) + +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} + +HIBIAPI = None + + +async def start_update_image_url( + current_keyword: List[str], black_pid: List[str] +) -> "int, int": + """ + 开始更新图片url + :param current_keyword: 关键词 + :param black_pid: 黑名单pid + :return: pid数量和图片数量 + """ + global HIBIAPI + pid_count = 0 + pic_count = 0 + tasks = [] + semaphore = asyncio.Semaphore(10) + if not HIBIAPI: + HIBIAPI = Config.get_config("hibiapi", "HIBIAPI") + HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] else HIBIAPI + for keyword in current_keyword: + for page in range(1, 110): + if keyword.startswith("uid:"): + url = f"{HIBIAPI}/api/pixiv/member_illust" + params = {"id": keyword[4:], "page": page} + if page == 30: + break + elif keyword.startswith("pid:"): + url = f"{HIBIAPI}/api/pixiv/illust" + params = {"id": keyword[4:]} + else: + url = f"{HIBIAPI}/api/pixiv/search" + params = {"word": keyword, "page": page} + tasks.append( + asyncio.ensure_future( + search_image( + url, keyword, params, semaphore, page, black_pid + ) + ) + ) + if keyword.startswith("pid:"): + break + result = await asyncio.gather(*tasks) + for x in result: + pid_count += x[0] + pic_count += x[1] + return pid_count, pic_count + + +async def search_image( + url: str, + keyword: str, + params: dict, + semaphore: Semaphore, + page: int = 1, + black: List[str] = None, +) -> "int, int": + """ + 搜索图片 + :param url: 搜索url + :param keyword: 关键词 + :param params: params参数 + :param semaphore: semaphore + :param page: 页面 + :param black: pid黑名单 + :return: pid数量和图片数量 + """ + tmp_pid = [] + pic_count = 0 + pid_count = 0 + async with semaphore: + try: + data = (await AsyncHttpx.get(url, params=params)).json() + if ( + not data + or data.get("error") + or (not data.get("illusts") and not data.get("illust")) + ): + return 0, 0 + if url != f"{HIBIAPI}/api/pixiv/illust": + logger.info(f'{keyword}: 获取数据成功...数据总量:{len(data["illusts"])}') + data = data["illusts"] + else: + logger.info(f'获取数据成功...PID:{params.get("id")}') + data = [data["illust"]] + img_data = {} + for x in data: + pid = x["id"] + title = x["title"] + width = x["width"] + height = x["height"] + view = x["total_view"] + bookmarks = x["total_bookmarks"] + uid = x["user"]["id"] + author = x["user"]["name"] + tags = [] + for tag in x["tags"]: + for i in tag: + if tag[i]: + tags.append(tag[i]) + img_urls = [] + if x["page_count"] == 1: + img_urls.append(x["meta_single_page"]["original_image_url"]) + else: + for urls in x["meta_pages"]: + img_urls.append(urls["image_urls"]["original"]) + if ( + ( + bookmarks + >= Config.get_config("pix", "SEARCH_HIBIAPI_BOOKMARKS") + or ( + url == f"{HIBIAPI}/api/pixiv/member_illust" + and bookmarks >= 1500 + ) + or (url == f"{HIBIAPI}/api/pixiv/illust") + ) + and len(img_urls) < 10 + and _check_black(img_urls, black) + ): + img_data[pid] = { + "pid": pid, + "title": title, + "width": width, + "height": height, + "view": view, + "bookmarks": bookmarks, + "img_urls": img_urls, + "uid": uid, + "author": author, + "tags": tags, + } + else: + continue + for x in img_data.keys(): + data = img_data[x] + for img_url in data["img_urls"]: + img_p = img_url[img_url.rfind("_") + 1 : img_url.rfind(".")] + try: + if await Pixiv.add_image_data( + data["pid"], + data["title"], + data["width"], + data["height"], + data["view"], + data["bookmarks"], + img_url, + img_p, + data["uid"], + data["author"], + ",".join(data["tags"]), + ): + if data["pid"] not in tmp_pid: + pid_count += 1 + tmp_pid.append(data["pid"]) + pic_count += 1 + logger.info(f'存储图片PID:{data["pid"]} IMG_P:{img_p}') + except UniqueViolationError: + logger.warning(f'{data["pid"]} | {img_url} 已存在...') + except Exception as e: + logger.warning(f"PIX在线搜索图片错误,已再次调用 {type(e)}:{e}") + await search_image(url, keyword, params, semaphore, page, black) + return pid_count, pic_count + + +async def get_image(img_url: str, user_id: int) -> Optional[str]: + """ + 下载图片 + :param img_url: + :param user_id: + :return: 图片名称 + """ + if "https://www.pixiv.net/artworks" in img_url: + pid = img_url.rsplit("/", maxsplit=1)[-1] + params = {"id": pid} + for _ in range(3): + try: + response = await AsyncHttpx.get(f"{HIBIAPI}/api/pixiv/illust", params=params) + if response.status_code == 200: + data = response.json() + if data.get("illust"): + if data["illust"]["page_count"] == 1: + img_url = data["illust"]["meta_single_page"][ + "original_image_url" + ] + else: + img_url = data["illust"]["meta_pages"][0][ + "image_urls" + ]["original"] + break + except TimeoutError: + pass + old_img_url = img_url + img_url = change_pixiv_image_links( + img_url, Config.get_config("pix", "PIX_IMAGE_SIZE"), Config.get_config("pixiv", "PIXIV_NGINX_URL") + ) + old_img_url = change_pixiv_image_links( + old_img_url, None, Config.get_config("pixiv", "PIXIV_NGINX_URL") + ) + for _ in range(3): + try: + response = await AsyncHttpx.get(img_url, timeout=Config.get_config("pix", "TIMEOUT"),) + if response.status_code == 404: + img_url = old_img_url + continue + async with aiofiles.open( + f"{TEMP_PATH}/pix_{user_id}_{img_url[-10:-4]}.jpg", "wb" + ) as f: + await f.write(response.content) + return f"pix_{user_id}_{img_url[-10:-4]}.jpg" + except TimeoutError: + pass + return None + + +async def uid_pid_exists(id_: str) -> bool: + """ + 检测 pid/uid 是否有效 + :param id_: pid/uid + """ + if id_.startswith("uid:"): + url = f"{HIBIAPI}/api/pixiv/member" + elif id_.startswith("pid:"): + url = f"{HIBIAPI}/api/pixiv/illust" + else: + return False + params = {"id": int(id_[4:])} + data = (await AsyncHttpx.get(url, params=params)).json() + if data.get("error"): + return False + return True + + +async def get_keyword_num(keyword: str) -> "int, int, int, int, int": + """ + 查看图片相关 tag 数量 + :param keyword: 关键词tag + """ + count, r18_count = await Pixiv.get_keyword_num(keyword.split()) + count_, setu_count, r18_count_ = await OmegaPixivIllusts.get_keyword_num( + keyword.split() + ) + return count, r18_count, count_, setu_count, r18_count_ + + +async def remove_image(pid: int, img_p: str) -> bool: + """ + 删除置顶图片 + :param pid: pid + :param img_p: 图片 p 如 p0,p1 等 + """ + if img_p: + if "p" not in img_p: + img_p = f"p{img_p}" + return await Pixiv.remove_image_data(pid, img_p) + + +def gen_keyword_pic( + _pass_keyword: List[str], not_pass_keyword: List[str], is_superuser: bool +): + """ + 已通过或未通过的所有关键词/uid/pid + :param _pass_keyword: 通过列表 + :param not_pass_keyword: 未通过列表 + :param is_superuser: 是否超级用户 + """ + _keyword = [ + x + for x in _pass_keyword + if not x.startswith("uid:") + and not x.startswith("pid:") + and not x.startswith("black:") + ] + _uid = [x for x in _pass_keyword if x.startswith("uid:")] + _pid = [x for x in _pass_keyword if x.startswith("pid:")] + _n_keyword = [ + x + for x in not_pass_keyword + if not x.startswith("uid:") + and not x.startswith("pid:") + and not x.startswith("black:") + ] + _n_uid = [ + x + for x in not_pass_keyword + if x.startswith("uid:") and not x.startswith("black:") + ] + _n_pid = [ + x + for x in not_pass_keyword + if x.startswith("pid:") and not x.startswith("black:") + ] + img_width = 0 + img_data = { + "_keyword": {"width": 0, "data": _keyword}, + "_uid": {"width": 0, "data": _uid}, + "_pid": {"width": 0, "data": _pid}, + "_n_keyword": {"width": 0, "data": _n_keyword}, + "_n_uid": {"width": 0, "data": _n_uid}, + "_n_pid": {"width": 0, "data": _n_pid}, + } + for x in list(img_data.keys()): + img_data[x]["width"] = math.ceil(len(img_data[x]["data"]) / 40) + img_width += img_data[x]["width"] * 200 + if not is_superuser: + img_width = ( + img_width + - ( + img_data["_n_keyword"]["width"] + + img_data["_n_uid"]["width"] + + img_data["_n_pid"]["width"] + ) + * 200 + ) + del img_data["_n_keyword"] + del img_data["_n_pid"] + del img_data["_n_uid"] + current_width = 0 + A = CreateImg(img_width, 1100) + for x in list(img_data.keys()): + if img_data[x]["data"]: + img = CreateImg(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40) + start_index = 0 + end_index = 40 + total_index = img_data[x]["width"] * 40 + for _ in range(img_data[x]["width"]): + tmp = CreateImg(198, 1100, font_size=20) + text_img = CreateImg(198, 100, font_size=50) + key_str = "\n".join( + [key for key in img_data[x]["data"][start_index:end_index]] + ) + tmp.text((10, 100), key_str) + if x.find("_n") == -1: + text_img.text((24, 24), "已收录") + else: + text_img.text((24, 24), "待收录") + tmp.paste(text_img, (0, 0)) + start_index += 40 + end_index = ( + end_index + 40 if end_index + 40 <= total_index else total_index + ) + background_img = CreateImg(200, 1100, color="#FFE4C4") + background_img.paste(tmp, (1, 1)) + img.paste(background_img) + A.paste(img, (current_width, 0)) + current_width += img_data[x]["width"] * 200 + return A.pic2bs4() + + +def _check_black(img_urls: List[str], black: List[str]) -> bool: + """ + 检测pid是否在黑名单中 + :param img_urls: 图片img列表 + :param black: 黑名单 + :return: + """ + for b in black: + for img_url in img_urls: + if b in img_url: + return False + return True diff --git a/plugins/pix_gallery/model/__init__.py b/plugins/pix_gallery/model/__init__.py old mode 100644 new mode 100755 index e3ad2b4b..f16c3e52 --- a/plugins/pix_gallery/model/__init__.py +++ b/plugins/pix_gallery/model/__init__.py @@ -1,3 +1,3 @@ -from .pixiv_keyword_user import * -from .omega_pixiv_illusts import * -from .pixiv import * +from .pixiv_keyword_user import * +from .omega_pixiv_illusts import * +from .pixiv import * diff --git a/plugins/pix_gallery/model/omega_pixiv_illusts.py b/plugins/pix_gallery/model/omega_pixiv_illusts.py old mode 100644 new mode 100755 index cd213853..b9df7fce --- a/plugins/pix_gallery/model/omega_pixiv_illusts.py +++ b/plugins/pix_gallery/model/omega_pixiv_illusts.py @@ -1,141 +1,141 @@ -from typing import Optional, List -from datetime import datetime -from services.db_context import db - - -class OmegaPixivIllusts(db.Model): - __tablename__ = "omega_pixiv_illusts" - __table_args__ = {'extend_existing': True} - - id = db.Column(db.Integer(), primary_key=True) - pid = db.Column(db.BigInteger(), nullable=False) - uid = db.Column(db.BigInteger(), nullable=False) - title = db.Column(db.String(), nullable=False) - uname = db.Column(db.String(), nullable=False) - nsfw_tag = db.Column(db.Integer(), nullable=False) - width = db.Column(db.Integer(), nullable=False) - height = db.Column(db.Integer(), nullable=False) - tags = db.Column(db.String(), nullable=False) - url = db.Column(db.String(), nullable=False) - created_at = db.Column(db.DateTime(timezone=True)) - updated_at = db.Column(db.DateTime(timezone=True)) - - _idx1 = db.Index("omega_pixiv_illusts_idx1", "pid", "url", unique=True) - - @classmethod - async def add_image_data( - cls, - pid: int, - title: str, - width: int, - height: int, - url: str, - uid: int, - uname: str, - nsfw_tag: int, - tags: str, - created_at: datetime, - updated_at: datetime, - ): - """ - 说明: - 添加图片信息 - 参数: - :param pid: pid - :param title: 标题 - :param width: 宽度 - :param height: 长度 - :param url: url链接 - :param uid: 作者uid - :param uname: 作者名称 - :param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18 - :param tags: 相关tag - :param created_at: 创建日期 - :param updated_at: 更新日期 - """ - if not await cls.check_exists(pid): - await cls.create( - pid=pid, - title=title, - width=width, - height=height, - url=url, - uid=uid, - uname=uname, - nsfw_tag=nsfw_tag, - tags=tags, - ) - return True - return False - - @classmethod - async def query_images( - cls, - keywords: Optional[List[str]] = None, - uid: Optional[int] = None, - pid: Optional[int] = None, - nsfw_tag: Optional[int] = 0, - num: int = 100 - ) -> List[Optional["OmegaPixivIllusts"]]: - """ - 说明: - 查找符合条件的图片 - 参数: - :param keywords: 关键词 - :param uid: 画师uid - :param pid: 图片pid - :param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18 - :param num: 获取图片数量 - """ - if nsfw_tag is not None: - query = cls.query.where(cls.nsfw_tag == nsfw_tag) - else: - query = cls.query - if keywords: - for keyword in keywords: - query = query.where(cls.tags.contains(keyword)) - elif uid: - query = query.where(cls.uid == uid) - elif pid: - query = query.where(cls.uid == pid) - query = query.order_by(db.func.random()).limit(num) - return await query.gino.all() - - @classmethod - async def check_exists(cls, pid: int) -> bool: - """ - 说明: - 检测pid是否已存在 - 参数: - :param pid: 图片PID - """ - query = await cls.query.where(cls.pid == pid).gino.all() - return bool(query) - - @classmethod - async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int": - """ - 说明: - 获取相关关键词(keyword, tag)在图库中的数量 - 参数: - :param tags: 关键词/Tag - """ - setattr(OmegaPixivIllusts, 'count', db.func.count(cls.pid).label('count')) - query = cls.select('count') - if tags: - for tag in tags: - query = query.where(cls.tags.contains(tag)) - count = await query.where(cls.nsfw_tag == 0).gino.first() - setu_count = await query.where(cls.nsfw_tag == 1).gino.first() - r18_count = await query.where(cls.nsfw_tag == 2).gino.first() - return count[0], setu_count[0], r18_count[0] - - @classmethod - async def get_all_pid(cls) -> List[int]: - """ - 说明: - 获取所有图片PID - """ - data = await cls.select('pid').gino.all() - return [x[0] for x in data] - +from typing import Optional, List +from datetime import datetime +from services.db_context import db + + +class OmegaPixivIllusts(db.Model): + __tablename__ = "omega_pixiv_illusts" + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer(), primary_key=True) + pid = db.Column(db.BigInteger(), nullable=False) + uid = db.Column(db.BigInteger(), nullable=False) + title = db.Column(db.String(), nullable=False) + uname = db.Column(db.String(), nullable=False) + nsfw_tag = db.Column(db.Integer(), nullable=False) + width = db.Column(db.Integer(), nullable=False) + height = db.Column(db.Integer(), nullable=False) + tags = db.Column(db.String(), nullable=False) + url = db.Column(db.String(), nullable=False) + created_at = db.Column(db.DateTime(timezone=True)) + updated_at = db.Column(db.DateTime(timezone=True)) + + _idx1 = db.Index("omega_pixiv_illusts_idx1", "pid", "url", unique=True) + + @classmethod + async def add_image_data( + cls, + pid: int, + title: str, + width: int, + height: int, + url: str, + uid: int, + uname: str, + nsfw_tag: int, + tags: str, + created_at: datetime, + updated_at: datetime, + ): + """ + 说明: + 添加图片信息 + 参数: + :param pid: pid + :param title: 标题 + :param width: 宽度 + :param height: 长度 + :param url: url链接 + :param uid: 作者uid + :param uname: 作者名称 + :param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18 + :param tags: 相关tag + :param created_at: 创建日期 + :param updated_at: 更新日期 + """ + if not await cls.check_exists(pid): + await cls.create( + pid=pid, + title=title, + width=width, + height=height, + url=url, + uid=uid, + uname=uname, + nsfw_tag=nsfw_tag, + tags=tags, + ) + return True + return False + + @classmethod + async def query_images( + cls, + keywords: Optional[List[str]] = None, + uid: Optional[int] = None, + pid: Optional[int] = None, + nsfw_tag: Optional[int] = 0, + num: int = 100 + ) -> List[Optional["OmegaPixivIllusts"]]: + """ + 说明: + 查找符合条件的图片 + 参数: + :param keywords: 关键词 + :param uid: 画师uid + :param pid: 图片pid + :param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18 + :param num: 获取图片数量 + """ + if nsfw_tag is not None: + query = cls.query.where(cls.nsfw_tag == nsfw_tag) + else: + query = cls.query + if keywords: + for keyword in keywords: + query = query.where(cls.tags.contains(keyword)) + elif uid: + query = query.where(cls.uid == uid) + elif pid: + query = query.where(cls.uid == pid) + query = query.order_by(db.func.random()).limit(num) + return await query.gino.all() + + @classmethod + async def check_exists(cls, pid: int) -> bool: + """ + 说明: + 检测pid是否已存在 + 参数: + :param pid: 图片PID + """ + query = await cls.query.where(cls.pid == pid).gino.all() + return bool(query) + + @classmethod + async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int": + """ + 说明: + 获取相关关键词(keyword, tag)在图库中的数量 + 参数: + :param tags: 关键词/Tag + """ + setattr(OmegaPixivIllusts, 'count', db.func.count(cls.pid).label('count')) + query = cls.select('count') + if tags: + for tag in tags: + query = query.where(cls.tags.contains(tag)) + count = await query.where(cls.nsfw_tag == 0).gino.first() + setu_count = await query.where(cls.nsfw_tag == 1).gino.first() + r18_count = await query.where(cls.nsfw_tag == 2).gino.first() + return count[0], setu_count[0], r18_count[0] + + @classmethod + async def get_all_pid(cls) -> List[int]: + """ + 说明: + 获取所有图片PID + """ + data = await cls.select('pid').gino.all() + return [x[0] for x in data] + diff --git a/plugins/pix_gallery/model/pixiv.py b/plugins/pix_gallery/model/pixiv.py old mode 100644 new mode 100755 index 15275d3e..e43b9c35 --- a/plugins/pix_gallery/model/pixiv.py +++ b/plugins/pix_gallery/model/pixiv.py @@ -1,170 +1,170 @@ -from typing import Optional, List -from services.db_context import db - - -class Pixiv(db.Model): - __tablename__ = "pixiv" - __table_args__ = {'extend_existing': True} - - id = db.Column(db.Integer(), primary_key=True) - pid = db.Column(db.BigInteger(), nullable=False) - title = db.Column(db.String(), nullable=False) - width = db.Column(db.Integer(), nullable=False) - height = db.Column(db.Integer(), nullable=False) - view = db.Column(db.Integer(), nullable=False) - bookmarks = db.Column(db.Integer(), nullable=False) - img_url = db.Column(db.String(), nullable=False) - img_p = db.Column(db.String(), nullable=False) - uid = db.Column(db.BigInteger(), nullable=False) - author = db.Column(db.String(), nullable=False) - is_r18 = db.Column(db.Boolean(), nullable=False) - tags = db.Column(db.String(), nullable=False) - - _idx1 = db.Index("pixiv_idx1", "pid", "img_url", unique=True) - - @classmethod - async def add_image_data( - cls, - pid: int, - title: str, - width: int, - height: int, - view: int, - bookmarks: int, - img_url: str, - img_p: str, - uid: int, - author: str, - tags: str, - ): - """ - 说明: - 添加图片信息 - 参数: - :param pid: pid - :param title: 标题 - :param width: 宽度 - :param height: 长度 - :param view: 被查看次数 - :param bookmarks: 收藏数 - :param img_url: url链接 - :param img_p: 张数 - :param uid: 作者uid - :param author: 作者名称 - :param tags: 相关tag - """ - if not await cls.check_exists(pid, img_p): - await cls.create( - pid=pid, - title=title, - width=width, - height=height, - view=view, - bookmarks=bookmarks, - img_url=img_url, - img_p=img_p, - uid=uid, - author=author, - is_r18=True if "R-18" in tags else False, - tags=tags, - ) - return True - return False - - @classmethod - async def remove_image_data(cls, pid: int, img_p: str) -> bool: - """ - 说明: - 删除图片数据 - 参数: - :param pid: 图片pid - :param img_p: 图片pid的张数,如:p0,p1 - """ - try: - if img_p: - await cls.delete.where( - (cls.pid == pid) & (cls.img_p == img_p) - ).gino.status() - else: - await cls.delete.where(cls.pid == pid).gino.status() - return True - except Exception: - return False - - @classmethod - async def get_all_pid(cls) -> List[int]: - """ - 说明: - 获取所有PID - """ - query = await cls.query.select("pid").gino.first() - pid = [x[0] for x in query] - return list(set(pid)) - - # 0:非r18 1:r18 2:混合 - @classmethod - async def query_images( - cls, - keywords: Optional[List[str]] = None, - uid: Optional[int] = None, - pid: Optional[int] = None, - r18: Optional[int] = 0, - num: int = 100 - ) -> List[Optional["Pixiv"]]: - """ - 说明: - 查找符合条件的图片 - 参数: - :param keywords: 关键词 - :param uid: 画师uid - :param pid: 图片pid - :param r18: 是否r18,0:非r18 1:r18 2:混合 - :param num: 查找图片的数量 - """ - if r18 == 0: - query = cls.query.where(cls.is_r18 == False) - elif r18 == 1: - query = cls.query.where(cls.is_r18 == True) - else: - query = cls.query - if keywords: - for keyword in keywords: - query = query.where(cls.tags.contains(keyword)) - elif uid: - query = query.where(cls.uid == uid) - elif pid: - query = query.where(cls.pid == pid) - query = query.order_by(db.func.random()).limit(num) - return await query.gino.all() - - @classmethod - async def check_exists(cls, pid: int, img_p: str) -> bool: - """ - 说明: - 检测pid是否已存在 - 参数: - :param pid: 图片PID - :param img_p: 张数 - """ - query = await cls.query.where( - (cls.pid == pid) & (cls.img_p == img_p) - ).gino.all() - return bool(query) - - @classmethod - async def get_keyword_num(cls, tags: List[str] = None) -> "int, int": - """ - 说明: - 获取相关关键词(keyword, tag)在图库中的数量 - 参数: - :param tags: 关键词/Tag - """ - setattr(Pixiv, 'count', db.func.count(cls.pid).label('count')) - query = cls.select('count') - if tags: - for tag in tags: - query = query.where(cls.tags.contains(tag)) - count = await query.where(cls.is_r18 == False).gino.first() - r18_count = await query.where(cls.is_r18 == True).gino.first() - return count[0], r18_count[0] - +from typing import Optional, List +from services.db_context import db + + +class Pixiv(db.Model): + __tablename__ = "pixiv" + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer(), primary_key=True) + pid = db.Column(db.BigInteger(), nullable=False) + title = db.Column(db.String(), nullable=False) + width = db.Column(db.Integer(), nullable=False) + height = db.Column(db.Integer(), nullable=False) + view = db.Column(db.Integer(), nullable=False) + bookmarks = db.Column(db.Integer(), nullable=False) + img_url = db.Column(db.String(), nullable=False) + img_p = db.Column(db.String(), nullable=False) + uid = db.Column(db.BigInteger(), nullable=False) + author = db.Column(db.String(), nullable=False) + is_r18 = db.Column(db.Boolean(), nullable=False) + tags = db.Column(db.String(), nullable=False) + + _idx1 = db.Index("pixiv_idx1", "pid", "img_url", unique=True) + + @classmethod + async def add_image_data( + cls, + pid: int, + title: str, + width: int, + height: int, + view: int, + bookmarks: int, + img_url: str, + img_p: str, + uid: int, + author: str, + tags: str, + ): + """ + 说明: + 添加图片信息 + 参数: + :param pid: pid + :param title: 标题 + :param width: 宽度 + :param height: 长度 + :param view: 被查看次数 + :param bookmarks: 收藏数 + :param img_url: url链接 + :param img_p: 张数 + :param uid: 作者uid + :param author: 作者名称 + :param tags: 相关tag + """ + if not await cls.check_exists(pid, img_p): + await cls.create( + pid=pid, + title=title, + width=width, + height=height, + view=view, + bookmarks=bookmarks, + img_url=img_url, + img_p=img_p, + uid=uid, + author=author, + is_r18=True if "R-18" in tags else False, + tags=tags, + ) + return True + return False + + @classmethod + async def remove_image_data(cls, pid: int, img_p: str) -> bool: + """ + 说明: + 删除图片数据 + 参数: + :param pid: 图片pid + :param img_p: 图片pid的张数,如:p0,p1 + """ + try: + if img_p: + await cls.delete.where( + (cls.pid == pid) & (cls.img_p == img_p) + ).gino.status() + else: + await cls.delete.where(cls.pid == pid).gino.status() + return True + except Exception: + return False + + @classmethod + async def get_all_pid(cls) -> List[int]: + """ + 说明: + 获取所有PID + """ + query = await cls.query.select("pid").gino.first() + pid = [x[0] for x in query] + return list(set(pid)) + + # 0:非r18 1:r18 2:混合 + @classmethod + async def query_images( + cls, + keywords: Optional[List[str]] = None, + uid: Optional[int] = None, + pid: Optional[int] = None, + r18: Optional[int] = 0, + num: int = 100 + ) -> List[Optional["Pixiv"]]: + """ + 说明: + 查找符合条件的图片 + 参数: + :param keywords: 关键词 + :param uid: 画师uid + :param pid: 图片pid + :param r18: 是否r18,0:非r18 1:r18 2:混合 + :param num: 查找图片的数量 + """ + if r18 == 0: + query = cls.query.where(cls.is_r18 == False) + elif r18 == 1: + query = cls.query.where(cls.is_r18 == True) + else: + query = cls.query + if keywords: + for keyword in keywords: + query = query.where(cls.tags.contains(keyword)) + elif uid: + query = query.where(cls.uid == uid) + elif pid: + query = query.where(cls.pid == pid) + query = query.order_by(db.func.random()).limit(num) + return await query.gino.all() + + @classmethod + async def check_exists(cls, pid: int, img_p: str) -> bool: + """ + 说明: + 检测pid是否已存在 + 参数: + :param pid: 图片PID + :param img_p: 张数 + """ + query = await cls.query.where( + (cls.pid == pid) & (cls.img_p == img_p) + ).gino.all() + return bool(query) + + @classmethod + async def get_keyword_num(cls, tags: List[str] = None) -> "int, int": + """ + 说明: + 获取相关关键词(keyword, tag)在图库中的数量 + 参数: + :param tags: 关键词/Tag + """ + setattr(Pixiv, 'count', db.func.count(cls.pid).label('count')) + query = cls.select('count') + if tags: + for tag in tags: + query = query.where(cls.tags.contains(tag)) + count = await query.where(cls.is_r18 == False).gino.first() + r18_count = await query.where(cls.is_r18 == True).gino.first() + return count[0], r18_count[0] + diff --git a/plugins/pix_gallery/model/pixiv_keyword_user.py b/plugins/pix_gallery/model/pixiv_keyword_user.py old mode 100644 new mode 100755 index f8774e6d..10d33301 --- a/plugins/pix_gallery/model/pixiv_keyword_user.py +++ b/plugins/pix_gallery/model/pixiv_keyword_user.py @@ -1,127 +1,127 @@ -from services.db_context import db -from typing import Set, List - - -class PixivKeywordUser(db.Model): - __tablename__ = "pixiv_keyword_users" - __table_args__ = {'extend_existing': True} - - id = db.Column(db.Integer(), primary_key=True) - user_qq = db.Column(db.BigInteger(), nullable=False) - group_id = db.Column(db.BigInteger(), nullable=False) - keyword = db.Column(db.String(), nullable=False) - is_pass = db.Column(db.Boolean(), default=False) - - _idx1 = db.Index("pixiv_keyword_users_idx1", "keyword", unique=True) - - @classmethod - async def add_keyword( - cls, user_qq: int, group_id: int, keyword: str, superusers: Set[str] - ) -> bool: - """ - 说明: - 添加搜图的关键词 - 参数: - :param user_qq: qq号 - :param group_id: 群号 - :param keyword: 关键词 - :param superusers: 是否为超级用户 - """ - is_pass = True if str(user_qq) in superusers else False - if not await cls._check_keyword_exists(keyword): - await cls.create( - user_qq=user_qq, group_id=group_id, keyword=keyword, is_pass=is_pass - ) - return True - return False - - @classmethod - async def delete_keyword(cls, keyword: str) -> bool: - """ - 说明: - 删除关键词 - 参数: - :param keyword: 关键词 - """ - if await cls._check_keyword_exists(keyword): - query = cls.query.where(cls.keyword == keyword).with_for_update() - query = await query.gino.first() - await query.delete() - return True - return False - - @classmethod - async def set_keyword_pass(cls, keyword: str, is_pass: bool) -> "int, int": - """ - 说明: - 通过或禁用关键词 - 参数: - :param keyword: 关键词 - :param is_pass: 通过状态 - """ - if await cls._check_keyword_exists(keyword): - query = cls.query.where(cls.keyword == keyword).with_for_update() - query = await query.gino.first() - await query.update( - is_pass=is_pass, - ).apply() - return query.user_qq, query.group_id - return 0, 0 - - @classmethod - async def get_all_user_dict(cls) -> dict: - """ - 说明: - 获取关键词数据库各个用户贡献的关键词字典 - """ - tmp = {} - query = await cls.query.gino.all() - for user in query: - if not tmp.get(user.user_qq): - tmp[user.user_qq] = {"keyword": []} - tmp[user.user_qq]["keyword"].append(user.keyword) - return tmp - - @classmethod - async def get_current_keyword(cls) -> "List[str], List[str]": - """ - 说明: - 获取当前通过与未通过的关键词 - """ - pass_keyword = [] - not_pass_keyword = [] - query = await cls.query.gino.all() - for user in query: - if user.is_pass: - pass_keyword.append(user.keyword) - else: - not_pass_keyword.append(user.keyword) - return pass_keyword, not_pass_keyword - - @classmethod - async def get_black_pid(cls) -> List[str]: - """ - 说明: - 获取黑名单PID - """ - black_pid = [] - query = await cls.query.where(cls.user_qq == 114514).gino.all() - for image in query: - black_pid.append(image.keyword[6:]) - return black_pid - - @classmethod - async def _check_keyword_exists(cls, keyword: str) -> bool: - """ - 说明: - 检测关键词是否已存在 - 参数: - :param keyword: 关键词 - """ - current_keyword = [] - query = await cls.query.gino.all() - for user in query: - current_keyword.append(user.keyword) - if keyword in current_keyword: - return True - return False +from services.db_context import db +from typing import Set, List + + +class PixivKeywordUser(db.Model): + __tablename__ = "pixiv_keyword_users" + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer(), primary_key=True) + user_qq = db.Column(db.BigInteger(), nullable=False) + group_id = db.Column(db.BigInteger(), nullable=False) + keyword = db.Column(db.String(), nullable=False) + is_pass = db.Column(db.Boolean(), default=False) + + _idx1 = db.Index("pixiv_keyword_users_idx1", "keyword", unique=True) + + @classmethod + async def add_keyword( + cls, user_qq: int, group_id: int, keyword: str, superusers: Set[str] + ) -> bool: + """ + 说明: + 添加搜图的关键词 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + :param keyword: 关键词 + :param superusers: 是否为超级用户 + """ + is_pass = True if str(user_qq) in superusers else False + if not await cls._check_keyword_exists(keyword): + await cls.create( + user_qq=user_qq, group_id=group_id, keyword=keyword, is_pass=is_pass + ) + return True + return False + + @classmethod + async def delete_keyword(cls, keyword: str) -> bool: + """ + 说明: + 删除关键词 + 参数: + :param keyword: 关键词 + """ + if await cls._check_keyword_exists(keyword): + query = cls.query.where(cls.keyword == keyword).with_for_update() + query = await query.gino.first() + await query.delete() + return True + return False + + @classmethod + async def set_keyword_pass(cls, keyword: str, is_pass: bool) -> "int, int": + """ + 说明: + 通过或禁用关键词 + 参数: + :param keyword: 关键词 + :param is_pass: 通过状态 + """ + if await cls._check_keyword_exists(keyword): + query = cls.query.where(cls.keyword == keyword).with_for_update() + query = await query.gino.first() + await query.update( + is_pass=is_pass, + ).apply() + return query.user_qq, query.group_id + return 0, 0 + + @classmethod + async def get_all_user_dict(cls) -> dict: + """ + 说明: + 获取关键词数据库各个用户贡献的关键词字典 + """ + tmp = {} + query = await cls.query.gino.all() + for user in query: + if not tmp.get(user.user_qq): + tmp[user.user_qq] = {"keyword": []} + tmp[user.user_qq]["keyword"].append(user.keyword) + return tmp + + @classmethod + async def get_current_keyword(cls) -> "List[str], List[str]": + """ + 说明: + 获取当前通过与未通过的关键词 + """ + pass_keyword = [] + not_pass_keyword = [] + query = await cls.query.gino.all() + for user in query: + if user.is_pass: + pass_keyword.append(user.keyword) + else: + not_pass_keyword.append(user.keyword) + return pass_keyword, not_pass_keyword + + @classmethod + async def get_black_pid(cls) -> List[str]: + """ + 说明: + 获取黑名单PID + """ + black_pid = [] + query = await cls.query.where(cls.user_qq == 114514).gino.all() + for image in query: + black_pid.append(image.keyword[6:]) + return black_pid + + @classmethod + async def _check_keyword_exists(cls, keyword: str) -> bool: + """ + 说明: + 检测关键词是否已存在 + 参数: + :param keyword: 关键词 + """ + current_keyword = [] + query = await cls.query.gino.all() + for user in query: + current_keyword.append(user.keyword) + if keyword in current_keyword: + return True + return False diff --git a/plugins/pix_gallery/pix.py b/plugins/pix_gallery/pix.py old mode 100644 new mode 100755 index 10c352c1..04c1bfc0 --- a/plugins/pix_gallery/pix.py +++ b/plugins/pix_gallery/pix.py @@ -1,165 +1,165 @@ -from nonebot.adapters.cqhttp.message import Message -from utils.utils import get_message_text, is_number -from configs.config import Config -from .model.omega_pixiv_illusts import OmegaPixivIllusts -from utils.message_builder import image -from utils.manager import withdraw_message_manager -from services.log import logger -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - GroupMessageEvent, -) -from nonebot.typing import T_State -from .data_source import get_image -from .model.pixiv import Pixiv -from nonebot import on_command -import random - - -__zx_plugin_name__ = "PIX" -__plugin_usage__ = """ -usage: - 查看 pix 好康图库 - 指令: - pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取 - pix pid[pid]: 查看图库中指定pid图片 -""".strip() -__plugin_superuser_usage__ = """ -usage: - 超级用户额外的 pix 指令 - 指令: - pix -s ?*[tags]: 通过tag获取色图,不含tag时随机 - pix -r ?*[tags]: 通过tag获取r18图,不含tag时随机 -""".strip() -__plugin_des__ = "这里是PIX图库!" -__plugin_cmd__ = [ - "pix ?*[tags]", - "pix pid [pid]", - "pix -s ?*[tags] [_superuser]", - "pix -r ?*[tags] [_superuser]", -] -__plugin_type__ = ("来点好康的",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["pix", "Pix", "PIX", "pIx"], -} -__plugin_block_limit__ = {"rst": "您有PIX图片正在处理,请稍等..."} - - -pix = on_command("pix", aliases={"PIX", "Pix"}, priority=5, block=True) - - -PIX_RATIO = None -OMEGA_RATIO = None - - -@pix.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - global PIX_RATIO, OMEGA_RATIO - if PIX_RATIO is None: - pix_omega_pixiv_ratio = Config.get_config("pix", "PIX_OMEGA_PIXIV_RATIO") - PIX_RATIO = pix_omega_pixiv_ratio[0] / ( - pix_omega_pixiv_ratio[0] + pix_omega_pixiv_ratio[1] - ) - OMEGA_RATIO = 1 - PIX_RATIO - num = 1 - keyword = get_message_text(event.json()) - x = keyword.split(" ") - if "-s" in x: - x.remove("-s") - nsfw_tag = 1 - elif "-r" in x: - x.remove("-r") - nsfw_tag = 2 - else: - nsfw_tag = 0 - if nsfw_tag != 0 and str(event.user_id) not in bot.config.superusers: - await pix.finish("你不能看这些噢,这些都是是留给管理员看的...") - if len(x) > 1: - if is_number(x[-1]): - num = int(x[-1]) - if num > 10: - if str(event.user_id) not in bot.config.superusers or ( - str(event.user_id) in bot.config.superusers and num > 30 - ): - num = random.randint(1, 10) - await pix.send(f"太贪心了,就给你发 {num}张 好了") - x = x[:-1] - keyword = " ".join(x) - pix_num = int(num * PIX_RATIO) + 15 if PIX_RATIO != 0 else 0 - omega_num = num - pix_num + 15 - if is_number(keyword): - if num == 1: - pix_num = 15 - omega_num = 15 - all_image = await Pixiv.query_images( - uid=int(keyword), num=pix_num, r18=1 if nsfw_tag == 2 else 0 - ) + await OmegaPixivIllusts.query_images( - uid=int(keyword), num=omega_num, nsfw_tag=nsfw_tag - ) - elif keyword.lower().startswith("pid"): - pid = keyword.replace("pid", "").replace(":", "").replace(":", "") - if not is_number(pid): - await pix.finish("PID必须是数字...", at_sender=True) - all_image = await Pixiv.query_images( - pid=int(pid), r18=1 if nsfw_tag == 2 else 0 - ) - if not all_image: - all_image = await OmegaPixivIllusts.query_images( - pid=int(pid), nsfw_tag=nsfw_tag - ) - else: - tmp = await Pixiv.query_images( - x, r18=1 if nsfw_tag == 2 else 0, num=pix_num - ) + await OmegaPixivIllusts.query_images(x, nsfw_tag=nsfw_tag, num=omega_num) - tmp_ = [] - all_image = [] - for x in tmp: - if x.pid not in tmp_: - all_image.append(x) - tmp_.append(x.pid) - if not all_image: - await pix.finish(f"未在图库中找到与 {keyword} 相关Tag/UID/PID的图片...", at_sender=True) - for _ in range(num): - img_url = None - author = None - if not all_image: - await pix.finish("坏了...发完了,没图了...") - img = random.choice(all_image) - all_image.remove(img) - if isinstance(img, OmegaPixivIllusts): - img_url = img.url - author = img.uname - elif isinstance(img, Pixiv): - img_url = img.img_url - author = img.author - pid = img.pid - title = img.title - uid = img.uid - _img = await get_image(img_url, event.user_id) - if _img: - msg_id = await pix.send( - Message( - f"title:{title}\n" - f"author:{author}\n" - f"PID:{pid}\nUID:{uid}\n" - f"{image(_img, 'temp')}" - ) - ) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查看PIX图库PID: {pid}" - ) - else: - msg_id = await pix.send(f"下载图片似乎出了一点问题,PID:{pid}") - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查看PIX图库PID: {pid},下载图片出错" - ) - withdraw_message_manager.withdraw_message(event, msg_id, Config.get_config("pix", "WITHDRAW_PIX_MESSAGE")) - +from nonebot.adapters.cqhttp.message import Message +from utils.utils import get_message_text, is_number +from configs.config import Config +from .model.omega_pixiv_illusts import OmegaPixivIllusts +from utils.message_builder import image +from utils.manager import withdraw_message_manager +from services.log import logger +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent, +) +from nonebot.typing import T_State +from .data_source import get_image +from .model.pixiv import Pixiv +from nonebot import on_command +import random + + +__zx_plugin_name__ = "PIX" +__plugin_usage__ = """ +usage: + 查看 pix 好康图库 + 指令: + pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取 + pix pid[pid]: 查看图库中指定pid图片 +""".strip() +__plugin_superuser_usage__ = """ +usage: + 超级用户额外的 pix 指令 + 指令: + pix -s ?*[tags]: 通过tag获取色图,不含tag时随机 + pix -r ?*[tags]: 通过tag获取r18图,不含tag时随机 +""".strip() +__plugin_des__ = "这里是PIX图库!" +__plugin_cmd__ = [ + "pix ?*[tags]", + "pix pid [pid]", + "pix -s ?*[tags] [_superuser]", + "pix -r ?*[tags] [_superuser]", +] +__plugin_type__ = ("来点好康的",) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["pix", "Pix", "PIX", "pIx"], +} +__plugin_block_limit__ = {"rst": "您有PIX图片正在处理,请稍等..."} + + +pix = on_command("pix", aliases={"PIX", "Pix"}, priority=5, block=True) + + +PIX_RATIO = None +OMEGA_RATIO = None + + +@pix.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + global PIX_RATIO, OMEGA_RATIO + if PIX_RATIO is None: + pix_omega_pixiv_ratio = Config.get_config("pix", "PIX_OMEGA_PIXIV_RATIO") + PIX_RATIO = pix_omega_pixiv_ratio[0] / ( + pix_omega_pixiv_ratio[0] + pix_omega_pixiv_ratio[1] + ) + OMEGA_RATIO = 1 - PIX_RATIO + num = 1 + keyword = get_message_text(event.json()) + x = keyword.split(" ") + if "-s" in x: + x.remove("-s") + nsfw_tag = 1 + elif "-r" in x: + x.remove("-r") + nsfw_tag = 2 + else: + nsfw_tag = 0 + if nsfw_tag != 0 and str(event.user_id) not in bot.config.superusers: + await pix.finish("你不能看这些噢,这些都是是留给管理员看的...") + if len(x) > 1: + if is_number(x[-1]): + num = int(x[-1]) + if num > 10: + if str(event.user_id) not in bot.config.superusers or ( + str(event.user_id) in bot.config.superusers and num > 30 + ): + num = random.randint(1, 10) + await pix.send(f"太贪心了,就给你发 {num}张 好了") + x = x[:-1] + keyword = " ".join(x) + pix_num = int(num * PIX_RATIO) + 15 if PIX_RATIO != 0 else 0 + omega_num = num - pix_num + 15 + if is_number(keyword): + if num == 1: + pix_num = 15 + omega_num = 15 + all_image = await Pixiv.query_images( + uid=int(keyword), num=pix_num, r18=1 if nsfw_tag == 2 else 0 + ) + await OmegaPixivIllusts.query_images( + uid=int(keyword), num=omega_num, nsfw_tag=nsfw_tag + ) + elif keyword.lower().startswith("pid"): + pid = keyword.replace("pid", "").replace(":", "").replace(":", "") + if not is_number(pid): + await pix.finish("PID必须是数字...", at_sender=True) + all_image = await Pixiv.query_images( + pid=int(pid), r18=1 if nsfw_tag == 2 else 0 + ) + if not all_image: + all_image = await OmegaPixivIllusts.query_images( + pid=int(pid), nsfw_tag=nsfw_tag + ) + else: + tmp = await Pixiv.query_images( + x, r18=1 if nsfw_tag == 2 else 0, num=pix_num + ) + await OmegaPixivIllusts.query_images(x, nsfw_tag=nsfw_tag, num=omega_num) + tmp_ = [] + all_image = [] + for x in tmp: + if x.pid not in tmp_: + all_image.append(x) + tmp_.append(x.pid) + if not all_image: + await pix.finish(f"未在图库中找到与 {keyword} 相关Tag/UID/PID的图片...", at_sender=True) + for _ in range(num): + img_url = None + author = None + if not all_image: + await pix.finish("坏了...发完了,没图了...") + img = random.choice(all_image) + all_image.remove(img) + if isinstance(img, OmegaPixivIllusts): + img_url = img.url + author = img.uname + elif isinstance(img, Pixiv): + img_url = img.img_url + author = img.author + pid = img.pid + title = img.title + uid = img.uid + _img = await get_image(img_url, event.user_id) + if _img: + msg_id = await pix.send( + Message( + f"title:{title}\n" + f"author:{author}\n" + f"PID:{pid}\nUID:{uid}\n" + f"{image(_img, 'temp')}" + ) + ) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查看PIX图库PID: {pid}" + ) + else: + msg_id = await pix.send(f"下载图片似乎出了一点问题,PID:{pid}") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查看PIX图库PID: {pid},下载图片出错" + ) + withdraw_message_manager.withdraw_message(event, msg_id, Config.get_config("pix", "WITHDRAW_PIX_MESSAGE")) + diff --git a/plugins/pix_gallery/pix_add_keyword.py b/plugins/pix_gallery/pix_add_keyword.py old mode 100644 new mode 100755 index 6160be15..84b8b957 --- a/plugins/pix_gallery/pix_add_keyword.py +++ b/plugins/pix_gallery/pix_add_keyword.py @@ -1,120 +1,120 @@ -from nonebot import on_command -from utils.utils import get_message_text, is_number -from services.log import logger -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.typing import T_State -from .data_source import uid_pid_exists -from .model.pixiv_keyword_user import PixivKeywordUser -from .model.pixiv import Pixiv -from nonebot.permission import SUPERUSER - -__zx_plugin_name__ = "PIX关键词/UID/PID添加管理 [Superuser]" -__plugin_usage__ = """ -usage: - PIX关键词/UID/PID添加管理操作 - 指令: - 添加pix关键词 [Tag]: 添加一个pix搜索收录Tag - 添加pixuid [uid]: 添加一个pix搜索收录uid - 添加pixpid [pid]: 添加一个pix收录pid -""".strip() -__plugin_des__ = "PIX关键词/UID/PID添加管理" -__plugin_cmd__ = ["添加pix关键词 [Tag]", "添加pixuid [uid]", "添加pixpid [pid]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -add_keyword = on_command("添加pix关键词", aliases={"添加pix关键字"}, priority=1, block=True) - -add_black_pid = on_command("添加pix黑名单", permission=SUPERUSER, priority=1, block=True) - -# 超级用户可以通过字符 -f 来强制收录不检查是否存在 -add_uid_pid = on_command( - "添加pixuid", - aliases={ - "添加pixpid", - }, - priority=1, - block=True, -) - - -@add_keyword.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - group_id = -1 - if isinstance(event, GroupMessageEvent): - group_id = event.group_id - if msg: - if await PixivKeywordUser.add_keyword( - event.user_id, group_id, msg, bot.config.superusers - ): - await add_keyword.send( - f"已成功添加pixiv搜图关键词:{msg},请等待管理员通过该关键词!", at_sender=True - ) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 添加了pixiv搜图关键词:" + msg - ) - else: - await add_keyword.finish(f"该关键词 {msg} 已存在...") - else: - await add_keyword.finish(f"虚空关键词?.?.?.?") - - -@add_uid_pid.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - exists_flag = True - if msg.find("-f") != -1 and str(event.user_id) in bot.config.superusers: - exists_flag = False - msg = msg.replace("-f", "").strip() - if msg: - for msg in msg.split(): - if not is_number(msg): - await add_uid_pid.finish("UID只能是数字的说...", at_sender=True) - if state["_prefix"]["raw_command"].lower().endswith("uid"): - msg = f"uid:{msg}" - else: - msg = f"pid:{msg}" - if await Pixiv.check_exists(int(msg[4:]), "p0"): - await add_uid_pid.finish(f"该PID:{msg[4:]}已存在...", at_sender=True) - if not await uid_pid_exists(msg) and exists_flag: - await add_uid_pid.finish("画师或作品不存在或搜索正在CD,请稍等...", at_sender=True) - group_id = -1 - if isinstance(event, GroupMessageEvent): - group_id = event.group_id - if await PixivKeywordUser.add_keyword( - event.user_id, group_id, msg, bot.config.superusers - ): - await add_uid_pid.send( - f"已成功添加pixiv搜图UID/PID:{msg[4:]},请等待管理员通过!", at_sender=True - ) - else: - await add_uid_pid.finish(f"该UID/PID:{msg[4:]} 已存在...") - else: - await add_uid_pid.finish("湮灭吧!虚空的UID!") - - -@add_black_pid.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - pid = get_message_text(event.json()) - img_p = "" - if "p" in pid: - img_p = pid.split("p")[-1] - pid = pid.replace("_", "") - pid = pid[: pid.find("p")] - if not is_number(pid): - await add_black_pid.finish("PID必须全部是数字!", at_sender=True) - if await PixivKeywordUser.add_keyword( - 114514, - 114514, - f"black:{pid}{f'_p{img_p}' if img_p else ''}", - bot.config.superusers, - ): - await add_black_pid.send(f"已添加PID:{pid} 至黑名单中...") - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 添加了pixiv搜图黑名单 PID:{pid}" - ) - else: - await add_black_pid.send(f"PID:{pid} 已添加黑名单中,添加失败...") +from nonebot import on_command +from utils.utils import get_message_text, is_number +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from .data_source import uid_pid_exists +from .model.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv import Pixiv +from nonebot.permission import SUPERUSER + +__zx_plugin_name__ = "PIX关键词/UID/PID添加管理 [Superuser]" +__plugin_usage__ = """ +usage: + PIX关键词/UID/PID添加管理操作 + 指令: + 添加pix关键词 [Tag]: 添加一个pix搜索收录Tag + 添加pixuid [uid]: 添加一个pix搜索收录uid + 添加pixpid [pid]: 添加一个pix收录pid +""".strip() +__plugin_des__ = "PIX关键词/UID/PID添加管理" +__plugin_cmd__ = ["添加pix关键词 [Tag]", "添加pixuid [uid]", "添加pixpid [pid]"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +add_keyword = on_command("添加pix关键词", aliases={"添加pix关键字"}, priority=1, block=True) + +add_black_pid = on_command("添加pix黑名单", permission=SUPERUSER, priority=1, block=True) + +# 超级用户可以通过字符 -f 来强制收录不检查是否存在 +add_uid_pid = on_command( + "添加pixuid", + aliases={ + "添加pixpid", + }, + priority=1, + block=True, +) + + +@add_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + group_id = -1 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + if msg: + if await PixivKeywordUser.add_keyword( + event.user_id, group_id, msg, bot.config.superusers + ): + await add_keyword.send( + f"已成功添加pixiv搜图关键词:{msg},请等待管理员通过该关键词!", at_sender=True + ) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 添加了pixiv搜图关键词:" + msg + ) + else: + await add_keyword.finish(f"该关键词 {msg} 已存在...") + else: + await add_keyword.finish(f"虚空关键词?.?.?.?") + + +@add_uid_pid.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + exists_flag = True + if msg.find("-f") != -1 and str(event.user_id) in bot.config.superusers: + exists_flag = False + msg = msg.replace("-f", "").strip() + if msg: + for msg in msg.split(): + if not is_number(msg): + await add_uid_pid.finish("UID只能是数字的说...", at_sender=True) + if state["_prefix"]["raw_command"].lower().endswith("uid"): + msg = f"uid:{msg}" + else: + msg = f"pid:{msg}" + if await Pixiv.check_exists(int(msg[4:]), "p0"): + await add_uid_pid.finish(f"该PID:{msg[4:]}已存在...", at_sender=True) + if not await uid_pid_exists(msg) and exists_flag: + await add_uid_pid.finish("画师或作品不存在或搜索正在CD,请稍等...", at_sender=True) + group_id = -1 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + if await PixivKeywordUser.add_keyword( + event.user_id, group_id, msg, bot.config.superusers + ): + await add_uid_pid.send( + f"已成功添加pixiv搜图UID/PID:{msg[4:]},请等待管理员通过!", at_sender=True + ) + else: + await add_uid_pid.finish(f"该UID/PID:{msg[4:]} 已存在...") + else: + await add_uid_pid.finish("湮灭吧!虚空的UID!") + + +@add_black_pid.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid = get_message_text(event.json()) + img_p = "" + if "p" in pid: + img_p = pid.split("p")[-1] + pid = pid.replace("_", "") + pid = pid[: pid.find("p")] + if not is_number(pid): + await add_black_pid.finish("PID必须全部是数字!", at_sender=True) + if await PixivKeywordUser.add_keyword( + 114514, + 114514, + f"black:{pid}{f'_p{img_p}' if img_p else ''}", + bot.config.superusers, + ): + await add_black_pid.send(f"已添加PID:{pid} 至黑名单中...") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 添加了pixiv搜图黑名单 PID:{pid}" + ) + else: + await add_black_pid.send(f"PID:{pid} 已添加黑名单中,添加失败...") diff --git a/plugins/pix_gallery/pix_pass_del_keyword.py b/plugins/pix_gallery/pix_pass_del_keyword.py old mode 100644 new mode 100755 index 9bbbba1d..812d21a2 --- a/plugins/pix_gallery/pix_pass_del_keyword.py +++ b/plugins/pix_gallery/pix_pass_del_keyword.py @@ -1,182 +1,182 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp.message import Message -from utils.utils import get_message_text, is_number -from utils.message_builder import at -from services.log import logger -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from .data_source import remove_image -from .model.pixiv_keyword_user import PixivKeywordUser -from .model.pixiv import Pixiv - - -__zx_plugin_name__ = "PIX关键词/UID/PID删除管理 [Superuser]" -__plugin_usage__ = """ -usage: - PIX关键词/UID/PID删除管理操作 - 指令: - 通过pix关键词 [关键词/pid/uid] - 取消pix关键词 [关键词/pid/uid] - 删除pix关键词 [关键词/pid/uid] - 删除pix图片 *[pid] - 示例:通过pix关键词萝莉 - 示例:通过pix关键词uid:123456 - 示例:通过pix关键词pid:123456 - 示例:删除pix图片4223442 -""".strip() -__plugin_des__ = "PIX关键词/UID/PID删除管理" -__plugin_cmd__ = [ - "通过pix关键词 [关键词/pid/uid]", - "取消pix关键词 [关键词/pid/uid]", - "删除pix关键词 [关键词/pid/uid]", - "删除pix图片 *[pid]", -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -pass_keyword = on_command( - "通过pix关键词", - aliases={"通过pix关键字", "取消pix关键词", "取消pix关键字"}, - permission=SUPERUSER, - priority=1, - block=True, -) - -del_keyword = on_command( - "删除pix关键词", aliases={"删除pix关键字"}, permission=SUPERUSER, priority=1, block=True -) - -del_pic = on_command("删除pix图片", permission=SUPERUSER, priority=1, block=True) - - -@del_keyword.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if not msg: - await del_keyword.finish("好好输入要删除什么关键字啊笨蛋!") - if is_number(msg): - msg = f"uid:{msg}" - if msg.lower().startswith("pid"): - msg = "pid:" + msg.replace("pid", "").replace(":", "") - if await PixivKeywordUser.delete_keyword(msg): - await del_keyword.send(f"删除搜图关键词/UID:{msg} 成功...") - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 删除了pixiv搜图关键词:" + msg - ) - else: - await del_keyword.send(f"未查询到搜索关键词/UID/PID:{msg},删除失败!") - - -@del_pic.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - pid_arr = get_message_text(event.json()) - if pid_arr: - msg = "" - black_pid = "" - flag = False - pid_arr = pid_arr.split() - if pid_arr[-1] in ["-black", "-b"]: - flag = True - pid_arr = pid_arr[:-1] - for pid in pid_arr: - img_p = None - if "p" in pid or "ugoira" in pid: - if "p" in pid: - img_p = pid.split("p")[-1] - pid = pid.replace("_", "") - pid = pid[: pid.find("p")] - elif "ugoira" in pid: - img_p = pid.split("ugoira")[-1] - pid = pid.replace("_", "") - pid = pid[: pid.find("ugoira")] - if is_number(pid): - if await Pixiv.query_images(pid=int(pid), r18=2): - if await remove_image(int(pid), img_p): - msg += f'{pid}{f"_p{img_p}" if img_p else ""},' - if flag: - if await PixivKeywordUser.add_keyword( - 114514, - 114514, - f"black:{pid}{f'_p{img_p}' if img_p else ''}", - bot.config.superusers, - ): - black_pid += f'{pid}{f"_p{img_p}" if img_p else ""},' - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 删除了PIX图片 PID:{pid}{f'_p{img_p}' if img_p else ''}" - ) - else: - await del_pic.send( - f"PIX:删除pid:{pid}{f'_p{img_p}' if img_p else ''} 失败.." - ) - else: - await del_pic.send( - f"PIX:图片pix:{pid}{f'_p{img_p}' if img_p else ''} 不存在...无法删除.." - ) - else: - await del_pic.send(f"PID必须为数字!pid:{pid}", at_sender=True) - await del_pic.send(f"PIX:成功删除图片:{msg[:-1]}") - if flag: - await del_pic.send(f"成功图片PID加入黑名单:{black_pid[:-1]}") - else: - await del_pic.send("虚空删除?") - - -@pass_keyword.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - tmp = {"group": {}, "private": {}} - msg = get_message_text(event.json()) - if not msg: - await pass_keyword.finish("通过虚空的关键词/UID?离谱...") - msg = msg.split() - flag = True if state["_prefix"]["raw_command"][:2] == "通过" else False - for x in msg: - if x.lower().startswith("uid"): - x = x.replace("uid", "").replace(":", "") - x = f"uid:{x}" - elif x.lower().startswith("pid"): - x = x.replace("pid", "").replace(":", "") - x = f"pid:{x}" - if x.lower().find("pid") != -1 or x.lower().find("uid") != -1: - if not is_number(x[4:]): - await pass_keyword.send(f"UID/PID:{x} 非全数字,跳过该关键词...") - continue - user_id, group_id = await PixivKeywordUser.set_keyword_pass(x, flag) - if not user_id: - await pass_keyword.send(f"未找到关键词/UID:{x},请检查关键词/UID是否存在...") - continue - if flag: - if group_id == -1: - if not tmp["private"].get(user_id): - tmp["private"][user_id] = {"keyword": [x]} - else: - tmp["private"][user_id]["keyword"].append(x) - else: - if not tmp["group"].get(group_id): - tmp["group"][group_id] = {} - if not tmp["group"][group_id].get(user_id): - tmp["group"][group_id][user_id] = {"keyword": [x]} - else: - tmp["group"][group_id][user_id]["keyword"].append(x) - msg = " ".join(msg) - await pass_keyword.send(f'已成功{state["_prefix"]["raw_command"][:2]}搜图关键词:{msg}....') - for user in tmp["private"]: - x = ",".join(tmp["private"][user]["keyword"]) - await bot.send_private_msg( - user_id=user, message=f"你的关键词/UID/PID {x} 已被管理员通过,将在下一次进行更新..." - ) - for group in tmp["group"]: - for user in tmp["group"][group]: - x = ",".join(tmp["group"][group][user]["keyword"]) - await bot.send_group_msg( - group_id=group, - message=Message(f"{at(user)}你的关键词/UID/PID {x} 已被管理员通过,将在下一次进行更新..."), - ) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 通过了pixiv搜图关键词/UID:" + msg - ) +from nonebot import on_command +from nonebot.adapters.cqhttp.message import Message +from utils.utils import get_message_text, is_number +from utils.message_builder import at +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from .data_source import remove_image +from .model.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv import Pixiv + + +__zx_plugin_name__ = "PIX关键词/UID/PID删除管理 [Superuser]" +__plugin_usage__ = """ +usage: + PIX关键词/UID/PID删除管理操作 + 指令: + 通过pix关键词 [关键词/pid/uid] + 取消pix关键词 [关键词/pid/uid] + 删除pix关键词 [关键词/pid/uid] + 删除pix图片 *[pid] + 示例:通过pix关键词萝莉 + 示例:通过pix关键词uid:123456 + 示例:通过pix关键词pid:123456 + 示例:删除pix图片4223442 +""".strip() +__plugin_des__ = "PIX关键词/UID/PID删除管理" +__plugin_cmd__ = [ + "通过pix关键词 [关键词/pid/uid]", + "取消pix关键词 [关键词/pid/uid]", + "删除pix关键词 [关键词/pid/uid]", + "删除pix图片 *[pid]", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +pass_keyword = on_command( + "通过pix关键词", + aliases={"通过pix关键字", "取消pix关键词", "取消pix关键字"}, + permission=SUPERUSER, + priority=1, + block=True, +) + +del_keyword = on_command( + "删除pix关键词", aliases={"删除pix关键字"}, permission=SUPERUSER, priority=1, block=True +) + +del_pic = on_command("删除pix图片", permission=SUPERUSER, priority=1, block=True) + + +@del_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await del_keyword.finish("好好输入要删除什么关键字啊笨蛋!") + if is_number(msg): + msg = f"uid:{msg}" + if msg.lower().startswith("pid"): + msg = "pid:" + msg.replace("pid", "").replace(":", "") + if await PixivKeywordUser.delete_keyword(msg): + await del_keyword.send(f"删除搜图关键词/UID:{msg} 成功...") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 删除了pixiv搜图关键词:" + msg + ) + else: + await del_keyword.send(f"未查询到搜索关键词/UID/PID:{msg},删除失败!") + + +@del_pic.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid_arr = get_message_text(event.json()) + if pid_arr: + msg = "" + black_pid = "" + flag = False + pid_arr = pid_arr.split() + if pid_arr[-1] in ["-black", "-b"]: + flag = True + pid_arr = pid_arr[:-1] + for pid in pid_arr: + img_p = None + if "p" in pid or "ugoira" in pid: + if "p" in pid: + img_p = pid.split("p")[-1] + pid = pid.replace("_", "") + pid = pid[: pid.find("p")] + elif "ugoira" in pid: + img_p = pid.split("ugoira")[-1] + pid = pid.replace("_", "") + pid = pid[: pid.find("ugoira")] + if is_number(pid): + if await Pixiv.query_images(pid=int(pid), r18=2): + if await remove_image(int(pid), img_p): + msg += f'{pid}{f"_p{img_p}" if img_p else ""},' + if flag: + if await PixivKeywordUser.add_keyword( + 114514, + 114514, + f"black:{pid}{f'_p{img_p}' if img_p else ''}", + bot.config.superusers, + ): + black_pid += f'{pid}{f"_p{img_p}" if img_p else ""},' + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 删除了PIX图片 PID:{pid}{f'_p{img_p}' if img_p else ''}" + ) + else: + await del_pic.send( + f"PIX:删除pid:{pid}{f'_p{img_p}' if img_p else ''} 失败.." + ) + else: + await del_pic.send( + f"PIX:图片pix:{pid}{f'_p{img_p}' if img_p else ''} 不存在...无法删除.." + ) + else: + await del_pic.send(f"PID必须为数字!pid:{pid}", at_sender=True) + await del_pic.send(f"PIX:成功删除图片:{msg[:-1]}") + if flag: + await del_pic.send(f"成功图片PID加入黑名单:{black_pid[:-1]}") + else: + await del_pic.send("虚空删除?") + + +@pass_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + tmp = {"group": {}, "private": {}} + msg = get_message_text(event.json()) + if not msg: + await pass_keyword.finish("通过虚空的关键词/UID?离谱...") + msg = msg.split() + flag = True if state["_prefix"]["raw_command"][:2] == "通过" else False + for x in msg: + if x.lower().startswith("uid"): + x = x.replace("uid", "").replace(":", "") + x = f"uid:{x}" + elif x.lower().startswith("pid"): + x = x.replace("pid", "").replace(":", "") + x = f"pid:{x}" + if x.lower().find("pid") != -1 or x.lower().find("uid") != -1: + if not is_number(x[4:]): + await pass_keyword.send(f"UID/PID:{x} 非全数字,跳过该关键词...") + continue + user_id, group_id = await PixivKeywordUser.set_keyword_pass(x, flag) + if not user_id: + await pass_keyword.send(f"未找到关键词/UID:{x},请检查关键词/UID是否存在...") + continue + if flag: + if group_id == -1: + if not tmp["private"].get(user_id): + tmp["private"][user_id] = {"keyword": [x]} + else: + tmp["private"][user_id]["keyword"].append(x) + else: + if not tmp["group"].get(group_id): + tmp["group"][group_id] = {} + if not tmp["group"][group_id].get(user_id): + tmp["group"][group_id][user_id] = {"keyword": [x]} + else: + tmp["group"][group_id][user_id]["keyword"].append(x) + msg = " ".join(msg) + await pass_keyword.send(f'已成功{state["_prefix"]["raw_command"][:2]}搜图关键词:{msg}....') + for user in tmp["private"]: + x = ",".join(tmp["private"][user]["keyword"]) + await bot.send_private_msg( + user_id=user, message=f"你的关键词/UID/PID {x} 已被管理员通过,将在下一次进行更新..." + ) + for group in tmp["group"]: + for user in tmp["group"][group]: + x = ",".join(tmp["group"][group][user]["keyword"]) + await bot.send_group_msg( + group_id=group, + message=Message(f"{at(user)}你的关键词/UID/PID {x} 已被管理员通过,将在下一次进行更新..."), + ) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 通过了pixiv搜图关键词/UID:" + msg + ) diff --git a/plugins/pix_gallery/pix_show_info.py b/plugins/pix_gallery/pix_show_info.py old mode 100644 new mode 100755 index 5ed7eb9f..798e1ca2 --- a/plugins/pix_gallery/pix_show_info.py +++ b/plugins/pix_gallery/pix_show_info.py @@ -1,84 +1,84 @@ -from nonebot import on_command -from utils.utils import get_message_text -from utils.message_builder import image -from nonebot.adapters.cqhttp import Bot, MessageEvent -from nonebot.typing import T_State -from .data_source import gen_keyword_pic, get_keyword_num -from .model.pixiv_keyword_user import PixivKeywordUser -import asyncio - - -__zx_plugin_name__ = "查看pix图库" -__plugin_usage__ = """ -usage: - 查看pix图库 - 指令: - 查看pix图库 ?[tags]: 查看指定tag图片数量,为空时查看整个图库 -""".strip() -__plugin_des__ = "让我看看管理员私藏了多少货" -__plugin_cmd__ = ["查看pix图库 ?[tags]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["查看pix图库"], -} - - -my_keyword = on_command("我的pix关键词", aliases={"我的pix关键字"}, priority=1, block=True) - -show_keyword = on_command("显示pix关键词", aliases={"显示pix关键字"}, priority=1, block=True) - -show_pix = on_command("查看pix图库", priority=1, block=True) - - -@my_keyword.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - data = await PixivKeywordUser.get_all_user_dict() - if data.get(event.user_id) is None or not data[event.user_id]["keyword"]: - await my_keyword.finish("您目前没有提供任何Pixiv搜图关键字...", at_sender=True) - await my_keyword.send( - f"您目前提供的如下关键字:\n\t" + ",".join(data[event.user_id]["keyword"]) - ) - - -@show_keyword.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - _pass_keyword, not_pass_keyword = await PixivKeywordUser.get_current_keyword() - if _pass_keyword or not_pass_keyword: - await show_keyword.send( - image( - b64=await asyncio.get_event_loop().run_in_executor( - None, - gen_keyword_pic, - _pass_keyword, - not_pass_keyword, - str(event.user_id) in bot.config.superusers, - ) - ) - ) - else: - if str(event.user_id) in bot.config.superusers: - await show_keyword.finish(f"目前没有已收录或待收录的搜索关键词...") - else: - await show_keyword.finish(f"目前没有已收录的搜索关键词...") - - -@show_pix.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - keyword = get_message_text(event.json()) - count, r18_count, count_, setu_count, r18_count_ = await get_keyword_num(keyword) - await show_pix.send( - f"PIX图库:{keyword}\n" - f"总数:{count + r18_count}\n" - f"美图:{count}\n" - f"R18:{r18_count}\n" - f"---------------\n" - f"Omega图库:{keyword}\n" - f"总数:{count_ + setu_count + r18_count_}\n" - f"美图:{count_}\n" - f"色图:{setu_count}\n" - f"R18:{r18_count_}" - ) +from nonebot import on_command +from utils.utils import get_message_text +from utils.message_builder import image +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.typing import T_State +from .data_source import gen_keyword_pic, get_keyword_num +from .model.pixiv_keyword_user import PixivKeywordUser +import asyncio + + +__zx_plugin_name__ = "查看pix图库" +__plugin_usage__ = """ +usage: + 查看pix图库 + 指令: + 查看pix图库 ?[tags]: 查看指定tag图片数量,为空时查看整个图库 +""".strip() +__plugin_des__ = "让我看看管理员私藏了多少货" +__plugin_cmd__ = ["查看pix图库 ?[tags]"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["查看pix图库"], +} + + +my_keyword = on_command("我的pix关键词", aliases={"我的pix关键字"}, priority=1, block=True) + +show_keyword = on_command("显示pix关键词", aliases={"显示pix关键字"}, priority=1, block=True) + +show_pix = on_command("查看pix图库", priority=1, block=True) + + +@my_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + data = await PixivKeywordUser.get_all_user_dict() + if data.get(event.user_id) is None or not data[event.user_id]["keyword"]: + await my_keyword.finish("您目前没有提供任何Pixiv搜图关键字...", at_sender=True) + await my_keyword.send( + f"您目前提供的如下关键字:\n\t" + ",".join(data[event.user_id]["keyword"]) + ) + + +@show_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + _pass_keyword, not_pass_keyword = await PixivKeywordUser.get_current_keyword() + if _pass_keyword or not_pass_keyword: + await show_keyword.send( + image( + b64=await asyncio.get_event_loop().run_in_executor( + None, + gen_keyword_pic, + _pass_keyword, + not_pass_keyword, + str(event.user_id) in bot.config.superusers, + ) + ) + ) + else: + if str(event.user_id) in bot.config.superusers: + await show_keyword.finish(f"目前没有已收录或待收录的搜索关键词...") + else: + await show_keyword.finish(f"目前没有已收录的搜索关键词...") + + +@show_pix.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + keyword = get_message_text(event.json()) + count, r18_count, count_, setu_count, r18_count_ = await get_keyword_num(keyword) + await show_pix.send( + f"PIX图库:{keyword}\n" + f"总数:{count + r18_count}\n" + f"美图:{count}\n" + f"R18:{r18_count}\n" + f"---------------\n" + f"Omega图库:{keyword}\n" + f"总数:{count_ + setu_count + r18_count_}\n" + f"美图:{count_}\n" + f"色图:{setu_count}\n" + f"R18:{r18_count_}" + ) diff --git a/plugins/pix_gallery/pix_update.py b/plugins/pix_gallery/pix_update.py old mode 100644 new mode 100755 index 162e03db..0b58b481 --- a/plugins/pix_gallery/pix_update.py +++ b/plugins/pix_gallery/pix_update.py @@ -1,208 +1,132 @@ -from nonebot import on_command -from utils.utils import get_message_text, is_number -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from .data_source import start_update_image_url -from .model import PixivKeywordUser, Pixiv, OmegaPixivIllusts -from nonebot.adapters.cqhttp import Bot, MessageEvent -from services.log import logger -from typing import List -from datetime import datetime -from pathlib import Path -import asyncio -import time -import os - - -__zx_plugin_name__ = "pix检查更新 [Superuser]" -__plugin_usage__ = """ -usage: - 更新pix收录的所有或指定数量的 关键词/uid/pid - 指令: - 更新pix关键词 *[keyword/uid/pid] [num=max]: 更新仅keyword/uid/pid或全部 - pix检测更新:检测从未更新过的uid和pid - 示例:更新pix关键词keyword - 示例:更新pix关键词uid 10 -""".strip() -__plugin_des__ = "pix图库收录数据检查更新" -__plugin_cmd__ = ["更新pix关键词 *[keyword/uid/pid] [num=max]", "pix检测更新"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - -start_update = on_command( - "更新pix关键词", aliases={"更新pix关键字"}, permission=SUPERUSER, priority=1, block=True -) - -check_omega = on_command( - "检测omega图库", permission=SUPERUSER, priority=1, block=True -) - -check_not_update_uid_pid = on_command( - "pix检测更新", - aliases={"pix检查更新"}, - permission=SUPERUSER, - priority=1, - block=True, -) - - -@start_update.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg_sp = get_message_text(event.json()).split() - _pass_keyword, _ = await PixivKeywordUser.get_current_keyword() - _pass_keyword.reverse() - black_pid = await PixivKeywordUser.get_black_pid() - _keyword = [ - x - for x in _pass_keyword - if not x.startswith("uid:") - and not x.startswith("pid:") - and not x.startswith("black:") - ] - _uid = [x for x in _pass_keyword if x.startswith("uid:")] - _pid = [x for x in _pass_keyword if x.startswith("pid:")] - num = 9999 - msg = msg_sp[0] if len(msg_sp) else "" - if len(msg_sp) == 2: - if is_number(msg_sp[1]): - num = int(msg_sp[1]) - else: - await start_update.finish("参数错误...第二参数必须为数字") - if num < 10000: - keyword_str = ",".join( - _keyword[: num if num < len(_keyword) else len(_keyword)] - ) - uid_str = ",".join(_uid[: num if num < len(_uid) else len(_uid)]) - pid_str = ",".join(_pid[: num if num < len(_pid) else len(_pid)]) - if msg.lower() == "pid": - update_lst = _pid - info = f"开始更新Pixiv搜图PID:\n{pid_str}" - elif msg.lower() == "uid": - update_lst = _uid - info = f"开始更新Pixiv搜图UID:\n{uid_str}" - elif msg.lower() == "keyword": - update_lst = _keyword - info = f"开始更新Pixiv搜图关键词:\n{keyword_str}" - else: - update_lst = _pass_keyword - info = f"开始更新Pixiv搜图关键词:\n{keyword_str}\n更新UID:{uid_str}\n更新PID:{pid_str}" - num = num if num < len(update_lst) else len(update_lst) - else: - if msg.lower() == "pid": - update_lst = [f"pid:{num}"] - info = f"开始更新Pixiv搜图UID:\npid:{num}" - else: - update_lst = [f"uid:{num}"] - info = f"开始更新Pixiv搜图UID:\nuid:{num}" - await start_update.send(info) - start_time = time.time() - pid_count, pic_count = await start_update_image_url(update_lst[:num], black_pid) - await start_update.send( - f"Pixiv搜图关键词搜图更新完成...\n" - f"累计更新PID {pid_count} 个\n" - f"累计更新图片 {pic_count} 张" + "\n耗时:{:.2f}秒".format((time.time() - start_time)) - ) - - -@check_not_update_uid_pid.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - flag = False - if msg == "update": - flag = True - _pass_keyword, _ = await PixivKeywordUser.get_current_keyword() - x_uid = [] - x_pid = [] - _uid = [int(x[4:]) for x in _pass_keyword if x.startswith("uid:")] - _pid = [int(x[4:]) for x in _pass_keyword if x.startswith("pid:")] - all_images = await Pixiv.query_images(r18=2) - for img in all_images: - if img.pid not in x_pid: - x_pid.append(img.pid) - if img.uid not in x_uid: - x_uid.append(img.uid) - await check_not_update_uid_pid.send( - "从未更新过的UID:" - + ",".join([f"uid:{x}" for x in _uid if x not in x_uid]) - + "\n" - + "从未更新过的PID:" - + ",".join([f"pid:{x}" for x in _pid if x not in x_pid]) - ) - if flag: - await check_not_update_uid_pid.send("开始自动自动更新PID....") - update_lst = [f"pid:{x}" for x in _uid if x not in x_uid] - black_pid = await PixivKeywordUser.get_black_pid() - start_time = time.time() - pid_count, pic_count = await start_update_image_url(update_lst, black_pid) - await check_not_update_uid_pid.send( - f"Pixiv搜图关键词搜图更新完成...\n" - f"累计更新PID {pid_count} 个\n" - f"累计更新图片 {pic_count} 张" + "\n耗时:{:.2f}秒".format((time.time() - start_time)) - ) - - -@check_omega.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - async def _tasks(line: str, all_pid: List[int], length: int, index: int): - data = line.split("VALUES", maxsplit=1)[-1].strip() - if data.startswith("("): - data = data[1:] - if data.endswith(");"): - data = data[:-2] - x = data.split(maxsplit=3) - pid = int(x[1][:-1].strip()) - if pid in all_pid: - logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}") - return - uid = int(x[2][:-1].strip()) - x = x[3].split(", '") - title = x[0].strip()[1:-1] - tmp = x[1].split(", ") - author = tmp[0].strip()[:-1] - nsfw_tag = int(tmp[1]) - width = int(tmp[2]) - height = int(tmp[3]) - tags = x[2][:-1] - url = x[3][:-1] - if await OmegaPixivIllusts.add_image_data( - pid, - title, - width, - height, - url, - uid, - author, - nsfw_tag, - tags, - datetime.min, - datetime.min, - ): - logger.info( - f"成功添加OmegaPixivIllusts图库数据 pid:{pid} 本次预计存储 {length} 张,已更新第 {index} 张" - ) - else: - logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}") - omega_pixiv_illusts = None - for file in os.listdir("."): - if "omega_pixiv_illusts" in file and ".sql" in file: - omega_pixiv_illusts = Path() / file - if omega_pixiv_illusts: - with open(omega_pixiv_illusts, "r", encoding="utf8") as f: - lines = f.readlines() - tasks = [] - length = len([x for x in lines if "INSERT INTO" in x.upper()]) - all_pid = await OmegaPixivIllusts.get_all_pid() - index = 0 - logger.info("检测到OmegaPixivIllusts数据库,准备开始更新....") - for line in lines: - if "INSERT INTO" in line.upper(): - index += 1 - tasks.append( - asyncio.ensure_future(_tasks(line, all_pid, length, index)) - ) - await asyncio.gather(*tasks) - omega_pixiv_illusts.unlink() - - - - +from nonebot import on_command +from utils.utils import get_message_text, is_number +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from .data_source import start_update_image_url +from .model.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv import Pixiv +from nonebot.adapters.cqhttp import Bot, MessageEvent +import time + + +__zx_plugin_name__ = "pix检查更新 [Superuser]" +__plugin_usage__ = """ +usage: + 更新pix收录的所有或指定数量的 关键词/uid/pid + 指令: + 更新pix关键词 *[keyword/uid/pid] [num=max]: 更新仅keyword/uid/pid或全部 + pix检测更新:检测从未更新过的uid和pid + 示例:更新pix关键词keyword + 示例:更新pix关键词uid 10 +""".strip() +__plugin_des__ = "pix图库收录数据检查更新" +__plugin_cmd__ = ["更新pix关键词 *[keyword/uid/pid] [num=max]", "pix检测更新"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + +start_update = on_command( + "更新pix关键词", aliases={"更新pix关键字"}, permission=SUPERUSER, priority=1, block=True +) + +check_not_update_uid_pid = on_command( + "pix检测更新", + aliases={"pix检查更新"}, + permission=SUPERUSER, + priority=1, + block=True, +) + + +@start_update.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg_sp = get_message_text(event.json()).split() + _pass_keyword, _ = await PixivKeywordUser.get_current_keyword() + _pass_keyword.reverse() + black_pid = await PixivKeywordUser.get_black_pid() + _keyword = [ + x + for x in _pass_keyword + if not x.startswith("uid:") + and not x.startswith("pid:") + and not x.startswith("black:") + ] + _uid = [x for x in _pass_keyword if x.startswith("uid:")] + _pid = [x for x in _pass_keyword if x.startswith("pid:")] + num = 9999 + msg = msg_sp[0] if len(msg_sp) else "" + if len(msg_sp) == 2: + if is_number(msg_sp[1]): + num = int(msg_sp[1]) + else: + await start_update.finish("参数错误...第二参数必须为数字") + if num < 10000: + keyword_str = ",".join( + _keyword[: num if num < len(_keyword) else len(_keyword)] + ) + uid_str = ",".join(_uid[: num if num < len(_uid) else len(_uid)]) + pid_str = ",".join(_pid[: num if num < len(_pid) else len(_pid)]) + if msg.lower() == "pid": + update_lst = _pid + info = f"开始更新Pixiv搜图PID:\n{pid_str}" + elif msg.lower() == "uid": + update_lst = _uid + info = f"开始更新Pixiv搜图UID:\n{uid_str}" + elif msg.lower() == "keyword": + update_lst = _keyword + info = f"开始更新Pixiv搜图关键词:\n{keyword_str}" + else: + update_lst = _pass_keyword + info = f"开始更新Pixiv搜图关键词:\n{keyword_str}\n更新UID:{uid_str}\n更新PID:{pid_str}" + num = num if num < len(update_lst) else len(update_lst) + else: + if msg.lower() == "pid": + update_lst = [f"pid:{num}"] + info = f"开始更新Pixiv搜图UID:\npid:{num}" + else: + update_lst = [f"uid:{num}"] + info = f"开始更新Pixiv搜图UID:\nuid:{num}" + await start_update.send(info) + start_time = time.time() + pid_count, pic_count = await start_update_image_url(update_lst[:num], black_pid) + await start_update.send( + f"Pixiv搜图关键词搜图更新完成...\n" + f"累计更新PID {pid_count} 个\n" + f"累计更新图片 {pic_count} 张" + "\n耗时:{:.2f}秒".format((time.time() - start_time)) + ) + + +@check_not_update_uid_pid.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + flag = False + if msg == "update": + flag = True + _pass_keyword, _ = await PixivKeywordUser.get_current_keyword() + x_uid = [] + x_pid = [] + _uid = [int(x[4:]) for x in _pass_keyword if x.startswith("uid:")] + _pid = [int(x[4:]) for x in _pass_keyword if x.startswith("pid:")] + all_images = await Pixiv.query_images(r18=2) + for img in all_images: + if img.pid not in x_pid: + x_pid.append(img.pid) + if img.uid not in x_uid: + x_uid.append(img.uid) + await check_not_update_uid_pid.send( + "从未更新过的UID:" + + ",".join([f"uid:{x}" for x in _uid if x not in x_uid]) + + "\n" + + "从未更新过的PID:" + + ",".join([f"pid:{x}" for x in _pid if x not in x_pid]) + ) + if flag: + await check_not_update_uid_pid.send("开始自动自动更新PID....") + update_lst = [f"pid:{x}" for x in _uid if x not in x_uid] + black_pid = await PixivKeywordUser.get_black_pid() + start_time = time.time() + pid_count, pic_count = await start_update_image_url(update_lst, black_pid) + await check_not_update_uid_pid.send( + f"Pixiv搜图关键词搜图更新完成...\n" + f"累计更新PID {pid_count} 个\n" + f"累计更新图片 {pic_count} 张" + "\n耗时:{:.2f}秒".format((time.time() - start_time)) + ) diff --git a/plugins/pixiv_rank_search/__init__.py b/plugins/pixiv_rank_search/__init__.py old mode 100644 new mode 100755 index 172cbc89..c09b4795 --- a/plugins/pixiv_rank_search/__init__.py +++ b/plugins/pixiv_rank_search/__init__.py @@ -1,213 +1,212 @@ -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from nonebot.matcher import Matcher -from nonebot import on_command -from utils.utils import get_message_text, is_number -from .data_source import get_pixiv_urls, download_pixiv_imgs, search_pixiv_urls -from services.log import logger -from nonebot.adapters.cqhttp.exception import NetworkError -from asyncio.exceptions import TimeoutError -from aiohttp.client_exceptions import ClientConnectorError -from utils.message_builder import custom_forward_msg -from configs.config import Config -from typing import Type -from nonebot.rule import to_me -import time - -__zx_plugin_name__ = "P站排行/搜图" - -__plugin_usage__ = """ -usage: - P站排行: - 可选参数: - 类型: - 1. 日排行 - 2. 周排行 - 3. 月排行 - 4. 原创排行 - 5. 新人排行 - 6. R18日排行 - 7. R18周排行 - 8. R18受男性欢迎排行 - 9. R18重口排行【慎重!】 - 【使用时选择参数序号即可,R18仅可私聊】 - p站排行 ?[参数] ?[数量] ?[日期] - 示例: - p站排行榜 [无参数默认为日榜] - p站排行榜 1 - p站排行榜 1 5 - p站排行榜 1 5 2018-4-25 - 【注意空格!!】【在线搜索会较慢】 - --------------------------------- - P站搜图: - 搜图 [关键词] ?[数量] ?[页数=1] ?[r18](不屏蔽R-18) - 示例: - 搜图 樱岛麻衣 - 搜图 樱岛麻衣 5 - 搜图 樱岛麻衣 5 r18 - 【默认为 热度排序】 - 【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】 -""".strip() -__plugin_des__ = "P站排行榜直接冲,P站搜图跟着冲" -__plugin_cmd__ = ["p站排行 ?[参数] ?[数量] ?[日期]", "搜图 [关键词] ?[数量] ?[页数=1] ?[r18](不屏蔽R-18)"] -__plugin_type__ = ("来点好康的",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 9, - "default_status": True, - "limit_superuser": False, - "cmd": ["p站排行", "搜图", "p站搜图", "P站搜图"], -} -__plugin_block_limit__ = {"rst": "P站排行榜或搜图正在搜索,请不要重复触发命令..."} -__plugin_configs__ = { - "TIMEOUT": { - "value": 10, - "help": "图片下载超时限制", - "default_value": 10 - } -} -Config.add_plugin_config( - "hibiapi", - "HIBIAPI", - "https://api.obfs.dev", - help_="如果没有自建或其他hibiapi请不要修改", - default_value="https://api.obfs.dev", -) -Config.add_plugin_config( - "pixiv", - "PIXIV_NGINX_URL", - "i.pixiv.re", - help_="Pixiv反向代理" -) - - -rank_dict = { - "1": "day", - "2": "week", - "3": "month", - "4": "week_original", - "5": "week_rookie", - "6": "day_r18", - "7": "week_r18", - "8": "day_male_r18", - "9": "week_r18g", -} - - -pixiv_rank = on_command( - "p站排行", - aliases={"P站排行榜", "p站排行榜", "P站排行榜", "P站排行"}, - priority=5, - block=True, - rule=to_me(), -) -pixiv_keyword = on_command("搜图", priority=5, block=True, rule=to_me()) - - -@pixiv_rank.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()).strip() - msg = msg.split(" ") - msg = [m for m in msg if m] - code = 0 - info_list = [] - if not msg: - msg = ["1"] - if msg[0] in ["6", "7", "8", "9"]: - if event.message_type == "group": - await pixiv_rank.finish("羞羞脸!私聊里自己看!", at_sender=True) - if (n := len(msg)) == 0 or msg[0] == "": - info_list, code = await get_pixiv_urls(rank_dict.get("1")) - elif n == 1: - if msg[0] not in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]: - await pixiv_rank.finish("要好好输入要看什么类型的排行榜呀!", at_sender=True) - info_list, code = await get_pixiv_urls(rank_dict.get(msg[0])) - elif n == 2: - info_list, code = await get_pixiv_urls(rank_dict.get(msg[0]), int(msg[1])) - elif n == 3: - if not check_date(msg[2]): - await pixiv_rank.finish("日期格式错误了", at_sender=True) - info_list, code = await get_pixiv_urls( - rank_dict.get(msg[0]), int(msg[1]), date=msg[2] - ) - else: - await pixiv_rank.finish("格式错了噢,参数不够?看看帮助?", at_sender=True) - if code != 200 and info_list: - await pixiv_rank.finish(info_list[0]) - if not info_list: - await pixiv_rank.finish("没有找到啊,等等再试试吧~V", at_sender=True) - await send_image(info_list, pixiv_rank, bot, event) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查看了P站排行榜 code:{msg[0]}" - ) - - -@pixiv_keyword.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if isinstance(event, GroupMessageEvent): - if "r18" in msg.lower(): - await pixiv_keyword.finish("(脸红#) 你不会害羞的 八嘎!", at_sender=True) - r18 = 0 if "r18" in msg else 1 - msg = msg.replace("r18", "").strip().split() - msg = [m.strip() for m in msg if m] - keyword = None - info_list = None - num = 10 - page = 1 - if (n := len(msg)) == 1: - keyword = msg[0] - if n > 1: - if not is_number(msg[1]): - await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True) - num = int(msg[1]) - if n > 2: - if not is_number(msg[2]): - await pixiv_keyword.finish("页数数量必须是数字!", at_sender=True) - page = int(msg[2]) - if keyword: - info_list, code = await search_pixiv_urls(keyword, num, page, r18) - if code != 200: - await pixiv_keyword.finish(info_list[0]) - if not info_list: - await pixiv_keyword.finish("没有找到啊,等等再试试吧~V", at_sender=True) - await send_image(info_list, pixiv_keyword, bot, event) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 查看了搜索 {keyword} R18:{r18}" - ) - - -def check_date(date): - try: - time.strptime(date, "%Y-%m-%d") - return True - except: - return False - - -async def send_image( - info_list: list, matcher: Type[Matcher], bot: Bot, event: MessageEvent -): - if isinstance(event, GroupMessageEvent): - await pixiv_rank.send("开始下载整理数据...") - idx = 0 - mes_list = [] - for title, author, urls in info_list: - _message = f"title: {title}\nauthor: {author}\n" + await download_pixiv_imgs(urls, event.user_id, idx) - mes_list.append(_message) - idx += 1 - mes_list = custom_forward_msg(mes_list, bot.self_id) - await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list) - else: - for title, author, urls in info_list: - try: - await matcher.send( - f"title: {title}\n" - f"author: {author}\n" - + await download_pixiv_imgs(urls, event.user_id) - ) - except (NetworkError, TimeoutError, ClientConnectorError): - await matcher.send("这张图网络直接炸掉了!", at_sender=True) +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.matcher import Matcher +from nonebot import on_command +from utils.utils import get_message_text, is_number +from .data_source import get_pixiv_urls, download_pixiv_imgs, search_pixiv_urls +from services.log import logger +from nonebot.adapters.cqhttp.exception import NetworkError +from asyncio.exceptions import TimeoutError +from utils.message_builder import custom_forward_msg +from configs.config import Config +from typing import Type +from nonebot.rule import to_me +import time + +__zx_plugin_name__ = "P站排行/搜图" + +__plugin_usage__ = """ +usage: + P站排行: + 可选参数: + 类型: + 1. 日排行 + 2. 周排行 + 3. 月排行 + 4. 原创排行 + 5. 新人排行 + 6. R18日排行 + 7. R18周排行 + 8. R18受男性欢迎排行 + 9. R18重口排行【慎重!】 + 【使用时选择参数序号即可,R18仅可私聊】 + p站排行 ?[参数] ?[数量] ?[日期] + 示例: + p站排行榜 [无参数默认为日榜] + p站排行榜 1 + p站排行榜 1 5 + p站排行榜 1 5 2018-4-25 + 【注意空格!!】【在线搜索会较慢】 + --------------------------------- + P站搜图: + 搜图 [关键词] ?[数量] ?[页数=1] ?[r18](不屏蔽R-18) + 示例: + 搜图 樱岛麻衣 + 搜图 樱岛麻衣 5 + 搜图 樱岛麻衣 5 r18 + 【默认为 热度排序】 + 【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】 +""".strip() +__plugin_des__ = "P站排行榜直接冲,P站搜图跟着冲" +__plugin_cmd__ = ["p站排行 ?[参数] ?[数量] ?[日期]", "搜图 [关键词] ?[数量] ?[页数=1] ?[r18](不屏蔽R-18)"] +__plugin_type__ = ("来点好康的",) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 9, + "default_status": True, + "limit_superuser": False, + "cmd": ["p站排行", "搜图", "p站搜图", "P站搜图"], +} +__plugin_block_limit__ = {"rst": "P站排行榜或搜图正在搜索,请不要重复触发命令..."} +__plugin_configs__ = { + "TIMEOUT": { + "value": 10, + "help": "图片下载超时限制", + "default_value": 10 + } +} +Config.add_plugin_config( + "hibiapi", + "HIBIAPI", + "https://api.obfs.dev", + help_="如果没有自建或其他hibiapi请不要修改", + default_value="https://api.obfs.dev", +) +Config.add_plugin_config( + "pixiv", + "PIXIV_NGINX_URL", + "i.pixiv.re", + help_="Pixiv反向代理" +) + + +rank_dict = { + "1": "day", + "2": "week", + "3": "month", + "4": "week_original", + "5": "week_rookie", + "6": "day_r18", + "7": "week_r18", + "8": "day_male_r18", + "9": "week_r18g", +} + + +pixiv_rank = on_command( + "p站排行", + aliases={"P站排行榜", "p站排行榜", "P站排行榜", "P站排行"}, + priority=5, + block=True, + rule=to_me(), +) +pixiv_keyword = on_command("搜图", priority=5, block=True, rule=to_me()) + + +@pixiv_rank.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).strip() + msg = msg.split(" ") + msg = [m for m in msg if m] + code = 0 + info_list = [] + if not msg: + msg = ["1"] + if msg[0] in ["6", "7", "8", "9"]: + if event.message_type == "group": + await pixiv_rank.finish("羞羞脸!私聊里自己看!", at_sender=True) + if (n := len(msg)) == 0 or msg[0] == "": + info_list, code = await get_pixiv_urls(rank_dict.get("1")) + elif n == 1: + if msg[0] not in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]: + await pixiv_rank.finish("要好好输入要看什么类型的排行榜呀!", at_sender=True) + info_list, code = await get_pixiv_urls(rank_dict.get(msg[0])) + elif n == 2: + info_list, code = await get_pixiv_urls(rank_dict.get(msg[0]), int(msg[1])) + elif n == 3: + if not check_date(msg[2]): + await pixiv_rank.finish("日期格式错误了", at_sender=True) + info_list, code = await get_pixiv_urls( + rank_dict.get(msg[0]), int(msg[1]), date=msg[2] + ) + else: + await pixiv_rank.finish("格式错了噢,参数不够?看看帮助?", at_sender=True) + if code != 200 and info_list: + await pixiv_rank.finish(info_list[0]) + if not info_list: + await pixiv_rank.finish("没有找到啊,等等再试试吧~V", at_sender=True) + await send_image(info_list, pixiv_rank, bot, event) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查看了P站排行榜 code:{msg[0]}" + ) + + +@pixiv_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if isinstance(event, GroupMessageEvent): + if "r18" in msg.lower(): + await pixiv_keyword.finish("(脸红#) 你不会害羞的 八嘎!", at_sender=True) + r18 = 0 if "r18" in msg else 1 + msg = msg.replace("r18", "").strip().split() + msg = [m.strip() for m in msg if m] + keyword = None + info_list = None + num = 10 + page = 1 + if (n := len(msg)) == 1: + keyword = msg[0] + if n > 1: + if not is_number(msg[1]): + await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True) + num = int(msg[1]) + if n > 2: + if not is_number(msg[2]): + await pixiv_keyword.finish("页数数量必须是数字!", at_sender=True) + page = int(msg[2]) + if keyword: + info_list, code = await search_pixiv_urls(keyword, num, page, r18) + if code != 200: + await pixiv_keyword.finish(info_list[0]) + if not info_list: + await pixiv_keyword.finish("没有找到啊,等等再试试吧~V", at_sender=True) + await send_image(info_list, pixiv_keyword, bot, event) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查看了搜索 {keyword} R18:{r18}" + ) + + +def check_date(date): + try: + time.strptime(date, "%Y-%m-%d") + return True + except: + return False + + +async def send_image( + info_list: list, matcher: Type[Matcher], bot: Bot, event: MessageEvent +): + if isinstance(event, GroupMessageEvent): + await pixiv_rank.send("开始下载整理数据...") + idx = 0 + mes_list = [] + for title, author, urls in info_list: + _message = f"title: {title}\nauthor: {author}\n" + await download_pixiv_imgs(urls, event.user_id, idx) + mes_list.append(_message) + idx += 1 + mes_list = custom_forward_msg(mes_list, bot.self_id) + await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list) + else: + for title, author, urls in info_list: + try: + await matcher.send( + f"title: {title}\n" + f"author: {author}\n" + + await download_pixiv_imgs(urls, event.user_id) + ) + except (NetworkError, TimeoutError): + await matcher.send("这张图网络直接炸掉了!", at_sender=True) diff --git a/plugins/pixiv_rank_search/data_source.py b/plugins/pixiv_rank_search/data_source.py old mode 100644 new mode 100755 index 6547747e..898c97d0 --- a/plugins/pixiv_rank_search/data_source.py +++ b/plugins/pixiv_rank_search/data_source.py @@ -1,174 +1,154 @@ -from configs.path_config import IMAGE_PATH -from utils.utils import get_local_proxy -from utils.message_builder import image -from asyncio.exceptions import TimeoutError -from configs.config import Config -from aiohttp.client_exceptions import ClientConnectorError -from typing import Optional -from pathlib import Path -import aiohttp -import aiofiles -import platform - -if platform.system() == "Windows": - import asyncio - - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - - -headers = { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" - " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", - "Referer": "https://www.pixiv.net", -} - - -async def get_pixiv_urls( - mode: str, num: int = 10, page: int = 1, date: Optional[str] = None -) -> "list, int": - """ - 拿到pixiv rank图片url - :param mode: 模式 - :param num: 数量 - :param page: 页数 - :param date: 日期 - """ - params = {"mode": mode, "page": page} - if date: - params["date"] = date - hibiapi = Config.get_config("hibiapi", "HIBIAPI") - hibiapi = hibiapi[:-1] if hibiapi[-1] == "/" else hibiapi - rank_url = f"{hibiapi}/api/pixiv/rank" - return await parser_data(rank_url, num, params, "rank") - - -async def search_pixiv_urls( - keyword: str, num: int, page: int, r18: int -) -> "list, list": - """ - 搜图图片的url - :param keyword: 关键词 - :param num: 数量 - :param page: 页数 - :param r18: 是否r18 - """ - params = {"word": keyword, "page": page} - hibiapi = Config.get_config("hibiapi", "HIBIAPI") - hibiapi = hibiapi[:-1] if hibiapi[-1] == "/" else hibiapi - search_url = f"{hibiapi}/api/pixiv/search" - return await parser_data(search_url, num, params, "search", r18) - - -async def parser_data( - url: str, num: int, params: dict, type_: str, r18: int = 0 -) -> "list, int": - """ - 解析数据 - :param url: hibiapi搜索url - :param num: 数量 - :param params: 参数 - :param type_: 类型,rank或search - :param r18: 是否r18 - """ - info_list = [] - async with aiohttp.ClientSession() as session: - for _ in range(3): - try: - async with session.get( - url, - params=params, - proxy=get_local_proxy(), - timeout=Config.get_config("pixiv_rank_search", "TIMEOUT"), - ) as response: - if response.status == 200: - data = await response.json() - if data.get("illusts"): - data = data["illusts"] - break - except (TimeoutError, ClientConnectorError): - pass - else: - return ["网络不太好?没有该页数?也许过一会就好了..."], 998 - num = num if num < 30 else 30 - data = data[:num] - for x in data: - if type_ == "search" and r18 == 1: - if "R-18" in str(x["tags"]): - continue - title = x["title"] - author = x["user"]["name"] - urls = [] - if x["page_count"] == 1: - urls.append(x["image_urls"]["large"]) - else: - for j in x["meta_pages"]: - urls.append(j["image_urls"]["large"]) - info_list.append((title, author, urls)) - return info_list, 200 - - -async def download_pixiv_imgs( - urls: list, user_id: int, forward_msg_index: int = None -) -> str: - """ - 下载图片 - :param urls: 图片链接 - :param user_id: 用户id - :param forward_msg_index: 转发消息中的图片排序 - """ - result = "" - index = 0 - for url in urls: - ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") - if ws_url.startswith("http"): - ws_url = ws_url.split("//")[-1] - url = ( - url.replace("i.pximg.net", ws_url) - .replace("i.pixiv.cat", ws_url) - .replace("_webp", "") - ) - async with aiohttp.ClientSession(headers=headers) as session: - for _ in range(3): - try: - async with session.get( - url, - proxy=get_local_proxy(), - timeout=Config.get_config("pixiv_rank_search", "TIMEOUT"), - ) as response: - if response.status == 200: - try: - file = ( - f"{IMAGE_PATH}/temp/{user_id}_{forward_msg_index}_{index}_pixiv.jpg" - if forward_msg_index is not None - else f"{IMAGE_PATH}/temp/{user_id}_{index}_pixiv.jpg" - ) - file = Path(file) - if forward_msg_index is not None: - async with aiofiles.open( - file, - "wb", - ) as f: - await f.write(await response.read()) - result += image( - f"{user_id}_{forward_msg_index}_{index}_pixiv.jpg", - "temp", - ) - else: - async with aiofiles.open( - file, - "wb", - ) as f: - await f.write(await response.read()) - result += image( - f"{user_id}_{index}_pixiv.jpg", "temp" - ) - index += 1 - break - except OSError: - file.unlink() - except (TimeoutError, ClientConnectorError): - # result += '\n这张图下载失败了..\n' - pass - else: - result += "\n这张图下载失败了..\n" - return result +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from asyncio.exceptions import TimeoutError +from configs.config import Config +from utils.http_utils import AsyncHttpx +from typing import Optional +from services.log import logger +from pathlib import Path +import platform + +if platform.system() == "Windows": + import asyncio + + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} + + +async def get_pixiv_urls( + mode: str, num: int = 10, page: int = 1, date: Optional[str] = None +) -> "list, int": + """ + 拿到pixiv rank图片url + :param mode: 模式 + :param num: 数量 + :param page: 页数 + :param date: 日期 + """ + params = {"mode": mode, "page": page} + if date: + params["date"] = date + hibiapi = Config.get_config("hibiapi", "HIBIAPI") + hibiapi = hibiapi[:-1] if hibiapi[-1] == "/" else hibiapi + rank_url = f"{hibiapi}/api/pixiv/rank" + return await parser_data(rank_url, num, params, "rank") + + +async def search_pixiv_urls( + keyword: str, num: int, page: int, r18: int +) -> "list, list": + """ + 搜图图片的url + :param keyword: 关键词 + :param num: 数量 + :param page: 页数 + :param r18: 是否r18 + """ + params = {"word": keyword, "page": page} + hibiapi = Config.get_config("hibiapi", "HIBIAPI") + hibiapi = hibiapi[:-1] if hibiapi[-1] == "/" else hibiapi + search_url = f"{hibiapi}/api/pixiv/search" + return await parser_data(search_url, num, params, "search", r18) + + +async def parser_data( + url: str, num: int, params: dict, type_: str, r18: int = 0 +) -> "list, int": + """ + 解析数据 + :param url: hibiapi搜索url + :param num: 数量 + :param params: 参数 + :param type_: 类型,rank或search + :param r18: 是否r18 + """ + info_list = [] + for _ in range(3): + try: + response = await AsyncHttpx.get( + url, + params=params, + timeout=Config.get_config("pixiv_rank_search", "TIMEOUT"), + ) + if response.status_code == 200: + data = response.json() + if data.get("illusts"): + data = data["illusts"] + break + except TimeoutError: + pass + except Exception as e: + logger.error(f"P站排行/搜图解析数据发生错误 {type(e)}:{e}") + return ["发生了一些些错误..."], 995 + else: + return ["网络不太好?没有该页数?也许过一会就好了..."], 998 + num = num if num < 30 else 30 + data = data[:num] + for x in data: + if type_ == "search" and r18 == 1: + if "R-18" in str(x["tags"]): + continue + title = x["title"] + author = x["user"]["name"] + urls = [] + if x["page_count"] == 1: + urls.append(x["image_urls"]["large"]) + else: + for j in x["meta_pages"]: + urls.append(j["image_urls"]["large"]) + info_list.append((title, author, urls)) + return info_list, 200 + + +async def download_pixiv_imgs( + urls: list, user_id: int, forward_msg_index: int = None +) -> str: + """ + 下载图片 + :param urls: 图片链接 + :param user_id: 用户id + :param forward_msg_index: 转发消息中的图片排序 + """ + result = "" + index = 0 + for url in urls: + ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") + if ws_url: + url = ( + url.replace("i.pximg.net", ws_url) + .replace("i.pixiv.cat", ws_url) + .replace("_webp", "") + ) + try: + file = ( + f"{IMAGE_PATH}/temp/{user_id}_{forward_msg_index}_{index}_pixiv.jpg" + if forward_msg_index is not None + else f"{IMAGE_PATH}/temp/{user_id}_{index}_pixiv.jpg" + ) + file = Path(file) + try: + if await AsyncHttpx.download_file( + url, + file, + timeout=Config.get_config("pixiv_rank_search", "TIMEOUT"), + ): + if forward_msg_index is not None: + result += image( + f"{user_id}_{forward_msg_index}_{index}_pixiv.jpg", + "temp", + ) + else: + result += image(f"{user_id}_{index}_pixiv.jpg", "temp") + index += 1 + except OSError: + if file.exists(): + file.unlink() + except Exception as e: + logger.error(f"P站排行/搜图下载图片错误 {type(e)}:{e}") + return result diff --git a/plugins/poke/__init__.py b/plugins/poke/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/quotations.py b/plugins/quotations.py old mode 100644 new mode 100755 index f3e98b90..3c083c84 --- a/plugins/quotations.py +++ b/plugins/quotations.py @@ -2,8 +2,7 @@ from nonebot import on_command from services.log import logger from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State -import aiohttp -from utils.utils import get_local_proxy +from utils.http_utils import AsyncHttpx __zx_plugin_name__ = "一言二次元语录" @@ -32,9 +31,7 @@ url = "https://international.v1.hitokoto.cn/?c=a" @quotations.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - async with aiohttp.ClientSession() as session: - async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: - data = await response.json() + data = (await AsyncHttpx.get(url, timeout=5)).json() result = f'{data["hitokoto"]}\t——{data["from"]}' await quotations.send(result) logger.info( diff --git a/plugins/roll.py b/plugins/roll.py old mode 100644 new mode 100755 index b8fe723b..35fb09f3 --- a/plugins/roll.py +++ b/plugins/roll.py @@ -1,66 +1,66 @@ -from nonebot import on_command -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from utils.utils import get_message_text -from services.log import logger -from configs.config import NICKNAME -import random -import asyncio - - -__zx_plugin_name__ = "roll" -__plugin_usage__ = """ -usage: - 随机数字 或 随机选择事件 - 指令: - roll: 随机 0-100 的数字 - roll *[文本]: 随机事件 - 示例:roll 吃饭 睡觉 打游戏 -""".strip() -__plugin_des__ = "犹豫不决吗?那就让我帮你决定吧" -__plugin_cmd__ = ["roll", "roll *[文本]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["roll"], -} - - -roll = on_command("roll", priority=5, block=True) - - -@roll.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()).split() - if not msg: - await roll.finish(f"roll: {random.randint(0, 100)}", at_sender=True) - user_name = event.sender.card if event.sender.card else event.sender.nickname - await roll.send( - random.choice( - [ - "转动命运的齿轮,拨开眼前迷雾...", - f"启动吧,命运的水晶球,为{user_name}指引方向!", - "嗯哼,在此刻转动吧!命运!", - f"在此祈愿,请为{user_name}降下指引...", - ] - ) - ) - await asyncio.sleep(1) - x = random.choice(msg) - await roll.send( - random.choice( - [ - f"让{NICKNAME}看看是什么结果!答案是:‘{x}’", - f"根据命运的指引,接下来{user_name} ‘{x}’ 会比较好", - f"祈愿被回应了!是 ‘{x}’!", - f"结束了,{user_name},命运之轮停在了 ‘{x}’!", - ] - ) - ) - logger.info( - f"(USER {event.user_id}, " - f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送roll:{msg}" - ) +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from utils.utils import get_message_text +from services.log import logger +from configs.config import NICKNAME +import random +import asyncio + + +__zx_plugin_name__ = "roll" +__plugin_usage__ = """ +usage: + 随机数字 或 随机选择事件 + 指令: + roll: 随机 0-100 的数字 + roll *[文本]: 随机事件 + 示例:roll 吃饭 睡觉 打游戏 +""".strip() +__plugin_des__ = "犹豫不决吗?那就让我帮你决定吧" +__plugin_cmd__ = ["roll", "roll *[文本]"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["roll"], +} + + +roll = on_command("roll", priority=5, block=True) + + +@roll.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).split() + if not msg: + await roll.finish(f"roll: {random.randint(0, 100)}", at_sender=True) + user_name = event.sender.card if event.sender.card else event.sender.nickname + await roll.send( + random.choice( + [ + "转动命运的齿轮,拨开眼前迷雾...", + f"启动吧,命运的水晶球,为{user_name}指引方向!", + "嗯哼,在此刻转动吧!命运!", + f"在此祈愿,请为{user_name}降下指引...", + ] + ) + ) + await asyncio.sleep(1) + x = random.choice(msg) + await roll.send( + random.choice( + [ + f"让{NICKNAME}看看是什么结果!答案是:‘{x}’", + f"根据命运的指引,接下来{user_name} ‘{x}’ 会比较好", + f"祈愿被回应了!是 ‘{x}’!", + f"结束了,{user_name},命运之轮停在了 ‘{x}’!", + ] + ) + ) + logger.info( + f"(USER {event.user_id}, " + f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送roll:{msg}" + ) diff --git a/plugins/russian/__init__.py b/plugins/russian/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/russian/data_source.py b/plugins/russian/data_source.py old mode 100644 new mode 100755 diff --git a/plugins/russian/model.py b/plugins/russian/model.py old mode 100644 new mode 100755 diff --git a/plugins/search_anime/__init__.py b/plugins/search_anime/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/search_anime/data_source.py b/plugins/search_anime/data_source.py old mode 100644 new mode 100755 index 577ccf3d..42daba1d --- a/plugins/search_anime/data_source.py +++ b/plugins/search_anime/data_source.py @@ -2,10 +2,9 @@ from lxml import etree import feedparser from urllib import parse from services.log import logger +from utils.http_utils import AsyncHttpx from typing import List -import aiohttp import time -from utils.utils import get_local_proxy async def from_anime_get_info(key_word: str, max_: int) -> List[str]: @@ -22,35 +21,32 @@ async def from_anime_get_info(key_word: str, max_: int) -> List[str]: async def get_repass(url: str, max_: int) -> List[str]: put_line = [] - async with aiohttp.ClientSession() as session: - async with session.get(url, proxy=get_local_proxy(), timeout=20) as response: - d = feedparser.parse(await response.text()) - max_ = max_ if max_ < len([e.link for e in d.entries]) else len([e.link for e in d.entries]) - url_list = [e.link for e in d.entries][:max_] - for u in url_list: - try: - async with session.get( - u, proxy=get_local_proxy(), timeout=20 - ) as res: - html = etree.HTML(await res.text()) - magent = html.xpath('.//a[@id="a_magnet"]/text()')[0] - title = html.xpath(".//h3/text()")[0] - item = html.xpath( - '//div[@class="info resource-info right"]/ul/li' - ) - class_a = ( - item[0] - .xpath("string(.)")[5:] - .strip() - .replace("\xa0", "") - .replace("\t", "") - ) - size = item[3].xpath("string(.)")[5:].strip() - put_line.append( - "【{}】| {}\n【{}】| {}".format(class_a, title, size, magent) - ) - except Exception as e: - logger.warning(f"搜番超时 e:{e}") + text = (await AsyncHttpx.get(url)).text + d = feedparser.parse(text) + max_ = max_ if max_ < len([e.link for e in d.entries]) else len([e.link for e in d.entries]) + url_list = [e.link for e in d.entries][:max_] + for u in url_list: + try: + text = (await AsyncHttpx.get(u)).text + html = etree.HTML(text) + magent = html.xpath('.//a[@id="a_magnet"]/text()')[0] + title = html.xpath(".//h3/text()")[0] + item = html.xpath( + '//div[@class="info resource-info right"]/ul/li' + ) + class_a = ( + item[0] + .xpath("string(.)")[5:] + .strip() + .replace("\xa0", "") + .replace("\t", "") + ) + size = item[3].xpath("string(.)")[5:].strip() + put_line.append( + "【{}】| {}\n【{}】| {}".format(class_a, title, size, magent) + ) + except Exception as e: + logger.error(f"搜番发生错误 {type(e)}:{e}") return put_line diff --git a/plugins/search_buff_skin_price/__init__.py b/plugins/search_buff_skin_price/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/search_buff_skin_price/data_source.py b/plugins/search_buff_skin_price/data_source.py old mode 100644 new mode 100755 index 3785484a..5ab86040 --- a/plugins/search_buff_skin_price/data_source.py +++ b/plugins/search_buff_skin_price/data_source.py @@ -1,44 +1,49 @@ -from utils.user_agent import get_user_agent -import aiohttp from asyncio.exceptions import TimeoutError from configs.config import Config +from utils.http_utils import AsyncHttpx +from services.log import logger url = "https://buff.163.com/api/market/goods" -async def get_price(dname): +async def get_price(d_name: str) -> "str, int": + """ + 查看皮肤价格 + :param d_name: 武器皮肤,如:awp 二西莫夫 + """ cookie = {"session": Config.get_config("search_buff_skin_price", "COOKIE")} name_list = [] price_list = [] - parameter = {"game": "csgo", "page_num": "1", "search": dname} + parameter = {"game": "csgo", "page_num": "1", "search": d_name} try: - async with aiohttp.ClientSession( - cookies=cookie, headers=get_user_agent() - ) as session: - async with session.get( - url, proxy=Config.get_config("search_buff_skin_price", "BUFF_PROXY"), params=parameter, timeout=5 - ) as response: - if response.status == 200: - try: - if str(await response.text()).find("Login Required") != -1: - return "BUFF登录被重置,请联系管理员重新登入", 996 - data = (await response.json())["data"] - total_page = data["total_page"] - data = data["items"] - for _ in range(total_page): - for i in range(len(data)): - name = data[i]["name"] - price = data[i]["sell_reference_price"] - name_list.append(name) - price_list.append(price) - except Exception: - return "没有查询到...", 998 - else: - return "访问失败!", response.status + response = await AsyncHttpx.get( + url, + proxy=Config.get_config("search_buff_skin_price", "BUFF_PROXY"), + params=parameter, + cookies=cookie, + ) + if response.status_code == 200: + try: + if response.text.find("Login Required") != -1: + return "BUFF登录被重置,请联系管理员重新登入", 996 + data = response.json()["data"] + total_page = data["total_page"] + data = data["items"] + for _ in range(total_page): + for i in range(len(data)): + name = data[i]["name"] + price = data[i]["sell_reference_price"] + name_list.append(name) + price_list.append(price) + except Exception as e: + logger.error(f"BUFF查询皮肤发生错误 {type(e)}:{e}") + return "没有查询到...", 998 + else: + return "访问失败!", response.status_code except TimeoutError: return "访问超时! 请重试或稍后再试!", 997 - result = f"皮肤: {dname}({len(name_list)})\n" + result = f"皮肤: {d_name}({len(name_list)})\n" for i in range(len(name_list)): result += name_list[i] + ": " + price_list[i] + "\n" return result[:-1], 999 diff --git a/plugins/search_image/__init__.py b/plugins/search_image/__init__.py new file mode 100644 index 00000000..d10b9505 --- /dev/null +++ b/plugins/search_image/__init__.py @@ -0,0 +1,133 @@ +# # -*- coding: utf-8 -*- +# from typing import Dict +# +# from aiohttp.client_exceptions import ClientError +# from nonebot.plugin import on_command, on_message +# from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +# from nonebot.typing import T_State +# from services.log import logger +# from utils.utils import get_message_text, get_message_imgs +# from configs.config import Config +# from nonebot.rule import to_me +# +# +# +# __zx_plugin_name__ = "识图" +# __plugin_usage__ = """ +# usage: +# 识别图片 [二次元图片] +# 指令: +# 识图 [图片] +# """.strip() +# __plugin_des__ = "以图搜图,看破本源" +# __plugin_cmd__ = ["识图"] +# __plugin_type__ = ("一些工具",) +# __plugin_version__ = 0.1 +# __plugin_author__ = "synodriver" +# __plugin_settings__ = { +# "level": 5, +# "default_status": True, +# "limit_superuser": False, +# "cmd": ["识图"], +# } +# __plugin_configs__ = { +# "MAX_FIND_IMAGE_COUNT": {"value": 3, "help": "识图返回的最大结果数", "default_value": 3} +# } +# +# +# async def get_des(url: str, mode: str, user_id: int): +# """ +# :param url: 图片链接 +# :param mode: 图源 +# :param user_id: 用户 id +# """ +# if mode == "iqdb": +# async for msg in get_des_iqdb(url): +# yield msg +# elif mode == "ex": +# async for msg in get_des_ex(url): +# yield msg +# elif mode == "trace": +# async for msg in get_des_trace(url): +# yield msg +# elif mode == "yandex": +# async for msg in get_des_yandex(url): +# yield msg +# elif mode.startswith("asc"): +# async for msg in get_des_asc(url, user_id): +# yield msg +# else: +# async for msg in get_des_sau(url, user_id): +# yield msg +# +# +# setu = on_command("识图", aliases={"search"}, block=True, priority=5) +# +# +# @setu.handle() +# async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State): +# msg = get_message_text(event.json()) +# imgs = get_message_imgs(event.json()) +# if imgs: +# state["setu"] = imgs[0] +# if msg: +# state["mod"] = msg +# +# +# # ex/nao/trace/iqdb/ascii2d +# # @setu.got("mod", prompt="从哪里查找呢? ex/nao/trace/iqdb/ascii2d") +# # async def get_func(bot: Bot, event: MessageEvent, state: dict): +# # pass +# +# +# @setu.args_parser +# async def get_setu(bot: Bot, event: MessageEvent, state: T_State): +# imgs = get_message_imgs(event.json()) +# msg = get_message_text(event.json()) +# if not imgs: +# await setu.reject() +# if msg: +# state["mod"] = msg +# state["setu"] = imgs[0] +# +# +# @setu.got("setu", prompt="图呢?") +# async def get_setu(bot: Bot, event: MessageEvent, state: T_State): +# """ +# 发现没有的时候要发问 +# :return: +# """ +# url: str = state["setu"] +# mod: str = state["mod"] if state.get("mod") else "nao" # 模式 +# try: +# await bot.send(event=event, message="正在处理图片") +# idx = 1 +# async for msg in get_des(url, mod, event.user_id): +# if msg: +# await bot.send(event=event, message=msg) +# if idx == Config.get_config( +# "nonebot_plugin_picsearcher", "MAX_FIND_IMAGE_COUNT" +# ): +# break +# idx += 1 +# if id == 1: +# await bot.send(event=event, message="没找着.") +# logger.info( +# f"(USER {event.user_id}, GROUP " +# f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 识图:{url}" +# ) +# # image_data: List[Tuple] = await get_pic_from_url(url) +# # await setu.finish("hso") +# except IndexError: +# # await bot.send(event, traceback.format_exc()) +# await setu.finish("参数错误") +# except ClientError: +# await setu.finish("连接失败") +# +# +# +# +# +# +# +# diff --git a/plugins/search_image/config.py b/plugins/search_image/config.py new file mode 100644 index 00000000..121ce757 --- /dev/null +++ b/plugins/search_image/config.py @@ -0,0 +1,4 @@ + +API_URL_SAUCENAO = 'https://saucenao.com/search.php' +API_URL_ASCII2D = 'https://ascii2d.net/search/url/' +API_URL_IQDB = 'https://iqdb.org/' diff --git a/plugins/search_image/saucenao.py b/plugins/search_image/saucenao.py new file mode 100644 index 00000000..8dd14280 --- /dev/null +++ b/plugins/search_image/saucenao.py @@ -0,0 +1,47 @@ +from utils.user_agent import get_user_agent +from utils.utils import get_local_proxy + + + + + +async def get_saucenao_identify_result(url: str) -> Result.DictListResult: + fetcher = HttpFetcher(timeout=10, flag='search_image_saucenao', headers=HEADERS) + + if not API_KEY: + logger.opt(colors=True).warning(f'Saucenao API KEY未配置, 无法使用Saucenao API进行识图!') + return Result.DictListResult(error=True, info='Saucenao API KEY未配置', result=[]) + + __payload = {'output_type': 2, + 'api_key': API_KEY, + 'testmode': 1, + 'numres': 6, + 'db': 999, + 'url': url} + saucenao_result = await fetcher.get_json(url=API_URL_SAUCENAO, params=__payload) + if saucenao_result.error: + logger.warning(f'get_saucenao_identify_result failed, Network error: {saucenao_result.info}') + return Result.DictListResult(error=True, info=f'Network error: {saucenao_result.info}', result=[]) + + __result_json = saucenao_result.result + + if __result_json['header']['status'] != 0: + logger.error(f"get_saucenao_identify_result failed, DataSource error, " + f"status code: {__result_json['header']['status']}") + return Result.DictListResult( + error=True, info=f"DataSource error, status code: {__result_json['header']['status']}", result=[]) + + __result = [] + for __item in __result_json['results']: + try: + if int(float(__item['header']['similarity'])) < 75: + continue + else: + __result.append({'similarity': __item['header']['similarity'], + 'thumbnail': __item['header']['thumbnail'], + 'index_name': __item['header']['index_name'], + 'ext_urls': __item['data']['ext_urls']}) + except Exception as res_err: + logger.warning(f"get_saucenao_identify_result failed: {repr(res_err)}, can not resolve results") + continue + return Result.DictListResult(error=False, info='Success', result=__result) \ No newline at end of file diff --git a/plugins/send_dinggong_voice/__init__.py b/plugins/send_dinggong_voice/__init__.py old mode 100644 new mode 100755 index 2b584168..d0359f53 --- a/plugins/send_dinggong_voice/__init__.py +++ b/plugins/send_dinggong_voice/__init__.py @@ -1,11 +1,11 @@ from nonebot import on_keyword from utils.message_builder import record from configs.path_config import VOICE_PATH -import random from services.log import logger from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.rule import to_me +import random import os __zx_plugin_name__ = "骂我" diff --git a/plugins/send_setu_/__init__.py b/plugins/send_setu_/__init__.py old mode 100644 new mode 100755 index 9418a25b..5f878a25 --- a/plugins/send_setu_/__init__.py +++ b/plugins/send_setu_/__init__.py @@ -1,4 +1,4 @@ -import nonebot - - -nonebot.load_plugins("plugins/send_setu_") +import nonebot + + +nonebot.load_plugins("plugins/send_setu_") diff --git a/plugins/send_setu_/model.py b/plugins/send_setu_/model.py old mode 100644 new mode 100755 index affe617d..b35881c9 --- a/plugins/send_setu_/model.py +++ b/plugins/send_setu_/model.py @@ -1,177 +1,177 @@ -from services.db_context import db -from typing import List, Optional - - -class Setu(db.Model): - __tablename__ = "setu" - __table_args__ = {'extend_existing': True} - - id = db.Column(db.Integer(), primary_key=True) - local_id = db.Column(db.Integer(), nullable=False) - title = db.Column(db.String(), nullable=False) - author = db.Column(db.String(), nullable=False) - pid = db.Column(db.BigInteger(), nullable=False) - img_hash = db.Column(db.String(), nullable=False) - img_url = db.Column(db.String(), nullable=False) - is_r18 = db.Column(db.Boolean(), nullable=False) - tags = db.Column(db.String()) - - _idx1 = db.Index("setu_pid_img_url_idx1", "pid", "img_url", unique=True) - - @classmethod - async def add_setu_data( - cls, - local_id: int, - title: str, - author: str, - pid: int, - img_hash: str, - img_url: str, - tags: str, - ): - """ - 说明: - 添加一份色图数据 - 参数: - :param local_id: 本地存储id - :param title: 标题 - :param author: 作者 - :param pid: 图片pid - :param img_hash: 图片hash值 - :param img_url: 图片链接 - :param tags: 图片标签 - """ - if not await cls._check_exists(pid, img_url): - await cls.create( - local_id=local_id, - title=title, - author=author, - pid=pid, - img_hash=img_hash, - img_url=img_url, - is_r18=True if "R-18" in tags else False, - tags=tags, - ) - - @classmethod - async def query_image( - cls, - local_id: Optional[int] = None, - tags: Optional[List[str]] = None, - r18: int = 0, - limit: int = 50, - ): - """ - 说明: - 通过tag查找色图 - 参数: - :param local_id: 本地色图 id - :param tags: tags - :param r18: 是否 r18,0:非r18 1:r18 2:混合 - :param limit: 获取数量 - """ - if local_id: - flag = True if r18 == 1 else False - return await cls.query.where( - (cls.local_id == local_id) & (cls.is_r18 == flag) - ).gino.first() - if r18 == 0: - query = cls.query.where(cls.is_r18 == False) - elif r18 == 1: - query = cls.query.where(cls.is_r18 == True) - else: - query = cls.query - if tags: - for tag in tags: - query = query.where(cls.tags.contains(tag) | cls.title.contains(tag) | cls.author.contains(tag)) - query = query.order_by(db.func.random()).limit(limit) - return await query.gino.all() - - @classmethod - async def get_image_count(cls, r18: int = 0) -> int: - """ - 说明: - 查询图片数量 - """ - flag = False if r18 == 0 else True - setattr(Setu, 'count', db.func.count(cls.local_id).label('count')) - count = await cls.select('count').where(cls.is_r18 == flag).gino.first() - return count[0] - - @classmethod - async def get_image_in_hash(cls, img_hash: str) -> "Setu": - """ - 说明: - 通过图像hash获取图像信息 - 参数: - :param img_hash: = 图像hash值 - """ - query = await cls.query.where(cls.img_hash == img_hash).gino.first() - return query - - @classmethod - async def _check_exists(cls, pid: int, img_url: str) -> bool: - """ - 说明: - 检测图片是否存在 - 参数: - :param pid: 图片pid - :param img_url: 图片链接 - """ - return bool( - await cls.query.where( - (cls.pid == pid) & (cls.img_url == img_url) - ).gino.first() - ) - - @classmethod - async def update_setu_data( - cls, - pid: int, - *, - local_id: Optional[int] = None, - title: Optional[str] = None, - author: Optional[str] = None, - img_hash: Optional[str] = None, - img_url: Optional[str] = None, - tags: Optional[str] = None, - ) -> bool: - """ - 说明: - 根据PID修改图片数据 - 参数: - :param local_id: 本地id - :param pid: 图片pid - :param title: 标题 - :param author: 作者 - :param img_hash: 图片hash值 - :param img_url: 图片链接 - :param tags: 图片标签 - """ - query = cls.query.where(cls.pid == pid).with_for_update() - image_list = await query.gino.all() - if image_list: - for image in image_list: - if local_id: - await image.update(local_id=local_id).apply() - if title: - await image.update(title=title).apply() - if author: - await image.update(author=author).apply() - if img_hash: - await image.update(img_hash=img_hash).apply() - if img_url: - await image.update(img_url=img_url).apply() - if tags: - await image.update(tags=tags).apply() - return True - return False - - @classmethod - async def get_all_setu(cls) -> List["Setu"]: - """ - 说明: - 获取所有图片对象 - """ - return await cls.query.gino.all() - +from services.db_context import db +from typing import List, Optional + + +class Setu(db.Model): + __tablename__ = "setu" + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer(), primary_key=True) + local_id = db.Column(db.Integer(), nullable=False) + title = db.Column(db.String(), nullable=False) + author = db.Column(db.String(), nullable=False) + pid = db.Column(db.BigInteger(), nullable=False) + img_hash = db.Column(db.String(), nullable=False) + img_url = db.Column(db.String(), nullable=False) + is_r18 = db.Column(db.Boolean(), nullable=False) + tags = db.Column(db.String()) + + _idx1 = db.Index("setu_pid_img_url_idx1", "pid", "img_url", unique=True) + + @classmethod + async def add_setu_data( + cls, + local_id: int, + title: str, + author: str, + pid: int, + img_hash: str, + img_url: str, + tags: str, + ): + """ + 说明: + 添加一份色图数据 + 参数: + :param local_id: 本地存储id + :param title: 标题 + :param author: 作者 + :param pid: 图片pid + :param img_hash: 图片hash值 + :param img_url: 图片链接 + :param tags: 图片标签 + """ + if not await cls._check_exists(pid, img_url): + await cls.create( + local_id=local_id, + title=title, + author=author, + pid=pid, + img_hash=img_hash, + img_url=img_url, + is_r18=True if "R-18" in tags else False, + tags=tags, + ) + + @classmethod + async def query_image( + cls, + local_id: Optional[int] = None, + tags: Optional[List[str]] = None, + r18: int = 0, + limit: int = 50, + ): + """ + 说明: + 通过tag查找色图 + 参数: + :param local_id: 本地色图 id + :param tags: tags + :param r18: 是否 r18,0:非r18 1:r18 2:混合 + :param limit: 获取数量 + """ + if local_id: + flag = True if r18 == 1 else False + return await cls.query.where( + (cls.local_id == local_id) & (cls.is_r18 == flag) + ).gino.first() + if r18 == 0: + query = cls.query.where(cls.is_r18 == False) + elif r18 == 1: + query = cls.query.where(cls.is_r18 == True) + else: + query = cls.query + if tags: + for tag in tags: + query = query.where(cls.tags.contains(tag) | cls.title.contains(tag) | cls.author.contains(tag)) + query = query.order_by(db.func.random()).limit(limit) + return await query.gino.all() + + @classmethod + async def get_image_count(cls, r18: int = 0) -> int: + """ + 说明: + 查询图片数量 + """ + flag = False if r18 == 0 else True + setattr(Setu, 'count', db.func.count(cls.local_id).label('count')) + count = await cls.select('count').where(cls.is_r18 == flag).gino.first() + return count[0] + + @classmethod + async def get_image_in_hash(cls, img_hash: str) -> "Setu": + """ + 说明: + 通过图像hash获取图像信息 + 参数: + :param img_hash: = 图像hash值 + """ + query = await cls.query.where(cls.img_hash == img_hash).gino.first() + return query + + @classmethod + async def _check_exists(cls, pid: int, img_url: str) -> bool: + """ + 说明: + 检测图片是否存在 + 参数: + :param pid: 图片pid + :param img_url: 图片链接 + """ + return bool( + await cls.query.where( + (cls.pid == pid) & (cls.img_url == img_url) + ).gino.first() + ) + + @classmethod + async def update_setu_data( + cls, + pid: int, + *, + local_id: Optional[int] = None, + title: Optional[str] = None, + author: Optional[str] = None, + img_hash: Optional[str] = None, + img_url: Optional[str] = None, + tags: Optional[str] = None, + ) -> bool: + """ + 说明: + 根据PID修改图片数据 + 参数: + :param local_id: 本地id + :param pid: 图片pid + :param title: 标题 + :param author: 作者 + :param img_hash: 图片hash值 + :param img_url: 图片链接 + :param tags: 图片标签 + """ + query = cls.query.where(cls.pid == pid).with_for_update() + image_list = await query.gino.all() + if image_list: + for image in image_list: + if local_id: + await image.update(local_id=local_id).apply() + if title: + await image.update(title=title).apply() + if author: + await image.update(author=author).apply() + if img_hash: + await image.update(img_hash=img_hash).apply() + if img_url: + await image.update(img_url=img_url).apply() + if tags: + await image.update(tags=tags).apply() + return True + return False + + @classmethod + async def get_all_setu(cls) -> List["Setu"]: + """ + 说明: + 获取所有图片对象 + """ + return await cls.query.gino.all() + diff --git a/plugins/send_setu_/send_setu/__init__.py b/plugins/send_setu_/send_setu/__init__.py old mode 100644 new mode 100755 index 2d4ce1fc..16b439b4 --- a/plugins/send_setu_/send_setu/__init__.py +++ b/plugins/send_setu_/send_setu/__init__.py @@ -1,368 +1,368 @@ -import random -from nonebot import on_command, on_regex -from services.log import logger -from models.sign_group_user import SignGroupUser -from nonebot.message import run_postprocessor -from nonebot.matcher import Matcher -from typing import Optional, Type -from gino.exceptions import UninitializedError -from utils.utils import ( - is_number, - get_message_text, - get_message_imgs, -) -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, - GroupMessageEvent, - PrivateMessageEvent, - Message, - Event, -) -from .data_source import ( - get_setu_list, - get_luoxiang, - search_online_setu, - get_setu_urls, - find_img_index, - gen_message, - check_local_exists_or_download, - add_data_to_database, - get_setu_count, -) -from nonebot.adapters.cqhttp.exception import ActionFailed -from configs.config import Config, NICKNAME -from utils.manager import withdraw_message_manager -import re - -try: - import ujson as json -except ModuleNotFoundError: - import json - -__zx_plugin_name__ = "色图" -__plugin_usage__ = f""" -usage: - 搜索 lolicon 图库,每日色图time... - 指令: - 色图: 随机本地色图 - 色图r: 随机在线十张r18涩图 - 色图 [id]: 本地指定id色图 - 色图 *[tags]: 在线搜索指定tag色图 - 色图r *[tags]: 同上 - [1-9]张涩图: 本地随机色图连发 - [1-9]张[tags]的涩图: 指定tag色图连发 - 示例:色图 萝莉|少女 白丝|黑丝 - 示例:色图 萝莉 猫娘 - 注: - tag至多取前20项,| 为或,萝莉|少女=萝莉或者少女 -""".strip() -__plugin_des__ = "不要小看涩图啊混蛋!" -__plugin_cmd__ = ["色图 ?[id]", "色图 ?[tags]", "色图r ?[tags]", "[1-9]张?[tags]色图"] -__plugin_type__ = ("来点好康的",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 9, - "default_status": True, - "limit_superuser": False, - "cmd": ["色图", "涩图", "瑟图"], -} -__plugin_block_limit__ = {} -__plugin_cd_limit__ = { - "rst": "您冲的太快了,请稍后再冲.", -} -__plugin_configs__ = { - "WITHDRAW_SETU_MESSAGE": { - "value": (0, 1), - "help": "自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)", - "default_value": (0, 1), - }, - "ONLY_USE_LOCAL_SETU": { - "value": False, - "help": "仅仅使用本地色图,不在线搜索", - "default_value": False, - }, - "INITIAL_SETU_PROBABILITY": { - "value": 0.7, - "help": "初始色图概率,总概率 = 初始色图概率 + 好感度", - "default_value": 0.7 - }, - "DOWNLOAD_SETU": { - "value": True, - "help": "是否存储下载的色图,使用本地色图可以加快图片发送速度", - "default_value": True - }, - "TIMEOUT": { - "value": 10, - "help": "色图下载超时限制(秒)", - "default_value": 10 - } -} -Config.add_plugin_config( - "pixiv", - "PIXIV_NGINX_URL", - "i.pixiv.re", - help_="Pixiv反向代理" -) - -setu_data_list = [] - - -@run_postprocessor -async def do_something( - matcher: Matcher, - exception: Optional[Exception], - bot: Bot, - event: Event, - state: T_State, -): - global setu_data_list - if isinstance(event, MessageEvent): - if matcher.module == "send_setu": - # 添加数据至数据库 - try: - await add_data_to_database(setu_data_list) - logger.info("色图数据自动存储数据库成功...") - setu_data_list = [] - except UninitializedError: - pass - - -setu = on_command( - "色图", aliases={"涩图", "不够色", "来一发", "再来点", "色图r"}, priority=5, block=True -) - -setu_reg = on_regex("(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图$", priority=5, block=True) - -find_setu = on_command("查色图", priority=5, block=True) - - -@setu.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if isinstance(event, GroupMessageEvent): - impression = ( - await SignGroupUser.ensure(event.user_id, event.group_id) - ).impression - luox = get_luoxiang(impression) - if luox: - await setu.finish(luox) - r18 = 0 - num = 1 - # 是否看r18 - if state["_prefix"]["raw_command"] == "色图r" and isinstance( - event, PrivateMessageEvent - ): - r18 = 1 - num = 10 - elif state["_prefix"]["raw_command"] == "色图r" and isinstance( - event, GroupMessageEvent - ): - await setu.finish( - random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"]) - ) - # 有 数字 的话先尝试本地色图id - if msg and is_number(msg): - setu_list, code = await get_setu_list(int(msg), r18=r18) - if code != 200: - await setu.finish(setu_list[0], at_sender=True) - setu_img, code = await check_local_exists_or_download(setu_list[0]) - msg_id = await setu.send(gen_message(setu_list[0]) + setu_img, at_sender=True) - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送色图 {setu_list[0].local_id}.png" - ) - if msg_id: - withdraw_message_manager.withdraw_message( - event, - msg_id["message_id"], - Config.get_config("send_setu", "WITHDRAW_SETU_MESSAGE"), - ) - return - await send_setu_handle(setu, event, state["_prefix"]["raw_command"], msg, num, r18) - - -num_key = { - "一": 1, - "二": 2, - "两": 2, - "双": 2, - "三": 3, - "四": 4, - "五": 5, - "六": 6, - "七": 7, - "八": 8, - "九": 9, -} - - -@setu_reg.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - if isinstance(event, GroupMessageEvent): - impression = ( - await SignGroupUser.ensure(event.user_id, event.group_id) - ).impression - luox = get_luoxiang(impression) - if luox: - await setu.finish(luox, at_sender=True) - msg = get_message_text(event.json()) - num = 1 - msg = re.search(r"(.*)[份发张个次点](.*)[瑟涩色]图", msg) - # 解析 tags 以及 num - if msg: - num = msg.group(1) - tags = msg.group(2) - if tags: - tags = tags[:-1] if tags[-1] == "的" else tags - if num: - num = num[-1] - if num_key.get(num): - num = num_key[num] - elif is_number(num): - try: - num = int(num) - except ValueError: - num = 1 - else: - num = 1 - else: - return - await send_setu_handle(setu_reg, event, "色图", tags, num, 0) - - -@find_setu.args_parser -async def _(bot: Bot, event: MessageEvent, state: T_State): - if str(event.message) == "取消": - await find_setu.finish("取消了操作", at_sender=True) - imgs = get_message_imgs(event.json()) - if not imgs: - await find_setu.reject("不搞错了,俺要图!") - state["img"] = imgs[0] - - -@find_setu.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - if get_message_text(event.json()) in ["帮助"]: - await find_setu.finish("通过图片获取本地色图id\n\t示例:查色图(图片)") - imgs = get_message_imgs(event.json()) - if imgs: - state["img"] = imgs[0] - - -@find_setu.got("img", prompt="速速来图!") -async def _(bot: Bot, event: MessageEvent, state: T_State): - img = state["img"] - await find_setu.send(await find_img_index(img, event.user_id), at_sender=True) - - -async def send_setu_handle( - matcher: Type[Matcher], - event: MessageEvent, - command: str, - msg: str, - num: int, - r18: int, -): - global setu_data_list - # 非 id,在线搜索 - tags = msg.split() - # 真寻的色图?怎么可能 - if f"{NICKNAME}" in tags: - await matcher.finish("咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀") - # 本地先拿图,下载失败补上去 - setu_list, code = None, 200 - setu_count = await get_setu_count(r18) - if ( - not Config.get_config("send_setu", "ONLY_USE_LOCAL_SETU") and tags - ) or setu_count <= 0: - # 先尝试获取在线图片 - urls, text_list, add_databases_list, code = await get_setu_urls( - tags, num, r18, command - ) - for x in add_databases_list: - setu_data_list.append(x) - # 未找到符合的色图,想来本地应该也没有 - if code == 401: - await setu.finish(urls[0], at_sender=True) - if code == 200: - for i in range(len(urls)): - try: - setu_img, index = await search_online_setu(urls[i]) - # 下载成功的话 - if index != -1: - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送色图 {index}.png" - ) - msg_id = await matcher.send( - Message(f"{text_list[i]}\n{setu_img}") - ) - else: - if setu_list is None: - setu_list, code = await get_setu_list(tags=tags, r18=r18) - if code != 200: - await setu.finish(setu_list[0], at_sender=True) - if setu_list: - setu_image = random.choice(setu_list) - setu_list.remove(setu_image) - msg_id = await matcher.send( - Message( - gen_message(setu_image) - + ( - await check_local_exists_or_download(setu_image) - )[0] - ) - ) - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送本地色图 {setu_image.local_id}.png" - ) - else: - msg_id = await matcher.send(text_list[i] + "\n" + setu_img) - if msg_id: - withdraw_message_manager.withdraw_message( - event, - msg_id["message_id"], - Config.get_config("send_setu", "WITHDRAW_SETU_MESSAGE"), - ) - except ActionFailed: - await matcher.finish("坏了,这张图色过头了,我自己看看就行了!", at_sender=True) - return - if code != 200: - await matcher.finish("网络连接失败...", at_sender=True) - # 本地无图 - if setu_list is None: - setu_list, code = await get_setu_list(tags=tags, r18=r18) - if code != 200: - await matcher.finish(setu_list[0], at_sender=True) - # 开始发图 - for _ in range(num): - if not setu_list: - await setu.finish("坏了,已经没图了,被榨干了!") - setu_image = random.choice(setu_list) - setu_list.remove(setu_image) - try: - msg_id = await matcher.send( - Message( - gen_message(setu_image) - + (await check_local_exists_or_download(setu_image))[0] - ) - ) - withdraw_message_manager.withdraw_message( - event, - msg_id["message_id"], - Config.get_config("send_setu", "WITHDRAW_SETU_MESSAGE"), - ) - logger.info( - f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" - f" 发送本地色图 {setu_image.local_id}.png" - ) - except ActionFailed: - await matcher.finish("坏了,这张图色过头了,我自己看看就行了!", at_sender=True) +import random +from nonebot import on_command, on_regex +from services.log import logger +from models.sign_group_user import SignGroupUser +from nonebot.message import run_postprocessor +from nonebot.matcher import Matcher +from typing import Optional, Type +from gino.exceptions import UninitializedError +from utils.utils import ( + is_number, + get_message_text, + get_message_imgs, +) +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent, + PrivateMessageEvent, + Message, + Event, +) +from .data_source import ( + get_setu_list, + get_luoxiang, + search_online_setu, + get_setu_urls, + find_img_index, + gen_message, + check_local_exists_or_download, + add_data_to_database, + get_setu_count, +) +from nonebot.adapters.cqhttp.exception import ActionFailed +from configs.config import Config, NICKNAME +from utils.manager import withdraw_message_manager +import re + +try: + import ujson as json +except ModuleNotFoundError: + import json + +__zx_plugin_name__ = "色图" +__plugin_usage__ = f""" +usage: + 搜索 lolicon 图库,每日色图time... + 指令: + 色图: 随机本地色图 + 色图r: 随机在线十张r18涩图 + 色图 [id]: 本地指定id色图 + 色图 *[tags]: 在线搜索指定tag色图 + 色图r *[tags]: 同上 + [1-9]张涩图: 本地随机色图连发 + [1-9]张[tags]的涩图: 指定tag色图连发 + 示例:色图 萝莉|少女 白丝|黑丝 + 示例:色图 萝莉 猫娘 + 注: + tag至多取前20项,| 为或,萝莉|少女=萝莉或者少女 +""".strip() +__plugin_des__ = "不要小看涩图啊混蛋!" +__plugin_cmd__ = ["色图 ?[id]", "色图 ?[tags]", "色图r ?[tags]", "[1-9]张?[tags]色图"] +__plugin_type__ = ("来点好康的",) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 9, + "default_status": True, + "limit_superuser": False, + "cmd": ["色图", "涩图", "瑟图"], +} +__plugin_block_limit__ = {} +__plugin_cd_limit__ = { + "rst": "您冲的太快了,请稍后再冲.", +} +__plugin_configs__ = { + "WITHDRAW_SETU_MESSAGE": { + "value": (0, 1), + "help": "自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)", + "default_value": (0, 1), + }, + "ONLY_USE_LOCAL_SETU": { + "value": False, + "help": "仅仅使用本地色图,不在线搜索", + "default_value": False, + }, + "INITIAL_SETU_PROBABILITY": { + "value": 0.7, + "help": "初始色图概率,总概率 = 初始色图概率 + 好感度", + "default_value": 0.7 + }, + "DOWNLOAD_SETU": { + "value": True, + "help": "是否存储下载的色图,使用本地色图可以加快图片发送速度", + "default_value": True + }, + "TIMEOUT": { + "value": 10, + "help": "色图下载超时限制(秒)", + "default_value": 10 + } +} +Config.add_plugin_config( + "pixiv", + "PIXIV_NGINX_URL", + "i.pixiv.re", + help_="Pixiv反向代理" +) + +setu_data_list = [] + + +@run_postprocessor +async def do_something( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: Event, + state: T_State, +): + global setu_data_list + if isinstance(event, MessageEvent): + if matcher.module == "send_setu": + # 添加数据至数据库 + try: + await add_data_to_database(setu_data_list) + logger.info("色图数据自动存储数据库成功...") + setu_data_list = [] + except UninitializedError: + pass + + +setu = on_command( + "色图", aliases={"涩图", "不够色", "来一发", "再来点", "色图r"}, priority=5, block=True +) + +setu_reg = on_regex("(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图$", priority=5, block=True) + +find_setu = on_command("查色图", priority=5, block=True) + + +@setu.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if isinstance(event, GroupMessageEvent): + impression = ( + await SignGroupUser.ensure(event.user_id, event.group_id) + ).impression + luox = get_luoxiang(impression) + if luox: + await setu.finish(luox) + r18 = 0 + num = 1 + # 是否看r18 + if state["_prefix"]["raw_command"] == "色图r" and isinstance( + event, PrivateMessageEvent + ): + r18 = 1 + num = 10 + elif state["_prefix"]["raw_command"] == "色图r" and isinstance( + event, GroupMessageEvent + ): + await setu.finish( + random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"]) + ) + # 有 数字 的话先尝试本地色图id + if msg and is_number(msg): + setu_list, code = await get_setu_list(int(msg), r18=r18) + if code != 200: + await setu.finish(setu_list[0], at_sender=True) + setu_img, code = await check_local_exists_or_download(setu_list[0]) + msg_id = await setu.send(gen_message(setu_list[0]) + setu_img, at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送色图 {setu_list[0].local_id}.png" + ) + if msg_id: + withdraw_message_manager.withdraw_message( + event, + msg_id["message_id"], + Config.get_config("send_setu", "WITHDRAW_SETU_MESSAGE"), + ) + return + await send_setu_handle(setu, event, state["_prefix"]["raw_command"], msg, num, r18) + + +num_key = { + "一": 1, + "二": 2, + "两": 2, + "双": 2, + "三": 3, + "四": 4, + "五": 5, + "六": 6, + "七": 7, + "八": 8, + "九": 9, +} + + +@setu_reg.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if isinstance(event, GroupMessageEvent): + impression = ( + await SignGroupUser.ensure(event.user_id, event.group_id) + ).impression + luox = get_luoxiang(impression) + if luox: + await setu.finish(luox, at_sender=True) + msg = get_message_text(event.json()) + num = 1 + msg = re.search(r"(.*)[份发张个次点](.*)[瑟涩色]图", msg) + # 解析 tags 以及 num + if msg: + num = msg.group(1) + tags = msg.group(2) + if tags: + tags = tags[:-1] if tags[-1] == "的" else tags + if num: + num = num[-1] + if num_key.get(num): + num = num_key[num] + elif is_number(num): + try: + num = int(num) + except ValueError: + num = 1 + else: + num = 1 + else: + return + await send_setu_handle(setu_reg, event, "色图", tags, num, 0) + + +@find_setu.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.message) == "取消": + await find_setu.finish("取消了操作", at_sender=True) + imgs = get_message_imgs(event.json()) + if not imgs: + await find_setu.reject("不搞错了,俺要图!") + state["img"] = imgs[0] + + +@find_setu.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if get_message_text(event.json()) in ["帮助"]: + await find_setu.finish("通过图片获取本地色图id\n\t示例:查色图(图片)") + imgs = get_message_imgs(event.json()) + if imgs: + state["img"] = imgs[0] + + +@find_setu.got("img", prompt="速速来图!") +async def _(bot: Bot, event: MessageEvent, state: T_State): + img = state["img"] + await find_setu.send(await find_img_index(img, event.user_id), at_sender=True) + + +async def send_setu_handle( + matcher: Type[Matcher], + event: MessageEvent, + command: str, + msg: str, + num: int, + r18: int, +): + global setu_data_list + # 非 id,在线搜索 + tags = msg.split() + # 真寻的色图?怎么可能 + if f"{NICKNAME}" in tags: + await matcher.finish("咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀") + # 本地先拿图,下载失败补上去 + setu_list, code = None, 200 + setu_count = await get_setu_count(r18) + if ( + not Config.get_config("send_setu", "ONLY_USE_LOCAL_SETU") and tags + ) or setu_count <= 0: + # 先尝试获取在线图片 + urls, text_list, add_databases_list, code = await get_setu_urls( + tags, num, r18, command + ) + for x in add_databases_list: + setu_data_list.append(x) + # 未找到符合的色图,想来本地应该也没有 + if code == 401: + await setu.finish(urls[0], at_sender=True) + if code == 200: + for i in range(len(urls)): + try: + setu_img, index = await search_online_setu(urls[i]) + # 下载成功的话 + if index != -1: + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送色图 {index}.png" + ) + msg_id = await matcher.send( + Message(f"{text_list[i]}\n{setu_img}") + ) + else: + if setu_list is None: + setu_list, code = await get_setu_list(tags=tags, r18=r18) + if code != 200: + await setu.finish(setu_list[0], at_sender=True) + if setu_list: + setu_image = random.choice(setu_list) + setu_list.remove(setu_image) + msg_id = await matcher.send( + Message( + gen_message(setu_image) + + ( + await check_local_exists_or_download(setu_image) + )[0] + ) + ) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送本地色图 {setu_image.local_id}.png" + ) + else: + msg_id = await matcher.send(text_list[i] + "\n" + setu_img) + if msg_id: + withdraw_message_manager.withdraw_message( + event, + msg_id["message_id"], + Config.get_config("send_setu", "WITHDRAW_SETU_MESSAGE"), + ) + except ActionFailed: + await matcher.finish("坏了,这张图色过头了,我自己看看就行了!", at_sender=True) + return + if code != 200: + await matcher.finish("网络连接失败...", at_sender=True) + # 本地无图 + if setu_list is None: + setu_list, code = await get_setu_list(tags=tags, r18=r18) + if code != 200: + await matcher.finish(setu_list[0], at_sender=True) + # 开始发图 + for _ in range(num): + if not setu_list: + await setu.finish("坏了,已经没图了,被榨干了!") + setu_image = random.choice(setu_list) + setu_list.remove(setu_image) + try: + msg_id = await matcher.send( + Message( + gen_message(setu_image) + + (await check_local_exists_or_download(setu_image))[0] + ) + ) + withdraw_message_manager.withdraw_message( + event, + msg_id["message_id"], + Config.get_config("send_setu", "WITHDRAW_SETU_MESSAGE"), + ) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送本地色图 {setu_image.local_id}.png" + ) + except ActionFailed: + await matcher.finish("坏了,这张图色过头了,我自己看看就行了!", at_sender=True) diff --git a/plugins/send_setu_/send_setu/data_source.py b/plugins/send_setu_/send_setu/data_source.py old mode 100644 new mode 100755 index acb9590f..99732714 --- a/plugins/send_setu_/send_setu/data_source.py +++ b/plugins/send_setu_/send_setu/data_source.py @@ -1,278 +1,269 @@ -from configs.path_config import IMAGE_PATH -from utils.message_builder import image -from services.log import logger -from aiohttp.client_exceptions import ClientConnectorError -from utils.image_utils import get_img_hash, compressed_image -from asyncpg.exceptions import UniqueViolationError -from utils.utils import get_local_proxy -from asyncio.exceptions import TimeoutError -from typing import List, Optional -from configs.config import NICKNAME, Config -from ..model import Setu -import aiohttp -import aiofiles -import asyncio -import os -import random - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -url = "https://api.lolicon.app/setu/v2" -path = "_setu/" -r18_path = "_r18/" - - -# 获取url -async def get_setu_urls( - tags: List[str], num: int = 1, r18: int = 0, command: str = "" -) -> "List[str], List[str], List[tuple], int": - tags = tags[:3] if len(tags) > 3 else tags - params = { - "r18": r18, # 添加r18参数 0为否,1为是,2为混合 - "tag": tags, # 若指定tag - "num": 100, # 一次返回的结果数量 - "size": ["original"], - } - async with aiohttp.ClientSession() as session: - for count in range(3): - logger.info(f"get_setu_url: count --> {count}") - try: - async with session.get( - url, proxy=get_local_proxy(), timeout=Config.get_config("send_setu", "TIMEOUT"), params=params - ) as response: - if response.status == 200: - data = await response.json() - if not data["error"]: - data = data["data"] - ( - urls, - text_list, - add_databases_list, - ) = await asyncio.get_event_loop().run_in_executor( - None, _setu_data_process, data, command - ) - num = num if num < len(data) else len(data) - random_idx = random.sample(range(len(data)), num) - x_urls = [] - x_text_lst = [] - for x in random_idx: - x_urls.append(urls[x]) - x_text_lst.append(text_list[x]) - if not x_urls: - return ["没找到符合条件的色图..."], [], [], 401 - return x_urls, x_text_lst, add_databases_list, 200 - else: - return ["没找到符合条件的色图..."], [], [], 401 - except (TimeoutError, ClientConnectorError): - pass - return ["我网线被人拔了..QAQ"], [], [], 999 - - -headers = { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" - " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", - "Referer": "https://www.pixiv.net", -} - - -async def search_online_setu( - url_: str, id_: Optional[int] = None, path_: Optional[str] = None -) -> "MessageSegment, int": - """ - 下载色图 - :param url_: 色图url - :param id_: 本地id - :param path_: 存储路径 - """ - ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") - if ws_url: - if ws_url.startswith("http"): - ws_url = ws_url.split("//")[-1] - url_ = url_.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url) - async with aiohttp.ClientSession(headers=headers) as session: - for i in range(3): - logger.info(f"search_online_setu --> {i}") - try: - async with session.get(url_, proxy=get_local_proxy(), timeout=Config.get_config("send_setu", "TIMEOUT")) as res: - if res.status == 200: - index = random.randint(1, 100000) if id_ is None else id_ - path_ = "temp" if not path_ else path_ - file = f"{index}_temp_setu.jpg" if not path_ else f"{index}.jpg" - if not os.path.exists(f"{IMAGE_PATH}/{path_}"): - os.mkdir(f"{IMAGE_PATH}/{path_}") - async with aiofiles.open( - f"{IMAGE_PATH}/{path_}/{file}", "wb" - ) as f: - try: - await f.write(await res.read()) - except TimeoutError: - continue - if id_ is not None: - if ( - os.path.getsize(f"{IMAGE_PATH}/{path_}/{index}.jpg") - > 1024 * 1024 * 1.5 - ): - compressed_image( - f"{IMAGE_PATH}/{path_}/{index}.jpg", - ) - logger.info(f"下载 lolicon图片 {url_} 成功, id:{index}") - return image(file, path_), index - else: - logger.warning(f"访问 lolicon图片 {url_} 失败 status:{res.status}") - # return '\n这图好难下载啊!QAQ', -1, False - except (TimeoutError, ClientConnectorError): - pass - return "图片被小怪兽恰掉啦..!QAQ", -1 - - -# 检测本地是否有id涩图,无的话则下载 -async def check_local_exists_or_download(setu_image: Setu) -> "MessageSegment, int": - path_ = None - id_ = None - if Config.get_config("send_setu", "DOWNLOAD_SETU"): - id_ = setu_image.local_id - if setu_image.is_r18: - path_ = "_r18" - else: - path_ = path - if os.path.exists(f"{IMAGE_PATH}/{path_}/{setu_image.local_id}.jpg"): - return image(f"{setu_image.local_id}.jpg", path_), 200 - return await search_online_setu(setu_image.img_url, id_, path_) - - -# 添加涩图数据到数据库 -async def add_data_to_database(lst: List[tuple]): - tmp = [] - for x in lst: - if x not in tmp: - tmp.append(x) - if tmp: - for x in tmp: - try: - r18 = 1 if "R-18" in x[5] else 0 - idx = await Setu.get_image_count(r18) - await Setu.add_setu_data( - idx, - x[0], - x[1], - x[2], - x[3], - x[4], - x[5], - ) - except UniqueViolationError: - pass - - -# 拿到本地色图列表 -async def get_setu_list( - index: Optional[int] = None, tags: Optional[List[str]] = None, r18: int = 0 -) -> "list, int": - if index: - image_count = await Setu.get_image_count(r18) - 1 - if index < 0 or index > image_count: - return [f"超过当前上下限!({image_count})"], 999 - image_list = [await Setu.query_image(index, r18=r18)] - elif tags: - image_list = await Setu.query_image(tags=tags, r18=r18) - else: - image_list = await Setu.query_image(r18=r18) - if not image_list: - return ["没找到符合条件的色图..."], 998 - return image_list, 200 - - -# 初始化消息 -def gen_message(setu_image: Setu, img_msg: bool = False) -> str: - local_id = setu_image.local_id - title = setu_image.title - author = setu_image.author - pid = setu_image.pid - return ( - f"id:{local_id}\n" - f"title:{title}\n" - f"author:{author}\n" - f"PID:{pid}\n" - f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}" - ) - - -# 罗翔老师! -def get_luoxiang(impression): - probability = ( - impression + Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100 - ) - if probability < random.randint(1, 101): - return ( - "我为什么要给你发这个?" - + image(random.choice(os.listdir(IMAGE_PATH + "luoxiang/")), "luoxiang") - + f"\n(快向{NICKNAME}签到提升好感度吧!)" - ) - return None - - -async def get_setu_count(r18: int) -> int: - """ - 获取色图数量 - :param r18: r18类型 - """ - return await Setu.get_image_count(r18) - - -async def find_img_index(img_url, user_id): - async with aiohttp.ClientSession() as session: - async with session.get(img_url, proxy=get_local_proxy(), timeout=Config.get_config("send_setu", "TIMEOUT")) as res: - async with aiofiles.open( - IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg", "wb" - ) as f: - await f.write(await res.read()) - img_hash = str(get_img_hash(IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg")) - setu_img = await Setu.get_image_in_hash(img_hash) - if setu_img: - return ( - f"id:{setu_img.local_id}\n" - f"title:{setu_img.title}\n" - f"author:{setu_img.author}\n" - f"PID:{setu_img.pid}" - ) - return "该图不在色图库中或色图库未更新!" - - -# 处理色图数据 -def _setu_data_process(data: dict, command: str) -> "list, list, list": - urls = [] - text_list = [] - add_databases_list = [] - for i in range(len(data)): - img_url = data[i]["urls"]["original"] - img_url = ( - img_url.replace("i.pixiv.cat", "i.pximg.net") - if "i.pixiv.cat" in img_url - else img_url - ) - title = data[i]["title"] - author = data[i]["author"] - pid = data[i]["pid"] - urls.append(img_url) - text_list.append(f"title:{title}\nauthor:{author}\nPID:{pid}") - tags = [] - for j in range(len(data[i]["tags"])): - tags.append(data[i]["tags"][j]) - if command != "色图r": - if "R-18" in tags: - tags.remove("R-18") - add_databases_list.append( - ( - title, - author, - pid, - "", - img_url, - ",".join(tags), - ) - ) - return urls, text_list, add_databases_list +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from services.log import logger +from utils.image_utils import get_img_hash, compressed_image +from asyncpg.exceptions import UniqueViolationError +from asyncio.exceptions import TimeoutError +from typing import List, Optional +from configs.config import NICKNAME, Config +from utils.http_utils import AsyncHttpx +from ..model import Setu +import asyncio +import os +import random + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +url = "https://api.lolicon.app/setu/v2" +path = "_setu/" +r18_path = "_r18/" + + +# 获取url +async def get_setu_urls( + tags: List[str], num: int = 1, r18: int = 0, command: str = "" +) -> "List[str], List[str], List[tuple], int": + tags = tags[:3] if len(tags) > 3 else tags + params = { + "r18": r18, # 添加r18参数 0为否,1为是,2为混合 + "tag": tags, # 若指定tag + "num": 100, # 一次返回的结果数量 + "size": ["original"], + } + for count in range(3): + logger.info(f"get_setu_url: count --> {count}") + try: + response = await AsyncHttpx.get( + url, timeout=Config.get_config("send_setu", "TIMEOUT"), params=params + ) + if response.status_code == 200: + data = await response.json() + if not data["error"]: + data = data["data"] + ( + urls, + text_list, + add_databases_list, + ) = await asyncio.get_event_loop().run_in_executor( + None, _setu_data_process, data, command + ) + num = num if num < len(data) else len(data) + random_idx = random.sample(range(len(data)), num) + x_urls = [] + x_text_lst = [] + for x in random_idx: + x_urls.append(urls[x]) + x_text_lst.append(text_list[x]) + if not x_urls: + return ["没找到符合条件的色图..."], [], [], 401 + return x_urls, x_text_lst, add_databases_list, 200 + else: + return ["没找到符合条件的色图..."], [], [], 401 + except TimeoutError: + pass + except Exception as e: + logger.error(f"send_setu 访问页面错误 {type(e)}:{e}") + return ["我网线被人拔了..QAQ"], [], [], 999 + + +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} + + +async def search_online_setu( + url_: str, id_: Optional[int] = None, path_: Optional[str] = None +) -> "MessageSegment, int": + """ + 下载色图 + :param url_: 色图url + :param id_: 本地id + :param path_: 存储路径 + """ + ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") + if ws_url: + url_ = url_.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url) + for i in range(3): + logger.info(f"search_online_setu --> {i}") + try: + index = random.randint(1, 100000) if id_ is None else id_ + path_ = "temp" if not path_ else path_ + file = f"{index}_temp_setu.jpg" if not path_ else f"{index}.jpg" + if not os.path.exists(f"{IMAGE_PATH}/{path_}"): + os.mkdir(f"{IMAGE_PATH}/{path_}") + if not await AsyncHttpx.download_file( + url_, + f"{IMAGE_PATH}/{path_}/{file}", + timeout=Config.get_config("send_setu", "TIMEOUT"), + ): + continue + if id_ is not None: + if ( + os.path.getsize(f"{IMAGE_PATH}/{path_}/{index}.jpg") + > 1024 * 1024 * 1.5 + ): + compressed_image( + f"{IMAGE_PATH}/{path_}/{index}.jpg", + ) + logger.info(f"下载 lolicon图片 {url_} 成功, id:{index}") + return image(file, path_), index + except TimeoutError: + pass + except Exception as e: + logger.error(f"send_setu 下载图片错误 {type(e)}:{e}") + return "图片被小怪兽恰掉啦..!QAQ", -1 + + +# 检测本地是否有id涩图,无的话则下载 +async def check_local_exists_or_download(setu_image: Setu) -> "MessageSegment, int": + path_ = None + id_ = None + if Config.get_config("send_setu", "DOWNLOAD_SETU"): + id_ = setu_image.local_id + if setu_image.is_r18: + path_ = "_r18" + else: + path_ = path + if os.path.exists(f"{IMAGE_PATH}/{path_}/{setu_image.local_id}.jpg"): + return image(f"{setu_image.local_id}.jpg", path_), 200 + return await search_online_setu(setu_image.img_url, id_, path_) + + +# 添加涩图数据到数据库 +async def add_data_to_database(lst: List[tuple]): + tmp = [] + for x in lst: + if x not in tmp: + tmp.append(x) + if tmp: + for x in tmp: + try: + r18 = 1 if "R-18" in x[5] else 0 + idx = await Setu.get_image_count(r18) + await Setu.add_setu_data( + idx, + x[0], + x[1], + x[2], + x[3], + x[4], + x[5], + ) + except UniqueViolationError: + pass + + +# 拿到本地色图列表 +async def get_setu_list( + index: Optional[int] = None, tags: Optional[List[str]] = None, r18: int = 0 +) -> "list, int": + if index: + image_count = await Setu.get_image_count(r18) - 1 + if index < 0 or index > image_count: + return [f"超过当前上下限!({image_count})"], 999 + image_list = [await Setu.query_image(index, r18=r18)] + elif tags: + image_list = await Setu.query_image(tags=tags, r18=r18) + else: + image_list = await Setu.query_image(r18=r18) + if not image_list: + return ["没找到符合条件的色图..."], 998 + return image_list, 200 + + +# 初始化消息 +def gen_message(setu_image: Setu, img_msg: bool = False) -> str: + local_id = setu_image.local_id + title = setu_image.title + author = setu_image.author + pid = setu_image.pid + return ( + f"id:{local_id}\n" + f"title:{title}\n" + f"author:{author}\n" + f"PID:{pid}\n" + f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}" + ) + + +# 罗翔老师! +def get_luoxiang(impression): + probability = ( + impression + Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100 + ) + if probability < random.randint(1, 101): + return ( + "我为什么要给你发这个?" + + image(random.choice(os.listdir(IMAGE_PATH + "luoxiang/")), "luoxiang") + + f"\n(快向{NICKNAME}签到提升好感度吧!)" + ) + return None + + +async def get_setu_count(r18: int) -> int: + """ + 获取色图数量 + :param r18: r18类型 + """ + return await Setu.get_image_count(r18) + + +async def find_img_index(img_url, user_id): + if not await AsyncHttpx.download_file( + img_url, + IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg", + timeout=Config.get_config("send_setu", "TIMEOUT"), + ): + return "检索图片下载上失败..." + img_hash = str(get_img_hash(IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg")) + setu_img = await Setu.get_image_in_hash(img_hash) + if setu_img: + return ( + f"id:{setu_img.local_id}\n" + f"title:{setu_img.title}\n" + f"author:{setu_img.author}\n" + f"PID:{setu_img.pid}" + ) + return "该图不在色图库中或色图库未更新!" + + +# 处理色图数据 +def _setu_data_process(data: dict, command: str) -> "list, list, list": + urls = [] + text_list = [] + add_databases_list = [] + for i in range(len(data)): + img_url = data[i]["urls"]["original"] + img_url = ( + img_url.replace("i.pixiv.cat", "i.pximg.net") + if "i.pixiv.cat" in img_url + else img_url + ) + title = data[i]["title"] + author = data[i]["author"] + pid = data[i]["pid"] + urls.append(img_url) + text_list.append(f"title:{title}\nauthor:{author}\nPID:{pid}") + tags = [] + for j in range(len(data[i]["tags"])): + tags.append(data[i]["tags"][j]) + if command != "色图r": + if "R-18" in tags: + tags.remove("R-18") + add_databases_list.append( + ( + title, + author, + pid, + "", + img_url, + ",".join(tags), + ) + ) + return urls, text_list, add_databases_list diff --git a/plugins/send_setu_/update_setu/__init__.py b/plugins/send_setu_/update_setu/__init__.py old mode 100644 new mode 100755 index aa048f9b..ff9018b4 --- a/plugins/send_setu_/update_setu/__init__.py +++ b/plugins/send_setu_/update_setu/__init__.py @@ -1,48 +1,48 @@ -from utils.utils import scheduler -from nonebot import on_command -from nonebot.permission import SUPERUSER -from nonebot.typing import T_State -from nonebot.adapters import Bot, Event -from nonebot.rule import to_me -from .data_source import update_setu_img -from configs.config import Config - - -__zx_plugin_name__ = "更新色图 [Superuser]" -__plugin_usage__ = """ -usage: - 更新数据库内存在的色图 - 指令: - 更新色图 -""".strip() -__plugin_cmd__ = ["更新色图"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_block_limit__ = { - "rst": "色图正在更新..." -} - - -update_setu = on_command( - "更新色图", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) - - -@update_setu.handle() -async def _(bot: Bot, event: Event, state: T_State): - if Config.get_config("send_setu", "DOWNLOAD_SETU"): - await update_setu.send("开始更新色图...", at_sender=True) - await update_setu.send(await update_setu_img(), at_sender=True) - else: - await update_setu.finish("更新色图配置未开启") - - -# 更新色图 -@scheduler.scheduled_job( - "cron", - hour=4, - minute=30, -) -async def _(): - if Config.get_config("send_setu", "DOWNLOAD_SETU"): - await update_setu_img() +from utils.utils import scheduler +from nonebot import on_command +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from nonebot.adapters import Bot, Event +from nonebot.rule import to_me +from .data_source import update_setu_img +from configs.config import Config + + +__zx_plugin_name__ = "更新色图 [Superuser]" +__plugin_usage__ = """ +usage: + 更新数据库内存在的色图 + 指令: + 更新色图 +""".strip() +__plugin_cmd__ = ["更新色图"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_block_limit__ = { + "rst": "色图正在更新..." +} + + +update_setu = on_command( + "更新色图", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) + + +@update_setu.handle() +async def _(bot: Bot, event: Event, state: T_State): + if Config.get_config("send_setu", "DOWNLOAD_SETU"): + await update_setu.send("开始更新色图...", at_sender=True) + await update_setu.send(await update_setu_img(), at_sender=True) + else: + await update_setu.finish("更新色图配置未开启") + + +# 更新色图 +@scheduler.scheduled_job( + "cron", + hour=4, + minute=30, +) +async def _(): + if Config.get_config("send_setu", "DOWNLOAD_SETU"): + await update_setu_img() diff --git a/plugins/send_setu_/update_setu/data_source.py b/plugins/send_setu_/update_setu/data_source.py old mode 100644 new mode 100755 index 92d81355..67d83c43 --- a/plugins/send_setu_/update_setu/data_source.py +++ b/plugins/send_setu_/update_setu/data_source.py @@ -1,177 +1,163 @@ -from configs.path_config import IMAGE_PATH, TEXT_PATH, TEMP_PATH -from services.log import logger -from datetime import datetime -from utils.image_utils import compressed_image, get_img_hash -from utils.utils import get_bot, get_local_proxy -from asyncio.exceptions import TimeoutError -from ..model import Setu -from aiohttp.client_exceptions import ClientConnectorError -from asyncpg.exceptions import UniqueViolationError -from configs.config import Config -from pathlib import Path -from nonebot import Driver -import nonebot -import aiofiles -import aiohttp -import os -import ujson as json -import shutil - -driver: Driver = nonebot.get_driver() - -_path = Path(IMAGE_PATH) - - -# 替换旧色图数据,修复local_id一直是50的问题 -@driver.on_startup -async def update_old_setu_data(): - path = Path(TEXT_PATH) - setu_data_file = path / "setu_data.json" - r18_data_file = path / "r18_setu_data.json" - if setu_data_file.exists() or r18_data_file.exists(): - index = 0 - r18_index = 0 - count = 0 - fail_count = 0 - for file in [setu_data_file, r18_data_file]: - if file.exists(): - data = json.load(open(file, "r", encoding="utf8")) - for x in data: - if file == setu_data_file: - idx = index - if 'R-18' in data[x]["tags"]: - data[x]["tags"].remove('R-18') - else: - idx = r18_index - img_url = ( - data[x]["img_url"].replace("i.pixiv.cat", "i.pximg.net") - if "i.pixiv.cat" in data[x]["img_url"] - else data[x]["img_url"] - ) - # idx = r18_index if 'R-18' in data[x]["tags"] else index - try: - await Setu.add_setu_data( - idx, - data[x]["title"], - data[x]["author"], - data[x]["pid"], - data[x]["img_hash"], - img_url, - ",".join(data[x]["tags"]), - ) - count += 1 - if 'R-18' in data[x]["tags"]: - r18_index += 1 - else: - index += 1 - logger.info(f'添加旧色图数据成功 PID:{data[x]["pid"]} index:{idx}....') - except UniqueViolationError: - fail_count += 1 - logger.info(f'添加旧色图数据失败,色图重复 PID:{data[x]["pid"]} index:{idx}....') - file.unlink() - setu_url_path = path / "setu_url.json" - setu_r18_url_path = path / "setu_r18_url.json" - if setu_url_path.exists(): - setu_url_path.unlink() - if setu_r18_url_path.exists(): - setu_r18_url_path.unlink() - logger.info(f"更新旧色图数据完成,成功更新数据:{count} 条,累计失败:{fail_count} 条") - - -# 删除色图rar文件夹 -shutil.rmtree(Path(IMAGE_PATH) / "setu_rar", ignore_errors=True) -shutil.rmtree(Path(IMAGE_PATH) / "r18_rar", ignore_errors=True) -shutil.rmtree(Path(IMAGE_PATH) / "rar", ignore_errors=True) - -headers = { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" - " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", - "Referer": "https://www.pixiv.net", -} - - -async def update_setu_img(): - image_list = await Setu.get_all_setu() - image_list.reverse() - _success = 0 - error_info = [] - error_type = [] - count = 0 - async with aiohttp.ClientSession(headers=headers) as session: - for image in image_list: - count += 1 - path = _path / "_r18" if image.is_r18 else _path / "_setu" - rar_path = Path(TEMP_PATH) - local_image = path / f"{image.local_id}.jpg" - path.mkdir(exist_ok=True, parents=True) - rar_path.mkdir(exist_ok=True, parents=True) - if not local_image.exists() or not image.img_hash: - url_ = image.img_url - ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") - if ws_url.startswith("http"): - ws_url = ws_url.split("//")[-1] - url_ = url_.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url) - for _ in range(3): - try: - async with session.get( - url_, proxy=get_local_proxy(), timeout=30 - ) as response: - if response.status == 200: - async with aiofiles.open( - rar_path / f'{image.local_id}.jpg', - "wb", - ) as f: - await f.write(await response.read()) - _success += 1 - try: - if ( - os.path.getsize( - rar_path / f'{image.local_id}.jpg', - ) - > 1024 * 1024 * 1.5 - ): - compressed_image( - rar_path / f"{image.local_id}.jpg", - path / f"{image.local_id}.jpg" - ) - else: - logger.info( - f"不需要压缩,移动图片{rar_path}/{image.local_id}.jpg " - f"--> /{path}/{image.local_id}.jpg" - ) - os.rename( - f"{rar_path}/{image.local_id}.jpg", - f"{path}/{image.local_id}.jpg", - ) - except FileNotFoundError: - logger.warning(f"文件 {image.local_id}.jpg 不存在,跳过...") - continue - img_hash = str( - get_img_hash( - f"{path}/{image.local_id}.jpg" - ) - ) - await Setu.update_setu_data( - image.pid, img_hash=img_hash - ) - break - except (TimeoutError, ClientConnectorError) as e: - logger.warning(f"{image.local_id}.jpg 更新失败 ..{type(e)}:{e}") - except Exception as e: - logger.error(f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}") - if type(e) not in error_type: - error_type.append(type(e)) - error_info.append( - f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}" - ) - else: - logger.info(f'更新色图 {image.local_id}.jpg 已存在') - error_info = ['无报错..'] if not error_info else error_info - if count or _success or (error_info and "无报错.." not in error_info): - await get_bot().send_private_msg( - user_id=int(list(get_bot().config.superusers)[0]), - message=f'{str(datetime.now()).split(".")[0]} 更新 色图 完成,本地存在 {count} 张,实际更新 {_success} 张,以下为更新时未知错误:\n' - + "\n".join(error_info), - ) - - +from configs.path_config import IMAGE_PATH, TEXT_PATH, TEMP_PATH +from services.log import logger +from datetime import datetime +from utils.image_utils import compressed_image, get_img_hash +from utils.utils import get_bot +from asyncio.exceptions import TimeoutError +from ..model import Setu +from asyncpg.exceptions import UniqueViolationError +from configs.config import Config +from utils.http_utils import AsyncHttpx +from pathlib import Path +from nonebot import Driver +import nonebot +import os +import ujson as json +import shutil + +driver: Driver = nonebot.get_driver() + +_path = Path(IMAGE_PATH) + + +# 替换旧色图数据,修复local_id一直是50的问题 +@driver.on_startup +async def update_old_setu_data(): + path = Path(TEXT_PATH) + setu_data_file = path / "setu_data.json" + r18_data_file = path / "r18_setu_data.json" + if setu_data_file.exists() or r18_data_file.exists(): + index = 0 + r18_index = 0 + count = 0 + fail_count = 0 + for file in [setu_data_file, r18_data_file]: + if file.exists(): + data = json.load(open(file, "r", encoding="utf8")) + for x in data: + if file == setu_data_file: + idx = index + if 'R-18' in data[x]["tags"]: + data[x]["tags"].remove('R-18') + else: + idx = r18_index + img_url = ( + data[x]["img_url"].replace("i.pixiv.cat", "i.pximg.net") + if "i.pixiv.cat" in data[x]["img_url"] + else data[x]["img_url"] + ) + # idx = r18_index if 'R-18' in data[x]["tags"] else index + try: + await Setu.add_setu_data( + idx, + data[x]["title"], + data[x]["author"], + data[x]["pid"], + data[x]["img_hash"], + img_url, + ",".join(data[x]["tags"]), + ) + count += 1 + if 'R-18' in data[x]["tags"]: + r18_index += 1 + else: + index += 1 + logger.info(f'添加旧色图数据成功 PID:{data[x]["pid"]} index:{idx}....') + except UniqueViolationError: + fail_count += 1 + logger.info(f'添加旧色图数据失败,色图重复 PID:{data[x]["pid"]} index:{idx}....') + file.unlink() + setu_url_path = path / "setu_url.json" + setu_r18_url_path = path / "setu_r18_url.json" + if setu_url_path.exists(): + setu_url_path.unlink() + if setu_r18_url_path.exists(): + setu_r18_url_path.unlink() + logger.info(f"更新旧色图数据完成,成功更新数据:{count} 条,累计失败:{fail_count} 条") + + +# 删除色图rar文件夹 +shutil.rmtree(Path(IMAGE_PATH) / "setu_rar", ignore_errors=True) +shutil.rmtree(Path(IMAGE_PATH) / "r18_rar", ignore_errors=True) +shutil.rmtree(Path(IMAGE_PATH) / "rar", ignore_errors=True) + +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} + + +async def update_setu_img(): + image_list = await Setu.get_all_setu() + image_list.reverse() + _success = 0 + error_info = [] + error_type = [] + count = 0 + for image in image_list: + count += 1 + path = _path / "_r18" if image.is_r18 else _path / "_setu" + rar_path = Path(TEMP_PATH) + local_image = path / f"{image.local_id}.jpg" + path.mkdir(exist_ok=True, parents=True) + rar_path.mkdir(exist_ok=True, parents=True) + if not local_image.exists() or not image.img_hash: + url_ = image.img_url + ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") + if ws_url: + url_ = url_.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url) + try: + if not await AsyncHttpx.download_file(url_, rar_path / f'{image.local_id}.jpg'): + continue + _success += 1 + try: + if ( + os.path.getsize( + rar_path / f'{image.local_id}.jpg', + ) + > 1024 * 1024 * 1.5 + ): + compressed_image( + rar_path / f"{image.local_id}.jpg", + path / f"{image.local_id}.jpg" + ) + else: + logger.info( + f"不需要压缩,移动图片{rar_path}/{image.local_id}.jpg " + f"--> /{path}/{image.local_id}.jpg" + ) + os.rename( + f"{rar_path}/{image.local_id}.jpg", + f"{path}/{image.local_id}.jpg", + ) + except FileNotFoundError: + logger.warning(f"文件 {image.local_id}.jpg 不存在,跳过...") + continue + img_hash = str( + get_img_hash( + f"{path}/{image.local_id}.jpg" + ) + ) + await Setu.update_setu_data( + image.pid, img_hash=img_hash + ) + except Exception as e: + _success -= 1 + logger.error(f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}") + if type(e) not in error_type: + error_type.append(type(e)) + error_info.append( + f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}" + ) + else: + logger.info(f'更新色图 {image.local_id}.jpg 已存在') + error_info = ['无报错..'] if not error_info else error_info + if count or _success or (error_info and "无报错.." not in error_info): + await get_bot().send_private_msg( + user_id=int(list(get_bot().config.superusers)[0]), + message=f'{str(datetime.now()).split(".")[0]} 更新 色图 完成,本地存在 {count} 张,实际更新 {_success} 张,以下为更新时未知错误:\n' + + "\n".join(error_info), + ) + + diff --git a/plugins/shop/__init__.py b/plugins/shop/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/shop/buy.py b/plugins/shop/buy.py old mode 100644 new mode 100755 diff --git a/plugins/shop/gold.py b/plugins/shop/gold.py old mode 100644 new mode 100755 diff --git a/plugins/shop/models/__init__.py b/plugins/shop/models/__init__.py old mode 100644 new mode 100755 index db1ab9ef..8a0c62fb --- a/plugins/shop/models/__init__.py +++ b/plugins/shop/models/__init__.py @@ -1 +1 @@ -from .goods_info import * +from .goods_info import * diff --git a/plugins/shop/models/goods_info.py b/plugins/shop/models/goods_info.py old mode 100644 new mode 100755 diff --git a/plugins/shop/my_props.py b/plugins/shop/my_props.py old mode 100644 new mode 100755 diff --git a/plugins/shop/reset_today_gold.py b/plugins/shop/reset_today_gold.py old mode 100644 new mode 100755 diff --git a/plugins/shop/shop_handle/__init__.py b/plugins/shop/shop_handle/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/shop/shop_handle/data_source.py b/plugins/shop/shop_handle/data_source.py old mode 100644 new mode 100755 diff --git a/plugins/shop/use/__init__.py b/plugins/shop/use/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/shop/use/data_source.py b/plugins/shop/use/data_source.py old mode 100644 new mode 100755 diff --git a/plugins/sign_in/__init__.py b/plugins/sign_in/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/sign_in/config.py b/plugins/sign_in/config.py old mode 100644 new mode 100755 index 68232954..1bfa7a30 --- a/plugins/sign_in/config.py +++ b/plugins/sign_in/config.py @@ -1,65 +1,65 @@ -from configs.path_config import IMAGE_PATH -from pathlib import Path - - -SIGN_RESOURCE_PATH = Path(IMAGE_PATH) / 'sign' / 'sign_res' -SIGN_TODAY_CARD_PATH = Path(IMAGE_PATH) / 'sign' / 'today_card' -SIGN_BORDER_PATH = Path(SIGN_RESOURCE_PATH) / 'border' -SIGN_BACKGROUND_PATH = Path(SIGN_RESOURCE_PATH) / 'background' - -SIGN_BORDER_PATH.mkdir(exist_ok=True, parents=True) -SIGN_BACKGROUND_PATH.mkdir(exist_ok=True, parents=True) - - -lik2relation = { - '0': '路人', - '1': '陌生', - '2': '初识', - '3': '普通', - '4': '熟悉', - '5': '信赖', - '6': '相知', - '7': '厚谊', - '8': '亲密' -} - -level2attitude = { - '0': '排斥', - '1': '警惕', - '2': '可以交流', - '3': '一般', - '4': '是个好人', - '5': '好朋友', - '6': '可以分享小秘密', - '7': '喜欢', - '8': '恋人' -} - -weekdays = { - 1: 'Mon', - 2: 'Tue', - 3: 'Wed', - 4: 'Thu', - 5: 'Fri', - 6: 'Sat', - 7: 'Sun' -} - -lik2level = { - 9999: '9', - 400: '8', - 270: '7', - 200: '6', - 140: '5', - 90: '4', - 50: '3', - 25: '2', - 10: '1', - 0: '0' -} - - - - - - +from configs.path_config import IMAGE_PATH +from pathlib import Path + + +SIGN_RESOURCE_PATH = Path(IMAGE_PATH) / 'sign' / 'sign_res' +SIGN_TODAY_CARD_PATH = Path(IMAGE_PATH) / 'sign' / 'today_card' +SIGN_BORDER_PATH = Path(SIGN_RESOURCE_PATH) / 'border' +SIGN_BACKGROUND_PATH = Path(SIGN_RESOURCE_PATH) / 'background' + +SIGN_BORDER_PATH.mkdir(exist_ok=True, parents=True) +SIGN_BACKGROUND_PATH.mkdir(exist_ok=True, parents=True) + + +lik2relation = { + '0': '路人', + '1': '陌生', + '2': '初识', + '3': '普通', + '4': '熟悉', + '5': '信赖', + '6': '相知', + '7': '厚谊', + '8': '亲密' +} + +level2attitude = { + '0': '排斥', + '1': '警惕', + '2': '可以交流', + '3': '一般', + '4': '是个好人', + '5': '好朋友', + '6': '可以分享小秘密', + '7': '喜欢', + '8': '恋人' +} + +weekdays = { + 1: 'Mon', + 2: 'Tue', + 3: 'Wed', + 4: 'Thu', + 5: 'Fri', + 6: 'Sat', + 7: 'Sun' +} + +lik2level = { + 9999: '9', + 400: '8', + 270: '7', + 200: '6', + 140: '5', + 90: '4', + 50: '3', + 25: '2', + 10: '1', + 0: '0' +} + + + + + + diff --git a/plugins/sign_in/group_user_checkin.py b/plugins/sign_in/group_user_checkin.py old mode 100644 new mode 100755 index 18150ab4..990b053a --- a/plugins/sign_in/group_user_checkin.py +++ b/plugins/sign_in/group_user_checkin.py @@ -4,7 +4,6 @@ from models.group_member_info import GroupInfoUser from models.bag_user import BagUser from configs.config import NICKNAME from nonebot.adapters.cqhttp import MessageSegment -from asyncio.exceptions import TimeoutError from utils.image_utils import CreateImg, CreateMat from services.db_context import db from .utils import get_card, SIGN_TODAY_CARD_PATH @@ -12,9 +11,9 @@ from typing import Optional from services.log import logger from .random_event import random_event from utils.data_utils import init_rank +from utils.utils import get_user_avatar from io import BytesIO import random -import aiohttp import math import asyncio import secrets @@ -142,47 +141,44 @@ async def _pst(users: list, impressions: list, groups: list): width = 10 idx = 0 A = CreateImg(1740, 3300, color="#FFE4C4") - async with aiohttp.ClientSession() as session: - for _ in range(count): - col_img = CreateImg(550, 3300, 550, 100, color="#FFE4C4") - for _ in range(33 if int(lens / 33) >= 1 else lens % 33 - 1): - idx += 1 - if idx > 100: - break - impression = max(impressions) - index = impressions.index(impression) - user = users[index] - group = groups[index] - impressions.pop(index) - users.pop(index) - groups.pop(index) - try: - user_name = ( - await GroupInfoUser.get_member_info(user, group) - ).user_name - except AttributeError: - user_name = f"我名字呢?" - user_name = user_name if len(user_name) < 11 else user_name[:10] + "..." - try: - async with session.get( - f"http://q1.qlogo.cn/g?b=qq&nk={user}&s=160", timeout=5 - ) as response: - ava = CreateImg( - 50, 50, background=BytesIO(await response.read()) - ) - except TimeoutError: - ava = CreateImg(50, 50, color="white") - ava.circle() - bk = CreateImg(550, 100, color="#FFE4C4", font_size=30) - font_w, font_h = bk.getsize(f"{idx}") - bk.text((5, int((100 - font_h) / 2)), f"{idx}.") - bk.paste(ava, (55, int((100 - 50) / 2)), True) - bk.text((120, int((100 - font_h) / 2)), f"{user_name}") - bk.text((460, int((100 - font_h) / 2)), f"[{impression:.2f}]") - col_img.paste(bk) - A.paste(col_img, (width, 0)) - lens -= 33 - width += 580 + for _ in range(count): + col_img = CreateImg(550, 3300, 550, 100, color="#FFE4C4") + for _ in range(33 if int(lens / 33) >= 1 else lens % 33 - 1): + idx += 1 + if idx > 100: + break + impression = max(impressions) + index = impressions.index(impression) + user = users[index] + group = groups[index] + impressions.pop(index) + users.pop(index) + groups.pop(index) + try: + user_name = ( + await GroupInfoUser.get_member_info(user, group) + ).user_name + except AttributeError: + user_name = f"我名字呢?" + user_name = user_name if len(user_name) < 11 else user_name[:10] + "..." + ava = await get_user_avatar(user) + if ava: + ava = CreateImg( + 50, 50, background=BytesIO(ava) + ) + else: + ava = CreateImg(50, 50, color="white") + ava.circle() + bk = CreateImg(550, 100, color="#FFE4C4", font_size=30) + font_w, font_h = bk.getsize(f"{idx}") + bk.text((5, int((100 - font_h) / 2)), f"{idx}.") + bk.paste(ava, (55, int((100 - 50) / 2)), True) + bk.text((120, int((100 - font_h) / 2)), f"{user_name}") + bk.text((460, int((100 - font_h) / 2)), f"[{impression:.2f}]") + col_img.paste(bk) + A.paste(col_img, (width, 0)) + lens -= 33 + width += 580 W = CreateImg(1740, 3700, color="#FFE4C4", font_size=130) W.paste(A, (0, 260)) font_w, font_h = W.getsize(f"{NICKNAME}的好感度总榜") diff --git a/plugins/sign_in/random_event.py b/plugins/sign_in/random_event.py old mode 100644 new mode 100755 index c5ea35a8..22d799c0 --- a/plugins/sign_in/random_event.py +++ b/plugins/sign_in/random_event.py @@ -1,33 +1,33 @@ -from configs.config import Config -import random - - -PROB_DATA = None - - -def random_event(impression: float) -> 'Union[str, int], str': - """ - 签到随机事件 - :param impression: 好感度 - :return: 额外奖励 和 类型 - """ - global PROB_DATA - if not PROB_DATA: - PROB_DATA = { - Config.get_config("sign_in", "SIGN_CARD3_PROB"): '好感度双倍加持卡Ⅲ', - Config.get_config("sign_in", "SIGN_CARD2_PROB"): '好感度双倍加持卡Ⅱ', - Config.get_config("sign_in", "SIGN_CARD1_PROB"): '好感度双倍加持卡Ⅰ' - } - rand = random.random() - impression / 1000 - for prob in PROB_DATA.keys(): - if rand <= prob: - return PROB_DATA[prob], 'props' - gold = random.randint(1, random.randint(1, int(1 if impression < 1 else impression))) - max_sign_gold = Config.get_config("sign_in", "MAX_SIGN_GOLD") - gold = max_sign_gold if gold > max_sign_gold else gold - return gold, 'gold' - - - - - +from configs.config import Config +import random + + +PROB_DATA = None + + +def random_event(impression: float) -> 'Union[str, int], str': + """ + 签到随机事件 + :param impression: 好感度 + :return: 额外奖励 和 类型 + """ + global PROB_DATA + if not PROB_DATA: + PROB_DATA = { + Config.get_config("sign_in", "SIGN_CARD3_PROB"): '好感度双倍加持卡Ⅲ', + Config.get_config("sign_in", "SIGN_CARD2_PROB"): '好感度双倍加持卡Ⅱ', + Config.get_config("sign_in", "SIGN_CARD1_PROB"): '好感度双倍加持卡Ⅰ' + } + rand = random.random() - impression / 1000 + for prob in PROB_DATA.keys(): + if rand <= prob: + return PROB_DATA[prob], 'props' + gold = random.randint(1, random.randint(1, int(1 if impression < 1 else impression))) + max_sign_gold = Config.get_config("sign_in", "MAX_SIGN_GOLD") + gold = max_sign_gold if gold > max_sign_gold else gold + return gold, 'gold' + + + + + diff --git a/plugins/sign_in/utils.py b/plugins/sign_in/utils.py old mode 100644 new mode 100755 index 2bffa5ea..4f5a8fb0 --- a/plugins/sign_in/utils.py +++ b/plugins/sign_in/utils.py @@ -1,352 +1,352 @@ -from .config import ( - SIGN_RESOURCE_PATH, - SIGN_TODAY_CARD_PATH, - SIGN_BORDER_PATH, - SIGN_BACKGROUND_PATH, - lik2level, - lik2relation, - level2attitude, - weekdays, -) -from models.sign_group_user import SignGroupUser -from models.group_member_info import GroupInfoUser -from nonebot.adapters.cqhttp import MessageSegment -from utils.utils import get_user_avatar -from utils.image_utils import CreateImg -from utils.message_builder import image -from configs.config import NICKNAME -from pathlib import Path -from datetime import datetime -from typing import Optional, List -from nonebot import Driver -from io import BytesIO -import asyncio -import random -import nonebot -import os - - -driver: Driver = nonebot.get_driver() - - -@driver.on_startup -async def init_image(): - SIGN_RESOURCE_PATH.mkdir(parents=True, exist_ok=True) - SIGN_TODAY_CARD_PATH.mkdir(exist_ok=True, parents=True) - await GroupInfoUser.add_member_info(114514, 114514, "", datetime.min, 0) - _u = await GroupInfoUser.get_member_info(114514, 114514) - if _u.uid is None: - await _u.update(uid=0).apply() - generate_progress_bar_pic() - clear_sign_data_pic() - - -async def get_card( - user: "SignGroupUser", - nickname: str, - add_impression: Optional[float], - gold: Optional[int], - gift: str, - is_double: bool = False, - is_card_view: bool = False, -) -> MessageSegment: - user_id = user.user_qq - date = datetime.now().date() - _type = "view" if is_card_view else "sign" - card_file = ( - Path(SIGN_TODAY_CARD_PATH) - / f"{user_id}_{user.belonging_group}_{_type}_{date}.png" - ) - if card_file.exists(): - return image( - f"{user_id}_{user.belonging_group}_{_type}_{date}.png", "sign/today_card" - ) - else: - if add_impression == -1: - card_file = ( - Path(SIGN_TODAY_CARD_PATH) - / f"{user_id}_{user.belonging_group}_view_{date}.png" - ) - if card_file.exists(): - return image( - f"{user_id}_{user.belonging_group}_view_{date}.png", - "sign/today_card", - ) - is_card_view = True - ava = BytesIO(await get_user_avatar(user_id)) - uid = await GroupInfoUser.get_group_member_uid( - user.user_qq, user.belonging_group - ) - impression_list = None - if is_card_view: - _, impression_list, _ = await SignGroupUser.get_all_impression( - user.belonging_group - ) - return await asyncio.get_event_loop().run_in_executor( - None, - _generate_card, - user, - nickname, - user_id, - add_impression, - gold, - gift, - uid, - ava, - impression_list, - is_double, - is_card_view, - ) - - -def _generate_card( - user: "SignGroupUser", - nickname: str, - user_id: int, - impression: Optional[float], - gold: Optional[int], - gift: str, - uid: str, - ava_bytes: BytesIO, - impression_list: List[float], - is_double: bool = False, - is_card_view: bool = False, -) -> MessageSegment: - ava_bk = CreateImg(140, 140, is_alpha=True) - ava_border = CreateImg( - 140, - 140, - background=SIGN_BORDER_PATH / "ava_border_01.png", - ) - ava = CreateImg(102, 102, background=ava_bytes) - ava.circle() - ava_bk.paste(ava, center_type="center") - ava_bk.paste(ava_border, alpha=True, center_type="center") - - info_img = CreateImg(250, 150, color=(255, 255, 255, 0), font_size=15) - level, next_impression, previous_impression = get_level_and_next_impression( - user.impression - ) - info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]") - info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}") - info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度") - - bar_bk = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png") - bar = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar.png") - bar_bk.paste( - bar, - ( - -int( - 220 - * ( - (next_impression - user.impression) - / (next_impression - previous_impression) - ) - ), - 0, - ), - True, - ) - font_size = 30 - if "好感度双倍加持卡" in gift: - font_size = 20 - gift_border = CreateImg( - 270, - 100, - background=SIGN_BORDER_PATH / "gift_border_02.png", - font_size=font_size, - ) - gift_border.text((0, 0), gift, center_type="center") - - bk = CreateImg( - 876, - 424, - background=SIGN_BACKGROUND_PATH - / random.choice(os.listdir(SIGN_BACKGROUND_PATH)), - font_size=25, - ) - A = CreateImg(876, 274, background=SIGN_RESOURCE_PATH / "white.png") - line = CreateImg(2, 180, color="black") - A.transparent(2) - A.paste(ava_bk, (25, 80), True) - A.paste(line, (200, 70)) - - nickname_img = CreateImg( - 0, - 0, - plain_text=nickname, - color=(255, 255, 255, 0), - font_size=50, - font_color=(255, 255, 255), - ) - if uid: - uid = f"{uid}".rjust(12, "0") - uid = uid[:4] + " " + uid[4:8] + " " + uid[8:] - else: - uid = "XXXX XXXX XXXX" - uid_img = CreateImg( - 0, - 0, - plain_text=f"UID: {uid}", - color=(255, 255, 255, 0), - font_size=30, - font_color=(255, 255, 255), - ) - sign_day_img = CreateImg( - 0, - 0, - plain_text=f"{user.checkin_count}", - color=(255, 255, 255, 0), - font_size=40, - font_color=(211, 64, 33), - ) - lik_text1_img = CreateImg( - 0, 0, plain_text="当前", color=(255, 255, 255, 0), font_size=20 - ) - lik_text2_img = CreateImg( - 0, - 0, - plain_text=f"好感度:{user.impression:.2f}", - color=(255, 255, 255, 0), - font_size=30, - ) - watermark = CreateImg( - 0, - 0, - plain_text=f"{NICKNAME}@{datetime.now().year}", - color=(255, 255, 255, 0), - font_size=15, - font_color=(155, 155, 155), - ) - today_data = CreateImg(300, 300, color=(255, 255, 255, 0), font_size=20) - if is_card_view: - today_sign_text_img = CreateImg( - 0, 0, plain_text="", color=(255, 255, 255, 0), font_size=30 - ) - if impression_list: - impression_list.sort(reverse=True) - index = impression_list.index(user.impression) - rank_img = CreateImg( - 0, - 0, - plain_text=f"* 此群好感排名第 {index + 1} 位", - color=(255, 255, 255, 0), - font_size=30, - ) - A.paste(rank_img, ((A.w - rank_img.w - 10), 20), True) - today_data.text( - (0, 0), - f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}", - ) - today_data.text((0, 25), f"总金币:{gold}") - today_data.text( - (0, 50), - f"色图概率:{(70 + user.impression if user.impression < 100 else 100):.2f}%", - ) - today_data.text((0, 75), f"开箱次数:{(20 + int(user.impression / 3))}") - _type = "view" - else: - A.paste(gift_border, (570, 140), True) - today_sign_text_img = CreateImg( - 0, 0, plain_text="今日签到", color=(255, 255, 255, 0), font_size=30 - ) - if is_double: - today_data.text((0, 0), f"好感度 + {impression / 2:.2f} × 2") - else: - today_data.text((0, 0), f"好感度 + {impression:.2f}") - today_data.text((0, 25), f"金币 + {gold}") - _type = "sign" - current_date = datetime.now() - week = current_date.isoweekday() - data = current_date.date() - hour = current_date.hour - minute = current_date.minute - second = current_date.second - data_img = CreateImg( - 0, - 0, - plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}", - color=(255, 255, 255, 0), - font_size=20, - ) - bk.paste(nickname_img, (30, 15), True) - bk.paste(uid_img, (30, 85), True) - bk.paste(A, (0, 150), alpha=True) - bk.text((30, 167), "Accumulative check-in for") - _x = bk.getsize("Accumulative check-in for")[0] + sign_day_img.w + 45 - bk.paste(sign_day_img, (346, 158), True) - bk.text((_x, 167), "days") - bk.paste(data_img, (220, 370), True) - bk.paste(lik_text1_img, (220, 240), True) - bk.paste(lik_text2_img, (262, 234), True) - bk.paste(bar_bk, (225, 275), True) - bk.paste(info_img, (220, 305), True) - bk.paste(today_sign_text_img, (550, 180), True) - bk.paste(today_data, (580, 220), True) - bk.paste(watermark, (15, 400), True) - bk.save( - SIGN_TODAY_CARD_PATH / f"{user_id}_{user.belonging_group}_{_type}_{data}.png" - ) - return image( - f"{user_id}_{user.belonging_group}_{_type}_{data}.png", "sign/today_card" - ) - - -def generate_progress_bar_pic(): - bg_2 = (254, 1, 254) - bg_1 = (0, 245, 246) - - bk = CreateImg(1000, 50, is_alpha=True) - img_x = CreateImg(50, 50, color=bg_2) - img_x.circle() - img_x.crop((25, 0, 50, 50)) - img_y = CreateImg(50, 50, color=bg_1) - img_y.circle() - img_y.crop((0, 0, 25, 50)) - A = CreateImg(950, 50) - width, height = A.size - - step_r = (bg_2[0] - bg_1[0]) / width - step_g = (bg_2[1] - bg_1[1]) / width - step_b = (bg_2[2] - bg_1[2]) / width - - for y in range(0, width): - bg_r = round(bg_1[0] + step_r * y) - bg_g = round(bg_1[1] + step_g * y) - bg_b = round(bg_1[2] + step_b * y) - for x in range(0, height): - A.point((y, x), fill=(bg_r, bg_g, bg_b)) - bk.paste(img_y, (0, 0), True) - bk.paste(A, (25, 0)) - bk.paste(img_x, (975, 0), True) - bk.save(SIGN_RESOURCE_PATH / "bar.png") - - A = CreateImg(950, 50) - bk = CreateImg(1000, 50, is_alpha=True) - img_x = CreateImg(50, 50) - img_x.circle() - img_x.crop((25, 0, 50, 50)) - img_y = CreateImg(50, 50) - img_y.circle() - img_y.crop((0, 0, 25, 50)) - bk.paste(img_y, (0, 0), True) - bk.paste(A, (25, 0)) - bk.paste(img_x, (975, 0), True) - bk.save(SIGN_RESOURCE_PATH / "bar_white.png") - - -def get_level_and_next_impression(impression: float): - if impression == 0: - return lik2level[10], 10, 0 - keys = list(lik2level.keys()) - for i in range(len(keys)): - if impression > keys[i]: - return lik2level[keys[i]], keys[i - 1], keys[i] - return lik2level[10], 10, 0 - - -def clear_sign_data_pic(): - date = datetime.now().date() - for file in os.listdir(SIGN_TODAY_CARD_PATH): - if str(date) not in file: - os.remove(SIGN_TODAY_CARD_PATH / file) +from .config import ( + SIGN_RESOURCE_PATH, + SIGN_TODAY_CARD_PATH, + SIGN_BORDER_PATH, + SIGN_BACKGROUND_PATH, + lik2level, + lik2relation, + level2attitude, + weekdays, +) +from models.sign_group_user import SignGroupUser +from models.group_member_info import GroupInfoUser +from nonebot.adapters.cqhttp import MessageSegment +from utils.utils import get_user_avatar +from utils.image_utils import CreateImg +from utils.message_builder import image +from configs.config import NICKNAME +from pathlib import Path +from datetime import datetime +from typing import Optional, List +from nonebot import Driver +from io import BytesIO +import asyncio +import random +import nonebot +import os + + +driver: Driver = nonebot.get_driver() + + +@driver.on_startup +async def init_image(): + SIGN_RESOURCE_PATH.mkdir(parents=True, exist_ok=True) + SIGN_TODAY_CARD_PATH.mkdir(exist_ok=True, parents=True) + await GroupInfoUser.add_member_info(114514, 114514, "", datetime.min, 0) + _u = await GroupInfoUser.get_member_info(114514, 114514) + if _u.uid is None: + await _u.update(uid=0).apply() + generate_progress_bar_pic() + clear_sign_data_pic() + + +async def get_card( + user: "SignGroupUser", + nickname: str, + add_impression: Optional[float], + gold: Optional[int], + gift: str, + is_double: bool = False, + is_card_view: bool = False, +) -> MessageSegment: + user_id = user.user_qq + date = datetime.now().date() + _type = "view" if is_card_view else "sign" + card_file = ( + Path(SIGN_TODAY_CARD_PATH) + / f"{user_id}_{user.belonging_group}_{_type}_{date}.png" + ) + if card_file.exists(): + return image( + f"{user_id}_{user.belonging_group}_{_type}_{date}.png", "sign/today_card" + ) + else: + if add_impression == -1: + card_file = ( + Path(SIGN_TODAY_CARD_PATH) + / f"{user_id}_{user.belonging_group}_view_{date}.png" + ) + if card_file.exists(): + return image( + f"{user_id}_{user.belonging_group}_view_{date}.png", + "sign/today_card", + ) + is_card_view = True + ava = BytesIO(await get_user_avatar(user_id)) + uid = await GroupInfoUser.get_group_member_uid( + user.user_qq, user.belonging_group + ) + impression_list = None + if is_card_view: + _, impression_list, _ = await SignGroupUser.get_all_impression( + user.belonging_group + ) + return await asyncio.get_event_loop().run_in_executor( + None, + _generate_card, + user, + nickname, + user_id, + add_impression, + gold, + gift, + uid, + ava, + impression_list, + is_double, + is_card_view, + ) + + +def _generate_card( + user: "SignGroupUser", + nickname: str, + user_id: int, + impression: Optional[float], + gold: Optional[int], + gift: str, + uid: str, + ava_bytes: BytesIO, + impression_list: List[float], + is_double: bool = False, + is_card_view: bool = False, +) -> MessageSegment: + ava_bk = CreateImg(140, 140, is_alpha=True) + ava_border = CreateImg( + 140, + 140, + background=SIGN_BORDER_PATH / "ava_border_01.png", + ) + ava = CreateImg(102, 102, background=ava_bytes) + ava.circle() + ava_bk.paste(ava, center_type="center") + ava_bk.paste(ava_border, alpha=True, center_type="center") + + info_img = CreateImg(250, 150, color=(255, 255, 255, 0), font_size=15) + level, next_impression, previous_impression = get_level_and_next_impression( + user.impression + ) + info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]") + info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}") + info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度") + + bar_bk = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png") + bar = CreateImg(220, 20, background=SIGN_RESOURCE_PATH / "bar.png") + bar_bk.paste( + bar, + ( + -int( + 220 + * ( + (next_impression - user.impression) + / (next_impression - previous_impression) + ) + ), + 0, + ), + True, + ) + font_size = 30 + if "好感度双倍加持卡" in gift: + font_size = 20 + gift_border = CreateImg( + 270, + 100, + background=SIGN_BORDER_PATH / "gift_border_02.png", + font_size=font_size, + ) + gift_border.text((0, 0), gift, center_type="center") + + bk = CreateImg( + 876, + 424, + background=SIGN_BACKGROUND_PATH + / random.choice(os.listdir(SIGN_BACKGROUND_PATH)), + font_size=25, + ) + A = CreateImg(876, 274, background=SIGN_RESOURCE_PATH / "white.png") + line = CreateImg(2, 180, color="black") + A.transparent(2) + A.paste(ava_bk, (25, 80), True) + A.paste(line, (200, 70)) + + nickname_img = CreateImg( + 0, + 0, + plain_text=nickname, + color=(255, 255, 255, 0), + font_size=50, + font_color=(255, 255, 255), + ) + if uid: + uid = f"{uid}".rjust(12, "0") + uid = uid[:4] + " " + uid[4:8] + " " + uid[8:] + else: + uid = "XXXX XXXX XXXX" + uid_img = CreateImg( + 0, + 0, + plain_text=f"UID: {uid}", + color=(255, 255, 255, 0), + font_size=30, + font_color=(255, 255, 255), + ) + sign_day_img = CreateImg( + 0, + 0, + plain_text=f"{user.checkin_count}", + color=(255, 255, 255, 0), + font_size=40, + font_color=(211, 64, 33), + ) + lik_text1_img = CreateImg( + 0, 0, plain_text="当前", color=(255, 255, 255, 0), font_size=20 + ) + lik_text2_img = CreateImg( + 0, + 0, + plain_text=f"好感度:{user.impression:.2f}", + color=(255, 255, 255, 0), + font_size=30, + ) + watermark = CreateImg( + 0, + 0, + plain_text=f"{NICKNAME}@{datetime.now().year}", + color=(255, 255, 255, 0), + font_size=15, + font_color=(155, 155, 155), + ) + today_data = CreateImg(300, 300, color=(255, 255, 255, 0), font_size=20) + if is_card_view: + today_sign_text_img = CreateImg( + 0, 0, plain_text="", color=(255, 255, 255, 0), font_size=30 + ) + if impression_list: + impression_list.sort(reverse=True) + index = impression_list.index(user.impression) + rank_img = CreateImg( + 0, + 0, + plain_text=f"* 此群好感排名第 {index + 1} 位", + color=(255, 255, 255, 0), + font_size=30, + ) + A.paste(rank_img, ((A.w - rank_img.w - 10), 20), True) + today_data.text( + (0, 0), + f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}", + ) + today_data.text((0, 25), f"总金币:{gold}") + today_data.text( + (0, 50), + f"色图概率:{(70 + user.impression if user.impression < 100 else 100):.2f}%", + ) + today_data.text((0, 75), f"开箱次数:{(20 + int(user.impression / 3))}") + _type = "view" + else: + A.paste(gift_border, (570, 140), True) + today_sign_text_img = CreateImg( + 0, 0, plain_text="今日签到", color=(255, 255, 255, 0), font_size=30 + ) + if is_double: + today_data.text((0, 0), f"好感度 + {impression / 2:.2f} × 2") + else: + today_data.text((0, 0), f"好感度 + {impression:.2f}") + today_data.text((0, 25), f"金币 + {gold}") + _type = "sign" + current_date = datetime.now() + week = current_date.isoweekday() + data = current_date.date() + hour = current_date.hour + minute = current_date.minute + second = current_date.second + data_img = CreateImg( + 0, + 0, + plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}", + color=(255, 255, 255, 0), + font_size=20, + ) + bk.paste(nickname_img, (30, 15), True) + bk.paste(uid_img, (30, 85), True) + bk.paste(A, (0, 150), alpha=True) + bk.text((30, 167), "Accumulative check-in for") + _x = bk.getsize("Accumulative check-in for")[0] + sign_day_img.w + 45 + bk.paste(sign_day_img, (346, 158), True) + bk.text((_x, 167), "days") + bk.paste(data_img, (220, 370), True) + bk.paste(lik_text1_img, (220, 240), True) + bk.paste(lik_text2_img, (262, 234), True) + bk.paste(bar_bk, (225, 275), True) + bk.paste(info_img, (220, 305), True) + bk.paste(today_sign_text_img, (550, 180), True) + bk.paste(today_data, (580, 220), True) + bk.paste(watermark, (15, 400), True) + bk.save( + SIGN_TODAY_CARD_PATH / f"{user_id}_{user.belonging_group}_{_type}_{data}.png" + ) + return image( + f"{user_id}_{user.belonging_group}_{_type}_{data}.png", "sign/today_card" + ) + + +def generate_progress_bar_pic(): + bg_2 = (254, 1, 254) + bg_1 = (0, 245, 246) + + bk = CreateImg(1000, 50, is_alpha=True) + img_x = CreateImg(50, 50, color=bg_2) + img_x.circle() + img_x.crop((25, 0, 50, 50)) + img_y = CreateImg(50, 50, color=bg_1) + img_y.circle() + img_y.crop((0, 0, 25, 50)) + A = CreateImg(950, 50) + width, height = A.size + + step_r = (bg_2[0] - bg_1[0]) / width + step_g = (bg_2[1] - bg_1[1]) / width + step_b = (bg_2[2] - bg_1[2]) / width + + for y in range(0, width): + bg_r = round(bg_1[0] + step_r * y) + bg_g = round(bg_1[1] + step_g * y) + bg_b = round(bg_1[2] + step_b * y) + for x in range(0, height): + A.point((y, x), fill=(bg_r, bg_g, bg_b)) + bk.paste(img_y, (0, 0), True) + bk.paste(A, (25, 0)) + bk.paste(img_x, (975, 0), True) + bk.save(SIGN_RESOURCE_PATH / "bar.png") + + A = CreateImg(950, 50) + bk = CreateImg(1000, 50, is_alpha=True) + img_x = CreateImg(50, 50) + img_x.circle() + img_x.crop((25, 0, 50, 50)) + img_y = CreateImg(50, 50) + img_y.circle() + img_y.crop((0, 0, 25, 50)) + bk.paste(img_y, (0, 0), True) + bk.paste(A, (25, 0)) + bk.paste(img_x, (975, 0), True) + bk.save(SIGN_RESOURCE_PATH / "bar_white.png") + + +def get_level_and_next_impression(impression: float): + if impression == 0: + return lik2level[10], 10, 0 + keys = list(lik2level.keys()) + for i in range(len(keys)): + if impression > keys[i]: + return lik2level[keys[i]], keys[i - 1], keys[i] + return lik2level[10], 10, 0 + + +def clear_sign_data_pic(): + date = datetime.now().date() + for file in os.listdir(SIGN_TODAY_CARD_PATH): + if str(date) not in file: + os.remove(SIGN_TODAY_CARD_PATH / file) diff --git a/plugins/statistics/__init__.py b/plugins/statistics/__init__.py old mode 100644 new mode 100755 index 590246d9..644a544e --- a/plugins/statistics/__init__.py +++ b/plugins/statistics/__init__.py @@ -1,128 +1,128 @@ -from pathlib import Path -from configs.path_config import DATA_PATH -import nonebot -import os -try: - import ujson as json -except ModuleNotFoundError: - import json - -nonebot.load_plugins("plugins/statistics") - -old_file1 = Path(DATA_PATH) / "_prefix_count.json" -old_file2 = Path(DATA_PATH) / "_prefix_user_count.json" -new_path = Path(DATA_PATH) / "statistics" -new_path.mkdir(parents=True, exist_ok=True) -if old_file1.exists(): - os.rename(old_file1, new_path / "_prefix_count.json") -if old_file2.exists(): - os.rename(old_file2, new_path / "_prefix_user_count.json") - - -# 修改旧数据 - -statistics_group_file = Path(DATA_PATH) / "statistics" / "_prefix_count.json" -statistics_user_file = Path(DATA_PATH) / "statistics" / "_prefix_user_count.json" - -for file in [statistics_group_file, statistics_user_file]: - if file.exists(): - with open(file, "r", encoding="utf8") as f: - data = json.load(f) - if not (statistics_group_file.parent / f"{file}.bak").exists(): - with open(f"{file}.bak", "w", encoding="utf8") as wf: - json.dump(data, wf, ensure_ascii=False, indent=4) - for x in ["total_statistics", "day_statistics"]: - for key in data[x].keys(): - num = 0 - if data[x][key].get("ai") is not None: - if data[x][key].get("Ai") is not None: - data[x][key]["Ai"] += data[x][key]["ai"] - else: - data[x][key]["Ai"] = data[x][key]["ai"] - del data[x][key]["ai"] - if data[x][key].get("抽卡") is not None: - if data[x][key].get("游戏抽卡") is not None: - data[x][key]["游戏抽卡"] += data[x][key]["抽卡"] - else: - data[x][key]["游戏抽卡"] = data[x][key]["抽卡"] - del data[x][key]["抽卡"] - if data[x][key].get("我的道具") is not None: - num += data[x][key]["我的道具"] - del data[x][key]["我的道具"] - if data[x][key].get("使用道具") is not None: - num += data[x][key]["使用道具"] - del data[x][key]["使用道具"] - if data[x][key].get("我的金币") is not None: - num += data[x][key]["我的金币"] - del data[x][key]["我的金币"] - if data[x][key].get("购买") is not None: - num += data[x][key]["购买"] - del data[x][key]["购买"] - if data[x][key].get("商店") is not None: - data[x][key]["商店"] += num - else: - data[x][key]["商店"] = num - for x in ["week_statistics", "month_statistics"]: - for key in data[x].keys(): - if key == "total": - if data[x][key].get("ai") is not None: - if data[x][key].get("Ai") is not None: - data[x][key]["Ai"] += data[x][key]["ai"] - else: - data[x][key]["Ai"] = data[x][key]["ai"] - del data[x][key]["ai"] - if data[x][key].get("抽卡") is not None: - if data[x][key].get("游戏抽卡") is not None: - data[x][key]["游戏抽卡"] += data[x][key]["抽卡"] - else: - data[x][key]["游戏抽卡"] = data[x][key]["抽卡"] - del data[x][key]["抽卡"] - if data[x][key].get("我的道具") is not None: - num += data[x][key]["我的道具"] - del data[x][key]["我的道具"] - if data[x][key].get("使用道具") is not None: - num += data[x][key]["使用道具"] - del data[x][key]["使用道具"] - if data[x][key].get("我的金币") is not None: - num += data[x][key]["我的金币"] - del data[x][key]["我的金币"] - if data[x][key].get("购买") is not None: - num += data[x][key]["购买"] - del data[x][key]["购买"] - if data[x][key].get("商店") is not None: - data[x][key]["商店"] += num - else: - data[x][key]["商店"] = num - else: - for day in data[x][key].keys(): - num = 0 - if data[x][key][day].get("ai") is not None: - if data[x][key][day].get("Ai") is not None: - data[x][key][day]["Ai"] += data[x][key][day]["ai"] - else: - data[x][key][day]["Ai"] = data[x][key][day]["ai"] - del data[x][key][day]["ai"] - if data[x][key][day].get("抽卡") is not None: - if data[x][key][day].get("游戏抽卡") is not None: - data[x][key][day]["游戏抽卡"] += data[x][key][day]["抽卡"] - else: - data[x][key][day]["游戏抽卡"] = data[x][key][day]["抽卡"] - del data[x][key][day]["抽卡"] - if data[x][key][day].get("我的道具") is not None: - num += data[x][key][day]["我的道具"] - del data[x][key][day]["我的道具"] - if data[x][key][day].get("使用道具") is not None: - num += data[x][key][day]["使用道具"] - del data[x][key][day]["使用道具"] - if data[x][key][day].get("我的金币") is not None: - num += data[x][key][day]["我的金币"] - del data[x][key][day]["我的金币"] - if data[x][key][day].get("购买") is not None: - num += data[x][key][day]["购买"] - del data[x][key][day]["购买"] - if data[x][key][day].get("商店") is not None: - data[x][key][day]["商店"] += num - else: - data[x][key][day]["商店"] = num - with open(file, "w", encoding="utf8") as f: - json.dump(data, f, ensure_ascii=False, indent=4) +from pathlib import Path +from configs.path_config import DATA_PATH +import nonebot +import os +try: + import ujson as json +except ModuleNotFoundError: + import json + +nonebot.load_plugins("plugins/statistics") + +old_file1 = Path(DATA_PATH) / "_prefix_count.json" +old_file2 = Path(DATA_PATH) / "_prefix_user_count.json" +new_path = Path(DATA_PATH) / "statistics" +new_path.mkdir(parents=True, exist_ok=True) +if old_file1.exists(): + os.rename(old_file1, new_path / "_prefix_count.json") +if old_file2.exists(): + os.rename(old_file2, new_path / "_prefix_user_count.json") + + +# 修改旧数据 + +statistics_group_file = Path(DATA_PATH) / "statistics" / "_prefix_count.json" +statistics_user_file = Path(DATA_PATH) / "statistics" / "_prefix_user_count.json" + +for file in [statistics_group_file, statistics_user_file]: + if file.exists(): + with open(file, "r", encoding="utf8") as f: + data = json.load(f) + if not (statistics_group_file.parent / f"{file}.bak").exists(): + with open(f"{file}.bak", "w", encoding="utf8") as wf: + json.dump(data, wf, ensure_ascii=False, indent=4) + for x in ["total_statistics", "day_statistics"]: + for key in data[x].keys(): + num = 0 + if data[x][key].get("ai") is not None: + if data[x][key].get("Ai") is not None: + data[x][key]["Ai"] += data[x][key]["ai"] + else: + data[x][key]["Ai"] = data[x][key]["ai"] + del data[x][key]["ai"] + if data[x][key].get("抽卡") is not None: + if data[x][key].get("游戏抽卡") is not None: + data[x][key]["游戏抽卡"] += data[x][key]["抽卡"] + else: + data[x][key]["游戏抽卡"] = data[x][key]["抽卡"] + del data[x][key]["抽卡"] + if data[x][key].get("我的道具") is not None: + num += data[x][key]["我的道具"] + del data[x][key]["我的道具"] + if data[x][key].get("使用道具") is not None: + num += data[x][key]["使用道具"] + del data[x][key]["使用道具"] + if data[x][key].get("我的金币") is not None: + num += data[x][key]["我的金币"] + del data[x][key]["我的金币"] + if data[x][key].get("购买") is not None: + num += data[x][key]["购买"] + del data[x][key]["购买"] + if data[x][key].get("商店") is not None: + data[x][key]["商店"] += num + else: + data[x][key]["商店"] = num + for x in ["week_statistics", "month_statistics"]: + for key in data[x].keys(): + if key == "total": + if data[x][key].get("ai") is not None: + if data[x][key].get("Ai") is not None: + data[x][key]["Ai"] += data[x][key]["ai"] + else: + data[x][key]["Ai"] = data[x][key]["ai"] + del data[x][key]["ai"] + if data[x][key].get("抽卡") is not None: + if data[x][key].get("游戏抽卡") is not None: + data[x][key]["游戏抽卡"] += data[x][key]["抽卡"] + else: + data[x][key]["游戏抽卡"] = data[x][key]["抽卡"] + del data[x][key]["抽卡"] + if data[x][key].get("我的道具") is not None: + num += data[x][key]["我的道具"] + del data[x][key]["我的道具"] + if data[x][key].get("使用道具") is not None: + num += data[x][key]["使用道具"] + del data[x][key]["使用道具"] + if data[x][key].get("我的金币") is not None: + num += data[x][key]["我的金币"] + del data[x][key]["我的金币"] + if data[x][key].get("购买") is not None: + num += data[x][key]["购买"] + del data[x][key]["购买"] + if data[x][key].get("商店") is not None: + data[x][key]["商店"] += num + else: + data[x][key]["商店"] = num + else: + for day in data[x][key].keys(): + num = 0 + if data[x][key][day].get("ai") is not None: + if data[x][key][day].get("Ai") is not None: + data[x][key][day]["Ai"] += data[x][key][day]["ai"] + else: + data[x][key][day]["Ai"] = data[x][key][day]["ai"] + del data[x][key][day]["ai"] + if data[x][key][day].get("抽卡") is not None: + if data[x][key][day].get("游戏抽卡") is not None: + data[x][key][day]["游戏抽卡"] += data[x][key][day]["抽卡"] + else: + data[x][key][day]["游戏抽卡"] = data[x][key][day]["抽卡"] + del data[x][key][day]["抽卡"] + if data[x][key][day].get("我的道具") is not None: + num += data[x][key][day]["我的道具"] + del data[x][key][day]["我的道具"] + if data[x][key][day].get("使用道具") is not None: + num += data[x][key][day]["使用道具"] + del data[x][key][day]["使用道具"] + if data[x][key][day].get("我的金币") is not None: + num += data[x][key][day]["我的金币"] + del data[x][key][day]["我的金币"] + if data[x][key][day].get("购买") is not None: + num += data[x][key][day]["购买"] + del data[x][key][day]["购买"] + if data[x][key][day].get("商店") is not None: + data[x][key][day]["商店"] += num + else: + data[x][key][day]["商店"] = num + with open(file, "w", encoding="utf8") as f: + json.dump(data, f, ensure_ascii=False, indent=4) diff --git a/plugins/statistics/statistics_handle.py b/plugins/statistics/statistics_handle.py old mode 100644 new mode 100755 index 6626bc2c..f44308c8 --- a/plugins/statistics/statistics_handle.py +++ b/plugins/statistics/statistics_handle.py @@ -1,280 +1,280 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent -from models.group_info import GroupInfo -from nonebot.typing import T_State -from pathlib import Path -from configs.path_config import DATA_PATH, IMAGE_PATH -from utils.utils import get_message_text -from utils.image_utils import CreateMat -from utils.message_builder import image -from utils.manager import plugins2settings_manager -import asyncio -import os - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -__zx_plugin_name__ = "功能调用统计可视化" -__plugin_usage__ = """ -usage: - 功能调用统计可视化 - 指令: - 功能调用统计 - 日功能调用统计 - 周功能调用统计 ?[功能] - 月功能调用统计 ?[功能] - 我的功能调用统计 - 我的日功能调用统计 ?[功能] - 我的周功能调用统计 ?[功能] - 我的月功能调用统计 ?[功能] -""".strip() -__plugin_superuser_usage__ = """ -usage: - 功能调用统计可视化 - 指令: - 全局功能调用统计 - 全局日功能调用统计 - 全局周功能调用统计 ?[功能] - 全局月功能调用统计 ?[功能] -""".strip() -__plugin_des__ = "功能调用统计可视化" -__plugin_cmd__ = [ - "功能调用统计", - "全局功能调用统计 [_superuser]", - "全局日功能调用统计 [_superuser]", - "全局周功能调用统计 ?[功能] [_superuser]", - "全局月功能调用统计 ?[功能] [_superuser]", - "周功能调用统计 ?[功能]", - "月功能调用统计 ?[功能]", - "我的功能调用统计", - "我的日功能调用统计 ?[功能]", - "我的周功能调用统计 ?[功能]", - "我的月功能调用统计 ?[功能]", -] -__plugin_type__ = ("功能调用统计可视化", 1) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["功能调用统计"], -} - - -statistics = on_command( - "功能调用统计", - aliases={ - "全局功能调用统计", - "全局日功能调用统计", - "全局周功能调用统计", - "全局月功能调用统计", - "日功能调用统计", - "周功能调用统计", - "月功能调用统计", - "我的功能调用统计", - "我的日功能调用统计", - "我的周功能调用统计", - "我的月功能调用统计", - }, - priority=5, - block=True, -) - - -statistics_group_file = Path(DATA_PATH) / "statistics" / "_prefix_count.json" -statistics_user_file = Path(DATA_PATH) / "statistics" / "_prefix_user_count.json" - - -@statistics.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if state["_prefix"]["raw_command"][:2] == "全局": - if str(event.user_id) in bot.config.superusers: - data: dict = json.load(open(statistics_group_file, "r", encoding="utf8")) - if state["_prefix"]["raw_command"][2] == '日': - itype = 'day_statistics' - elif state["_prefix"]["raw_command"][2] == '周': - itype = 'week_statistics' - elif state["_prefix"]["raw_command"][2] == '月': - itype = 'month_statistics' - else: - itype = 'total_statistics' - tmp_dict = {} - data = data[itype] - if itype in ["day_statistics", "total_statistics"]: - for key in data['total']: - tmp_dict[key] = data['total'][key] - else: - for group in data.keys(): - if group != 'total': - for day in data[group].keys(): - for plugin_name in data[group][day].keys(): - if data[group][day][plugin_name] is not None: - if tmp_dict.get(plugin_name) is None: - tmp_dict[plugin_name] = 1 - else: - tmp_dict[plugin_name] += data[group][day][plugin_name] - bar_graph = await init_bar_graph(tmp_dict, state["_prefix"]["raw_command"]) - await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph) - await statistics.finish(image(b64=bar_graph.pic2bs4())) - return - if state["_prefix"]["raw_command"][:2] == "我的": - itype = "user" - key = str(event.user_id) - state["_prefix"]["raw_command"] = state["_prefix"]["raw_command"][2:] - if not statistics_user_file.exists(): - await statistics.finish("统计文件不存在...", at_sender=True) - else: - if not isinstance(event, GroupMessageEvent): - await statistics.finish("请在群内调用此功能...") - itype = "group" - key = str(event.group_id) - if not statistics_group_file.exists(): - await statistics.finish("统计文件不存在...", at_sender=True) - plugin = "" - if state["_prefix"]["raw_command"][0] == "日": - arg = "day_statistics" - elif state["_prefix"]["raw_command"][0] == "周": - arg = "week_statistics" - elif state["_prefix"]["raw_command"][0] == "月": - arg = "month_statistics" - else: - arg = "total_statistics" - if msg: - plugin = plugins2settings_manager.get_plugin_module(msg) - if not plugin: - if arg not in ["day_statistics", "total_statistics"]: - await statistics.finish("未找到此功能的调用...", at_sender=True) - if itype == "group": - data: dict = json.load(open(statistics_group_file, "r", encoding="utf8")) - if not data[arg].get(str(event.group_id)): - await statistics.finish("该群统计数据不存在...", at_sender=True) - else: - data: dict = json.load(open(statistics_user_file, "r", encoding="utf8")) - if not data[arg].get(str(event.user_id)): - await statistics.finish("该用户统计数据不存在...", at_sender=True) - day_index = data["day_index"] - data = data[arg][key] - if itype == "group": - name = await GroupInfo.get_group_info(event.group_id) - name = name.group_name if name else str(event.group_id) - else: - name = event.sender.card if event.sender.card else event.sender.nickname - img = await generate_statistics_img(data, arg, name, plugin, day_index) - await statistics.send(image(b64=img)) - - -async def generate_statistics_img( - data: dict, arg: str, name: str, plugin: str, day_index: int -): - try: - plugin = plugins2settings_manager.get_plugin_data(plugin)['cmd'][0] - except (KeyError, IndexError): - pass - bar_graph = None - if arg == "day_statistics": - bar_graph = await init_bar_graph(data, f"{name} 日功能调用统计") - elif arg == "week_statistics": - if plugin: - current_week = day_index % 7 - week_lst = [] - if current_week == 0: - week_lst = [1, 2, 3, 4, 5, 6, 7] - else: - for i in range(current_week + 1, 7): - week_lst.append(str(i)) - for i in range(current_week + 1): - week_lst.append(str(i)) - count = [] - for i in range(7): - if int(week_lst[i]) == 7: - try: - count.append(data[str(0)][plugin]) - except KeyError: - count.append(0) - else: - try: - count.append(data[str(week_lst[i])][plugin]) - except KeyError: - count.append(0) - week_lst = ["7" if i == "0" else i for i in week_lst] - bar_graph = CreateMat( - y=count, - mat_type="line", - title=f"{name} 周 {plugin} 功能调用统计【为7天统计】", - x_index=week_lst, - display_num=True, - background=[ - f"{IMAGE_PATH}/background/create_mat/{x}" - for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") - ], - bar_color=["*"], - ) - else: - bar_graph = await init_bar_graph(update_data(data), f"{name} 周功能调用统计【为7天统计】") - elif arg == "month_statistics": - if plugin: - day_index = day_index % 30 - day_lst = [] - for i in range(day_index + 1, 30): - day_lst.append(i) - for i in range(day_index + 1): - day_lst.append(i) - count = [data[str(day_lst[i])][plugin] for i in range(30)] - day_lst = [str(x + 1) for x in day_lst] - bar_graph = CreateMat( - y=count, - mat_type="line", - title=f"{name} 月 {plugin} 功能调用统计【为30天统计】", - x_index=day_lst, - display_num=True, - background=[ - f"{IMAGE_PATH}/background/create_mat/{x}" - for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") - ], - bar_color=["*"], - ) - else: - bar_graph = await init_bar_graph(update_data(data), f"{name} 月功能调用统计【为30天统计】") - elif arg == "total_statistics": - bar_graph = await init_bar_graph(data, f"{name} 功能调用统计") - await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph) - return bar_graph.pic2bs4() - - -async def init_bar_graph(data: dict, title: str) -> CreateMat: - return await asyncio.get_event_loop().run_in_executor(None, _init_bar_graph, data, title) - - -def _init_bar_graph(data: dict, title: str) -> CreateMat: - bar_graph = CreateMat( - y=[data[x] for x in data.keys() if data[x] != 0], - mat_type="barh", - title=title, - x_index=[x for x in data.keys() if data[x] != 0], - display_num=True, - background=[ - f"{IMAGE_PATH}/background/create_mat/{x}" - for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") - ], - bar_color=["*"], - ) - return bar_graph - - -def update_data(data: dict): - tmp_dict = {} - for day in data.keys(): - for plugin_name in data[day].keys(): - # print(f'{day}:{plugin_name} = {data[day][plugin_name]}') - if data[day][plugin_name] is not None: - if tmp_dict.get(plugin_name) is None: - tmp_dict[plugin_name] = 1 - else: - tmp_dict[plugin_name] += data[day][plugin_name] - return tmp_dict - +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent +from models.group_info import GroupInfo +from nonebot.typing import T_State +from pathlib import Path +from configs.path_config import DATA_PATH, IMAGE_PATH +from utils.utils import get_message_text +from utils.image_utils import CreateMat +from utils.message_builder import image +from utils.manager import plugins2settings_manager +import asyncio +import os + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +__zx_plugin_name__ = "功能调用统计可视化" +__plugin_usage__ = """ +usage: + 功能调用统计可视化 + 指令: + 功能调用统计 + 日功能调用统计 + 周功能调用统计 ?[功能] + 月功能调用统计 ?[功能] + 我的功能调用统计 + 我的日功能调用统计 ?[功能] + 我的周功能调用统计 ?[功能] + 我的月功能调用统计 ?[功能] +""".strip() +__plugin_superuser_usage__ = """ +usage: + 功能调用统计可视化 + 指令: + 全局功能调用统计 + 全局日功能调用统计 + 全局周功能调用统计 ?[功能] + 全局月功能调用统计 ?[功能] +""".strip() +__plugin_des__ = "功能调用统计可视化" +__plugin_cmd__ = [ + "功能调用统计", + "全局功能调用统计 [_superuser]", + "全局日功能调用统计 [_superuser]", + "全局周功能调用统计 ?[功能] [_superuser]", + "全局月功能调用统计 ?[功能] [_superuser]", + "周功能调用统计 ?[功能]", + "月功能调用统计 ?[功能]", + "我的功能调用统计", + "我的日功能调用统计 ?[功能]", + "我的周功能调用统计 ?[功能]", + "我的月功能调用统计 ?[功能]", +] +__plugin_type__ = ("功能调用统计可视化", 1) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["功能调用统计"], +} + + +statistics = on_command( + "功能调用统计", + aliases={ + "全局功能调用统计", + "全局日功能调用统计", + "全局周功能调用统计", + "全局月功能调用统计", + "日功能调用统计", + "周功能调用统计", + "月功能调用统计", + "我的功能调用统计", + "我的日功能调用统计", + "我的周功能调用统计", + "我的月功能调用统计", + }, + priority=5, + block=True, +) + + +statistics_group_file = Path(DATA_PATH) / "statistics" / "_prefix_count.json" +statistics_user_file = Path(DATA_PATH) / "statistics" / "_prefix_user_count.json" + + +@statistics.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if state["_prefix"]["raw_command"][:2] == "全局": + if str(event.user_id) in bot.config.superusers: + data: dict = json.load(open(statistics_group_file, "r", encoding="utf8")) + if state["_prefix"]["raw_command"][2] == '日': + itype = 'day_statistics' + elif state["_prefix"]["raw_command"][2] == '周': + itype = 'week_statistics' + elif state["_prefix"]["raw_command"][2] == '月': + itype = 'month_statistics' + else: + itype = 'total_statistics' + tmp_dict = {} + data = data[itype] + if itype in ["day_statistics", "total_statistics"]: + for key in data['total']: + tmp_dict[key] = data['total'][key] + else: + for group in data.keys(): + if group != 'total': + for day in data[group].keys(): + for plugin_name in data[group][day].keys(): + if data[group][day][plugin_name] is not None: + if tmp_dict.get(plugin_name) is None: + tmp_dict[plugin_name] = 1 + else: + tmp_dict[plugin_name] += data[group][day][plugin_name] + bar_graph = await init_bar_graph(tmp_dict, state["_prefix"]["raw_command"]) + await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph) + await statistics.finish(image(b64=bar_graph.pic2bs4())) + return + if state["_prefix"]["raw_command"][:2] == "我的": + itype = "user" + key = str(event.user_id) + state["_prefix"]["raw_command"] = state["_prefix"]["raw_command"][2:] + if not statistics_user_file.exists(): + await statistics.finish("统计文件不存在...", at_sender=True) + else: + if not isinstance(event, GroupMessageEvent): + await statistics.finish("请在群内调用此功能...") + itype = "group" + key = str(event.group_id) + if not statistics_group_file.exists(): + await statistics.finish("统计文件不存在...", at_sender=True) + plugin = "" + if state["_prefix"]["raw_command"][0] == "日": + arg = "day_statistics" + elif state["_prefix"]["raw_command"][0] == "周": + arg = "week_statistics" + elif state["_prefix"]["raw_command"][0] == "月": + arg = "month_statistics" + else: + arg = "total_statistics" + if msg: + plugin = plugins2settings_manager.get_plugin_module(msg) + if not plugin: + if arg not in ["day_statistics", "total_statistics"]: + await statistics.finish("未找到此功能的调用...", at_sender=True) + if itype == "group": + data: dict = json.load(open(statistics_group_file, "r", encoding="utf8")) + if not data[arg].get(str(event.group_id)): + await statistics.finish("该群统计数据不存在...", at_sender=True) + else: + data: dict = json.load(open(statistics_user_file, "r", encoding="utf8")) + if not data[arg].get(str(event.user_id)): + await statistics.finish("该用户统计数据不存在...", at_sender=True) + day_index = data["day_index"] + data = data[arg][key] + if itype == "group": + name = await GroupInfo.get_group_info(event.group_id) + name = name.group_name if name else str(event.group_id) + else: + name = event.sender.card if event.sender.card else event.sender.nickname + img = await generate_statistics_img(data, arg, name, plugin, day_index) + await statistics.send(image(b64=img)) + + +async def generate_statistics_img( + data: dict, arg: str, name: str, plugin: str, day_index: int +): + try: + plugin = plugins2settings_manager.get_plugin_data(plugin)['cmd'][0] + except (KeyError, IndexError): + pass + bar_graph = None + if arg == "day_statistics": + bar_graph = await init_bar_graph(data, f"{name} 日功能调用统计") + elif arg == "week_statistics": + if plugin: + current_week = day_index % 7 + week_lst = [] + if current_week == 0: + week_lst = [1, 2, 3, 4, 5, 6, 7] + else: + for i in range(current_week + 1, 7): + week_lst.append(str(i)) + for i in range(current_week + 1): + week_lst.append(str(i)) + count = [] + for i in range(7): + if int(week_lst[i]) == 7: + try: + count.append(data[str(0)][plugin]) + except KeyError: + count.append(0) + else: + try: + count.append(data[str(week_lst[i])][plugin]) + except KeyError: + count.append(0) + week_lst = ["7" if i == "0" else i for i in week_lst] + bar_graph = CreateMat( + y=count, + mat_type="line", + title=f"{name} 周 {plugin} 功能调用统计【为7天统计】", + x_index=week_lst, + display_num=True, + background=[ + f"{IMAGE_PATH}/background/create_mat/{x}" + for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") + ], + bar_color=["*"], + ) + else: + bar_graph = await init_bar_graph(update_data(data), f"{name} 周功能调用统计【为7天统计】") + elif arg == "month_statistics": + if plugin: + day_index = day_index % 30 + day_lst = [] + for i in range(day_index + 1, 30): + day_lst.append(i) + for i in range(day_index + 1): + day_lst.append(i) + count = [data[str(day_lst[i])][plugin] for i in range(30)] + day_lst = [str(x + 1) for x in day_lst] + bar_graph = CreateMat( + y=count, + mat_type="line", + title=f"{name} 月 {plugin} 功能调用统计【为30天统计】", + x_index=day_lst, + display_num=True, + background=[ + f"{IMAGE_PATH}/background/create_mat/{x}" + for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") + ], + bar_color=["*"], + ) + else: + bar_graph = await init_bar_graph(update_data(data), f"{name} 月功能调用统计【为30天统计】") + elif arg == "total_statistics": + bar_graph = await init_bar_graph(data, f"{name} 功能调用统计") + await asyncio.get_event_loop().run_in_executor(None, bar_graph.gen_graph) + return bar_graph.pic2bs4() + + +async def init_bar_graph(data: dict, title: str) -> CreateMat: + return await asyncio.get_event_loop().run_in_executor(None, _init_bar_graph, data, title) + + +def _init_bar_graph(data: dict, title: str) -> CreateMat: + bar_graph = CreateMat( + y=[data[x] for x in data.keys() if data[x] != 0], + mat_type="barh", + title=title, + x_index=[x for x in data.keys() if data[x] != 0], + display_num=True, + background=[ + f"{IMAGE_PATH}/background/create_mat/{x}" + for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") + ], + bar_color=["*"], + ) + return bar_graph + + +def update_data(data: dict): + tmp_dict = {} + for day in data.keys(): + for plugin_name in data[day].keys(): + # print(f'{day}:{plugin_name} = {data[day][plugin_name]}') + if data[day][plugin_name] is not None: + if tmp_dict.get(plugin_name) is None: + tmp_dict[plugin_name] = 1 + else: + tmp_dict[plugin_name] += data[day][plugin_name] + return tmp_dict + diff --git a/plugins/statistics/statistics_hook.py b/plugins/statistics/statistics_hook.py old mode 100644 new mode 100755 index b2f0b2d6..c94622c9 --- a/plugins/statistics/statistics_hook.py +++ b/plugins/statistics/statistics_hook.py @@ -1,217 +1,217 @@ -from configs.path_config import DATA_PATH -from nonebot.matcher import Matcher -from nonebot.message import run_postprocessor -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from datetime import datetime -from utils.manager import plugins2settings_manager -from utils.utils import scheduler -from nonebot.typing import Optional -from pathlib import Path - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -__zx_plugin_name__ = "功能调用统计 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - -statistics_group_file = Path(DATA_PATH) / "statistics" / "_prefix_count.json" -statistics_user_file = Path(DATA_PATH) / "statistics" / "_prefix_user_count.json" - -try: - with open(statistics_group_file, "r", encoding="utf8") as f: - _prefix_count_dict = json.load(f) -except (FileNotFoundError, ValueError): - _prefix_count_dict = { - "total_statistics": { - "total": {}, - }, - "day_statistics": { - "total": {}, - }, - "week_statistics": { - "total": {}, - }, - "month_statistics": { - "total": {}, - }, - "start_time": str(datetime.now().date()), - "day_index": 0, - } - -try: - with open(statistics_user_file, "r", encoding="utf8") as f: - _prefix_user_count_dict = json.load(f) -except (FileNotFoundError, ValueError): - _prefix_user_count_dict = { - "total_statistics": { - "total": {}, - }, - "day_statistics": { - "total": {}, - }, - "week_statistics": { - "total": {}, - }, - "month_statistics": { - "total": {}, - }, - "start_time": str(datetime.now().date()), - "day_index": 0, - } - - -# 以前版本转换 -if _prefix_count_dict.get("day_index") is None: - tmp = _prefix_count_dict.copy() - _prefix_count_dict = { - "total_statistics": tmp["total_statistics"], - "day_statistics": { - "total": {}, - }, - "week_statistics": { - "total": {}, - }, - "month_statistics": { - "total": {}, - }, - "start_time": tmp["start_time"], - "day_index": 0, - } - - -# 添加命令次数 -@run_postprocessor -async def _( - matcher: Matcher, - exception: Optional[Exception], - bot: Bot, - event: GroupMessageEvent, - state: T_State, -): - global _prefix_count_dict - if ( - matcher.type == "message" - and matcher.priority not in [1, 9] - and matcher.module not in ["update_info", "statistics_handle"] - ): - module = matcher.module - day_index = _prefix_count_dict["day_index"] - try: - group_id = str(event.group_id) - except AttributeError: - group_id = "total" - user_id = str(event.user_id) - plugin_name = plugins2settings_manager.get_plugin_data(module) - if plugin_name and plugin_name.get('cmd'): - plugin_name = plugin_name.get('cmd')[0] - check_exists_key(group_id, user_id, plugin_name) - for data in [_prefix_count_dict, _prefix_user_count_dict]: - data["total_statistics"]["total"][plugin_name] += 1 - data["day_statistics"]["total"][plugin_name] += 1 - data["week_statistics"]["total"][plugin_name] += 1 - data["month_statistics"]["total"][plugin_name] += 1 - # print(_prefix_count_dict) - if group_id != "total": - for data in [_prefix_count_dict, _prefix_user_count_dict]: - if data == _prefix_count_dict: - key = group_id - else: - key = user_id - data["total_statistics"][key][plugin_name] += 1 - data["day_statistics"][key][plugin_name] += 1 - data["week_statistics"][key][str(day_index % 7)][ - plugin_name - ] += 1 - data["month_statistics"][key][str(day_index % 30)][ - plugin_name - ] += 1 - with open(statistics_group_file, "w", encoding="utf8") as f: - json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False) - with open( - statistics_user_file, "w", encoding="utf8" - ) as f: - json.dump(_prefix_user_count_dict, f, ensure_ascii=False, indent=4) - - -def check_exists_key(group_id: str, user_id: str, plugin_name: str): - global _prefix_count_dict, _prefix_user_count_dict - for data in [_prefix_count_dict, _prefix_user_count_dict]: - if data == _prefix_count_dict: - key = group_id - else: - key = user_id - if not data["total_statistics"]["total"].get(plugin_name): - data["total_statistics"]["total"][plugin_name] = 0 - if not data["day_statistics"]["total"].get(plugin_name): - data["day_statistics"]["total"][plugin_name] = 0 - if not data["week_statistics"]["total"].get(plugin_name): - data["week_statistics"]["total"][plugin_name] = 0 - if not data["month_statistics"]["total"].get(plugin_name): - data["month_statistics"]["total"][plugin_name] = 0 - - if not data["total_statistics"].get(key): - data["total_statistics"][key] = {} - if not data["total_statistics"][key].get(plugin_name): - data["total_statistics"][key][plugin_name] = 0 - if not data["day_statistics"].get(key): - data["day_statistics"][key] = {} - if not data["day_statistics"][key].get(plugin_name): - data["day_statistics"][key][plugin_name] = 0 - - if key != 'total': - if not data["week_statistics"].get(key): - data["week_statistics"][key] = {} - if data["week_statistics"][key].get("0") is None: - for i in range(7): - data["week_statistics"][key][str(i)] = {} - if data["week_statistics"][key]["0"].get(plugin_name) is None: - for i in range(7): - data["week_statistics"][key][str(i)][plugin_name] = 0 - - if not data["month_statistics"].get(key): - data["month_statistics"][key] = {} - if data["month_statistics"][key].get("0") is None: - for i in range(30): - data["month_statistics"][key][str(i)] = {} - if data["month_statistics"][key]["0"].get(plugin_name) is None: - for i in range(30): - data["month_statistics"][key][str(i)][plugin_name] = 0 - - -# 天 -@scheduler.scheduled_job( - "cron", - hour=0, - minute=1, -) -async def _(): - for data in [_prefix_count_dict, _prefix_user_count_dict]: - data["day_index"] += 1 - for x in data["day_statistics"].keys(): - for key in data["day_statistics"][x].keys(): - try: - data["day_statistics"][x][key] = 0 - except KeyError: - pass - for type_ in ["week_statistics", "month_statistics"]: - index = str( - data["day_index"] % 7 - if type_ == "week_statistics" - else data["day_index"] % 30 - ) - for x in data[type_].keys(): - try: - for key in data[type_][x][index].keys(): - data[type_][x][index][key] = 0 - except KeyError: - pass - with open(statistics_group_file, "w", encoding="utf8") as f: - json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False) - with open(statistics_user_file, "w", encoding="utf8") as f: - json.dump(_prefix_user_count_dict, f, indent=4, ensure_ascii=False) - +from configs.path_config import DATA_PATH +from nonebot.matcher import Matcher +from nonebot.message import run_postprocessor +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from datetime import datetime +from utils.manager import plugins2settings_manager +from utils.utils import scheduler +from nonebot.typing import Optional +from pathlib import Path + +try: + import ujson as json +except ModuleNotFoundError: + import json + + +__zx_plugin_name__ = "功能调用统计 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + +statistics_group_file = Path(DATA_PATH) / "statistics" / "_prefix_count.json" +statistics_user_file = Path(DATA_PATH) / "statistics" / "_prefix_user_count.json" + +try: + with open(statistics_group_file, "r", encoding="utf8") as f: + _prefix_count_dict = json.load(f) +except (FileNotFoundError, ValueError): + _prefix_count_dict = { + "total_statistics": { + "total": {}, + }, + "day_statistics": { + "total": {}, + }, + "week_statistics": { + "total": {}, + }, + "month_statistics": { + "total": {}, + }, + "start_time": str(datetime.now().date()), + "day_index": 0, + } + +try: + with open(statistics_user_file, "r", encoding="utf8") as f: + _prefix_user_count_dict = json.load(f) +except (FileNotFoundError, ValueError): + _prefix_user_count_dict = { + "total_statistics": { + "total": {}, + }, + "day_statistics": { + "total": {}, + }, + "week_statistics": { + "total": {}, + }, + "month_statistics": { + "total": {}, + }, + "start_time": str(datetime.now().date()), + "day_index": 0, + } + + +# 以前版本转换 +if _prefix_count_dict.get("day_index") is None: + tmp = _prefix_count_dict.copy() + _prefix_count_dict = { + "total_statistics": tmp["total_statistics"], + "day_statistics": { + "total": {}, + }, + "week_statistics": { + "total": {}, + }, + "month_statistics": { + "total": {}, + }, + "start_time": tmp["start_time"], + "day_index": 0, + } + + +# 添加命令次数 +@run_postprocessor +async def _( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: GroupMessageEvent, + state: T_State, +): + global _prefix_count_dict + if ( + matcher.type == "message" + and matcher.priority not in [1, 9] + and matcher.module not in ["update_info", "statistics_handle"] + ): + module = matcher.module + day_index = _prefix_count_dict["day_index"] + try: + group_id = str(event.group_id) + except AttributeError: + group_id = "total" + user_id = str(event.user_id) + plugin_name = plugins2settings_manager.get_plugin_data(module) + if plugin_name and plugin_name.get('cmd'): + plugin_name = plugin_name.get('cmd')[0] + check_exists_key(group_id, user_id, plugin_name) + for data in [_prefix_count_dict, _prefix_user_count_dict]: + data["total_statistics"]["total"][plugin_name] += 1 + data["day_statistics"]["total"][plugin_name] += 1 + data["week_statistics"]["total"][plugin_name] += 1 + data["month_statistics"]["total"][plugin_name] += 1 + # print(_prefix_count_dict) + if group_id != "total": + for data in [_prefix_count_dict, _prefix_user_count_dict]: + if data == _prefix_count_dict: + key = group_id + else: + key = user_id + data["total_statistics"][key][plugin_name] += 1 + data["day_statistics"][key][plugin_name] += 1 + data["week_statistics"][key][str(day_index % 7)][ + plugin_name + ] += 1 + data["month_statistics"][key][str(day_index % 30)][ + plugin_name + ] += 1 + with open(statistics_group_file, "w", encoding="utf8") as f: + json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False) + with open( + statistics_user_file, "w", encoding="utf8" + ) as f: + json.dump(_prefix_user_count_dict, f, ensure_ascii=False, indent=4) + + +def check_exists_key(group_id: str, user_id: str, plugin_name: str): + global _prefix_count_dict, _prefix_user_count_dict + for data in [_prefix_count_dict, _prefix_user_count_dict]: + if data == _prefix_count_dict: + key = group_id + else: + key = user_id + if not data["total_statistics"]["total"].get(plugin_name): + data["total_statistics"]["total"][plugin_name] = 0 + if not data["day_statistics"]["total"].get(plugin_name): + data["day_statistics"]["total"][plugin_name] = 0 + if not data["week_statistics"]["total"].get(plugin_name): + data["week_statistics"]["total"][plugin_name] = 0 + if not data["month_statistics"]["total"].get(plugin_name): + data["month_statistics"]["total"][plugin_name] = 0 + + if not data["total_statistics"].get(key): + data["total_statistics"][key] = {} + if not data["total_statistics"][key].get(plugin_name): + data["total_statistics"][key][plugin_name] = 0 + if not data["day_statistics"].get(key): + data["day_statistics"][key] = {} + if not data["day_statistics"][key].get(plugin_name): + data["day_statistics"][key][plugin_name] = 0 + + if key != 'total': + if not data["week_statistics"].get(key): + data["week_statistics"][key] = {} + if data["week_statistics"][key].get("0") is None: + for i in range(7): + data["week_statistics"][key][str(i)] = {} + if data["week_statistics"][key]["0"].get(plugin_name) is None: + for i in range(7): + data["week_statistics"][key][str(i)][plugin_name] = 0 + + if not data["month_statistics"].get(key): + data["month_statistics"][key] = {} + if data["month_statistics"][key].get("0") is None: + for i in range(30): + data["month_statistics"][key][str(i)] = {} + if data["month_statistics"][key]["0"].get(plugin_name) is None: + for i in range(30): + data["month_statistics"][key][str(i)][plugin_name] = 0 + + +# 天 +@scheduler.scheduled_job( + "cron", + hour=0, + minute=1, +) +async def _(): + for data in [_prefix_count_dict, _prefix_user_count_dict]: + data["day_index"] += 1 + for x in data["day_statistics"].keys(): + for key in data["day_statistics"][x].keys(): + try: + data["day_statistics"][x][key] = 0 + except KeyError: + pass + for type_ in ["week_statistics", "month_statistics"]: + index = str( + data["day_index"] % 7 + if type_ == "week_statistics" + else data["day_index"] % 30 + ) + for x in data[type_].keys(): + try: + for key in data[type_][x][index].keys(): + data[type_][x][index][key] = 0 + except KeyError: + pass + with open(statistics_group_file, "w", encoding="utf8") as f: + json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False) + with open(statistics_user_file, "w", encoding="utf8") as f: + json.dump(_prefix_user_count_dict, f, indent=4, ensure_ascii=False) + diff --git a/plugins/translate/__init__.py b/plugins/translate/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/translate/data_source.py b/plugins/translate/data_source.py old mode 100644 new mode 100755 index e2fd80f5..77df30a1 --- a/plugins/translate/data_source.py +++ b/plugins/translate/data_source.py @@ -1,6 +1,4 @@ -import aiohttp -from utils.utils import get_local_proxy -from utils.user_agent import get_user_agent +from utils.http_utils import AsyncHttpx url = f"http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" @@ -16,11 +14,9 @@ async def translate_msg(language_type, msg): "action": "FY_BY_CLICKBUTTON", "typoResult": "true", } - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.post(url, data=data, proxy=get_local_proxy()) as res: - data = await res.json() - if data["errorCode"] == 0: - return data["translateResult"][0][0]["tgt"] + data = (await AsyncHttpx.post(url, data=data)).json() + if data["errorCode"] == 0: + return data["translateResult"][0][0]["tgt"] return "翻译惜败.." diff --git a/plugins/update_gocqhttp/__init__.py b/plugins/update_gocqhttp/__init__.py old mode 100644 new mode 100755 index d910e596..ba6e7ed8 --- a/plugins/update_gocqhttp/__init__.py +++ b/plugins/update_gocqhttp/__init__.py @@ -2,12 +2,12 @@ from nonebot import on_command from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from .data_source import download_gocq_lasted, upload_gocq_lasted -import os from services.log import logger from utils.utils import scheduler, get_bot from nonebot.permission import SUPERUSER from configs.config import Config from pathlib import Path +import os __zx_plugin_name__ = "更新gocq [Superuser]" diff --git a/plugins/update_gocqhttp/data_source.py b/plugins/update_gocqhttp/data_source.py old mode 100644 new mode 100755 index 2782f629..9383648d --- a/plugins/update_gocqhttp/data_source.py +++ b/plugins/update_gocqhttp/data_source.py @@ -1,10 +1,8 @@ -import aiohttp -from utils.utils import get_local_proxy, get_bot -from utils.user_agent import get_user_agent +from utils.utils import get_bot +from bs4 import BeautifulSoup +from utils.http_utils import AsyncHttpx import asyncio import platform -import aiofiles -from bs4 import BeautifulSoup import os if platform.system() == "Windows": @@ -15,53 +13,45 @@ url = "https://github.com/Mrs4s/go-cqhttp/releases" async def download_gocq_lasted(path: str): - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, proxy=get_local_proxy()) as response: - soup = BeautifulSoup(await response.text(), "lxml") - a = soup.find("div", {"class": "release-header"}).find("a") - title = a.text - _url = a.get("href") - for file in os.listdir(path): - if file.endswith(".zip"): - if ( - file == title + "-windows-amd64.zip" - or file == title + "_windows_amd64.zip" - ): - return "gocqhttp没有更新!" - for file in os.listdir(path): - os.remove(path + file) - async with session.get( - "https://github.com" + _url, proxy=get_local_proxy() - ) as res: - update_info = "" - soup = BeautifulSoup(await res.text(), "lxml") - info_div = soup.find("div", {"class": "markdown-body"}) - for p in info_div.find_all("p"): - update_info += p.text.replace("
", "\n") + "\n" - div_all = soup.select( - "div.d-flex.flex-justify-between.flex-items-center.py-1.py-md-2.Box-body.px-2" - ) - for div in div_all: - if ( - div.find("a").find("span").text == title + "-windows-amd64.zip" - or div.find("a").find("span").text - == title + "-linux-arm64.tar.gz" - or div.find("a").find("span").text - == "go-cqhttp_windows_amd64.zip" - or div.find("a").find("span").text - == "go-cqhttp_linux_arm64.tar.gz" - ): - file_url = div.find("a").get("href") - if div.find("a").find("span").text.find("windows") == -1: - tag = "-linux-arm64.tar.gz" - else: - tag = "-windows-amd64.zip" - async with session.get( - "https://github.com" + file_url, proxy=get_local_proxy() - ) as res_file: - async with aiofiles.open(path + title + tag, "wb") as f: - await f.write(await res_file.read()) - return update_info + text = (await AsyncHttpx.get(url)).text + soup = BeautifulSoup(text, "lxml") + a = soup.find("div", {"class": "release-header"}).find("a") + title = a.text + _url = a.get("href") + for file in os.listdir(path): + if file.endswith(".zip"): + if ( + file == title + "-windows-amd64.zip" + or file == title + "_windows_amd64.zip" + ): + return "gocqhttp没有更新!" + for file in os.listdir(path): + os.remove(path + file) + text = (await AsyncHttpx.get("https://github.com" + _url)).text + update_info = "" + soup = BeautifulSoup(text, "lxml") + info_div = soup.find("div", {"class": "markdown-body"}) + for p in info_div.find_all("p"): + update_info += p.text.replace("
", "\n") + "\n" + div_all = soup.select( + "div.d-flex.flex-justify-between.flex-items-center.py-1.py-md-2.Box-body.px-2" + ) + for div in div_all: + if ( + div.find("a").find("span").text == title + "-windows-amd64.zip" + or div.find("a").find("span").text == title + "-linux-arm64.tar.gz" + or div.find("a").find("span").text == "go-cqhttp_windows_amd64.zip" + or div.find("a").find("span").text == "go-cqhttp_linux_arm64.tar.gz" + ): + file_url = div.find("a").get("href") + if div.find("a").find("span").text.find("windows") == -1: + tag = "-linux-arm64.tar.gz" + else: + tag = "-windows-amd64.zip" + await AsyncHttpx.download_file( + "https://github.com" + file_url, path + title + tag + ) + return update_info async def upload_gocq_lasted(path, name, group_id): diff --git a/plugins/update_pic.py b/plugins/update_picture.py old mode 100644 new mode 100755 similarity index 90% rename from plugins/update_pic.py rename to plugins/update_picture.py index 15511fdb..f197a5a3 --- a/plugins/update_pic.py +++ b/plugins/update_picture.py @@ -7,11 +7,11 @@ from nonebot.rule import to_me from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from utils.utils import get_message_imgs -import aiofiles -import aiohttp +from pathlib import Path from utils.utils import is_number, get_message_text from utils.image_utils import CreateImg, pic2b64 from configs.config import NICKNAME +from utils.http_utils import AsyncHttpx import cv2 import numpy as np @@ -203,22 +203,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): y = int(y) index = 0 result = "" - async with aiohttp.ClientSession() as session: - for img_url in img_list: - async with session.get(img_url, timeout=7) as response: - if response.status == 200: - async with aiofiles.open( - IMAGE_PATH + f"temp/{event.user_id}_{index}_update.png", "wb" - ) as f: - await f.write(await response.read()) - index += 1 - else: - logger.warning( - f"USER {event.user_id} GROUP " - f"{event.group_id if event.message_type != 'private' else 'private'} " - f"使用 {method} 时下载图片超时" - ) - await update_img.finish("获取图片超时了...", at_sender=True) + for img_url in img_list: + if await AsyncHttpx.download_file( + img_url, Path(IMAGE_PATH) / "temp" / f"{event.user_id}_{index}_update.png" + ): + index += 1 + else: + await update_img.finish("获取图片超时了...", at_sender=True) if index == 0: return if method in ["修改尺寸", "1"]: diff --git a/plugins/weather/__init__.py b/plugins/weather/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/weather/data_source.py b/plugins/weather/data_source.py old mode 100644 new mode 100755 index a787cf67..d5c39431 --- a/plugins/weather/data_source.py +++ b/plugins/weather/data_source.py @@ -1,11 +1,10 @@ from utils.message_builder import image -from utils.user_agent import get_user_agent from configs.path_config import TEXT_PATH from configs.config import NICKNAME from typing import List from nonebot import Driver from pathlib import Path -import aiohttp +from utils.http_utils import AsyncHttpx import ujson as json import nonebot @@ -17,37 +16,44 @@ data = {} async def get_weather_of_city(city: str) -> str: + """ + 获取城市天气数据 + :param city: 城市 + """ code = _check_exists_city(city) if code == 999: return "不要查一个省份的天气啊,很累人的!" elif code == 998: return f"{NICKNAME}没查到!!试试查火星的天气?" else: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get( - f"http://wthrcdn.etouch.cn/weather_mini?city={city}", timeout=5 - ) as res: - data_json = json.loads(await res.text(encoding="utf8")) - if "desc" in data_json: - if data_json["desc"] == "invilad-citykey": - return f"{NICKNAME}没查到!!试试查火星的天气?" + image( - "shengqi", "zhenxun" - ) - elif data_json["desc"] == "OK": - w_type = data_json["data"]["forecast"][0]["type"] - w_max = data_json["data"]["forecast"][0]["high"][3:] - w_min = data_json["data"]["forecast"][0]["low"][3:] - fengli = data_json["data"]["forecast"][0]["fengli"][9:-3] - ganmao = data_json["data"]["ganmao"] - fengxiang = data_json["data"]["forecast"][0]["fengxiang"] - repass = f"{city}的天气是 {w_type} 天\n最高温度: {w_max}\n最低温度: {w_min}\n风力: {fengli} {fengxiang}\n{ganmao}" - return repass - else: - return "好像出错了?" + data_json = json.loads( + ( + await AsyncHttpx.get( + f"http://wthrcdn.etouch.cn/weather_mini?city={city}" + ) + ).text + ) + if "desc" in data_json: + if data_json["desc"] == "invilad-citykey": + return f"{NICKNAME}没查到!!试试查火星的天气?" + image("shengqi", "zhenxun") + elif data_json["desc"] == "OK": + w_type = data_json["data"]["forecast"][0]["type"] + w_max = data_json["data"]["forecast"][0]["high"][3:] + w_min = data_json["data"]["forecast"][0]["low"][3:] + fengli = data_json["data"]["forecast"][0]["fengli"][9:-3] + ganmao = data_json["data"]["ganmao"] + fengxiang = data_json["data"]["forecast"][0]["fengxiang"] + repass = f"{city}的天气是 {w_type} 天\n最高温度: {w_max}\n最低温度: {w_min}\n风力: {fengli} {fengxiang}\n{ganmao}" + return repass + else: + return "好像出错了?再试试?" -# 城市是否存在或是否是省份 def _check_exists_city(city: str) -> int: + """ + 检测城市是否存在合法 + :param city: 城市名称 + """ global data city = city if city[-1] != "市" else city[:-1] for province in data.keys(): @@ -61,6 +67,9 @@ def _check_exists_city(city: str) -> int: def get_city_list() -> List[str]: + """ + 获取城市列表 + """ global data if not data: try: diff --git a/plugins/what_anime/__init__.py b/plugins/what_anime/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/what_anime/data_source.py b/plugins/what_anime/data_source.py old mode 100644 new mode 100755 index d80b65a7..87fc071a --- a/plugins/what_anime/data_source.py +++ b/plugins/what_anime/data_source.py @@ -1,8 +1,7 @@ import time from services.log import logger from utils.langconv import * -import aiohttp -from utils.user_agent import get_user_agent +from utils.http_utils import AsyncHttpx async def get_anime(anime: str) -> str: @@ -10,44 +9,39 @@ async def get_anime(anime: str) -> str: url = "https://api.trace.moe/search?anilistInfo&url={}".format(anime) logger.debug("[info]Now starting get the {}".format(url)) try: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, timeout=45) as response: - if response.status == 200: - anime_json = await response.json() - if not anime_json["error"]: - if anime_json == "Error reading imagenull": - return "图像源错误,注意必须是静态图片哦" - repass = "" - # 拿到动漫 中文名 - for anime in anime_json["result"][:5]: - synonyms = anime["anilist"]["synonyms"] - for x in synonyms: - _count_ch = 0 - for word in x: - if "\u4e00" <= word <= "\u9fff": - _count_ch += 1 - if _count_ch > 3: - anime_name = x - break - else: - anime_name = anime["anilist"]["title"]["native"] - episode = anime["episode"] - from_ = int(anime["from"]) - m, s = divmod(from_, 60) - similarity = anime["similarity"] - putline = "[ {} ][{}][{}:{}] 相似度:{:.2%}".format( - Converter("zh-hans").convert(anime_name), - episode if episode else "?", - m, - s, - similarity, - ) - repass += putline + "\n" - return f"耗时 {int(time.time() - s_time)} 秒\n" + repass[:-1] - else: - return f'访问错误 error:{anime_json["error"]}' + anime_json = (await AsyncHttpx.get(url)).json() + if not anime_json["error"]: + if anime_json == "Error reading imagenull": + return "图像源错误,注意必须是静态图片哦" + repass = "" + # 拿到动漫 中文名 + for anime in anime_json["result"][:5]: + synonyms = anime["anilist"]["synonyms"] + for x in synonyms: + _count_ch = 0 + for word in x: + if "\u4e00" <= word <= "\u9fff": + _count_ch += 1 + if _count_ch > 3: + anime_name = x + break else: - return f"访问失败,请再试一次吧, status: {response.status}" + anime_name = anime["anilist"]["title"]["native"] + episode = anime["episode"] + from_ = int(anime["from"]) + m, s = divmod(from_, 60) + similarity = anime["similarity"] + putline = "[ {} ][{}][{}:{}] 相似度:{:.2%}".format( + Converter("zh-hans").convert(anime_name), + episode if episode else "?", + m, + s, + similarity, + ) + repass += putline + "\n" + return f"耗时 {int(time.time() - s_time)} 秒\n" + repass[:-1] + else: + return f'访问错误 error:{anime_json["error"]}' except Exception as e: - logger.error(f"识番发生错误 e:{e}") + logger.error(f"识番发生错误 {type(e)}:{e}") return "发生了奇怪的错误,那就没办法了,再试一次?" diff --git a/plugins/white2black_img.py b/plugins/white2black_image.py old mode 100644 new mode 100755 similarity index 71% rename from plugins/white2black_img.py rename to plugins/white2black_image.py index 2d5e8e5e..9a5bf7f2 --- a/plugins/white2black_img.py +++ b/plugins/white2black_image.py @@ -1,14 +1,13 @@ from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot import on_command -from utils.utils import get_message_imgs, get_local_proxy, get_message_text, is_chinese +from utils.utils import get_message_imgs, get_message_text, is_chinese from utils.message_builder import image -import aiohttp -import aiofiles -from configs.path_config import IMAGE_PATH +from configs.path_config import TEMP_PATH from utils.image_utils import CreateImg -from utils.user_agent import get_user_agent from services.log import logger +from utils.http_utils import AsyncHttpx +from pathlib import Path # ZH_CN2EN 中文 » 英语 # ZH_CN2JA 中文 » 日语 @@ -53,14 +52,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if not img or not msg: await w2b_img.finish(f"格式错误:\n" + __plugin_usage__) img = img[0] - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(img, proxy=get_local_proxy()) as response: - async with aiofiles.open( - IMAGE_PATH + f"temp/{event.user_id}_w2b.png", "wb" - ) as f: - await f.write(await response.read()) + if not await AsyncHttpx.download_file( + img, Path(TEMP_PATH) / f"{event.user_id}_w2b.png" + ): + await w2b_img.finish("下载图片失败...请稍后再试...") msg = await get_translate(msg) - w2b = CreateImg(0, 0, background=IMAGE_PATH + f"temp/{event.user_id}_w2b.png") + w2b = CreateImg(0, 0, background=Path(TEMP_PATH) / f"{event.user_id}_w2b.png") w2b.convert("L") msg_sp = msg.split("<|>") w, h = w2b.size @@ -93,17 +90,17 @@ def centered_text(img: CreateImg, text: str, add_h: int): text_sp = text.split("<|>") w, h = img.getsize(text_sp[0]) if len(text_sp) == 1: - w = (img.w - w) / 2 - h = top_h + (bottom_h - top_h - h) / 2 + w = int((img.w - w) / 2) + h = int(top_h + (bottom_h - top_h - h) / 2) img.text((w, h), text_sp[0], (255, 255, 255)) else: - br_h = top_h + (bottom_h - top_h) / 2 - w = (img.w - w) / 2 - h = top_h + (br_h - top_h - h) / 2 + br_h = int(top_h + (bottom_h - top_h) / 2) + w = int((img.w - w) / 2) + h = int(top_h + (br_h - top_h - h) / 2) img.text((w, h), text_sp[0], (255, 255, 255)) w, h = img.getsize(text_sp[1]) - w = (img.w - w) / 2 - h = br_h + (bottom_h - br_h - h) / 2 + w = int((img.w - w) / 2) + h = int(br_h + (bottom_h - br_h - h) / 2) img.text((w, h), text_sp[1], (255, 255, 255)) @@ -119,15 +116,10 @@ async def get_translate(msg: str) -> str: "action": "FY_BY_CLICKBUTTON", "typoResult": "true", } - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - try: - async with session.post(url, data=data, proxy=get_local_proxy()) as res: - data = await res.json() - if data["errorCode"] == 0: - translate = data["translateResult"][0][0]["tgt"] - msg += "<|>" + translate - except Exception as e: - logger.warning(f"黑白草图翻译出错 e:{e}") + data = (await AsyncHttpx.post(url, data=data)).json() + if data["errorCode"] == 0: + translate = data["translateResult"][0][0]["tgt"] + msg += "<|>" + translate return msg diff --git a/plugins/withdraw.py b/plugins/withdraw.py old mode 100644 new mode 100755 index 534f9adb..42319f59 --- a/plugins/withdraw.py +++ b/plugins/withdraw.py @@ -1,30 +1,30 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from nonebot.typing import T_State -import re - - -__zx_plugin_name__ = "消息撤回 [Admin]" -__plugin_usage__ = """ -usage: - 简易的消息撤回机制 - 指令: - [回复]撤回 -""".strip() -__plugin_des__ = "消息撤回机制" -__plugin_cmd__ = ["[回复]撤回"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "admin_level": 0, -} - - -withdraw_msg = on_command("撤回", priority=5, block=True) - - -@withdraw_msg.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - r = re.search(r"\[CQ:reply,id=(-?\d*)]", event.raw_message) - if r: - await bot.delete_msg(message_id=int(r.group(1)), self_id=int(bot.self_id)) +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +import re + + +__zx_plugin_name__ = "消息撤回 [Admin]" +__plugin_usage__ = """ +usage: + 简易的消息撤回机制 + 指令: + [回复]撤回 +""".strip() +__plugin_des__ = "消息撤回机制" +__plugin_cmd__ = ["[回复]撤回"] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "admin_level": 0, +} + + +withdraw_msg = on_command("撤回", priority=5, block=True) + + +@withdraw_msg.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + r = re.search(r"\[CQ:reply,id=(-?\d*)]", event.raw_message) + if r: + await bot.delete_msg(message_id=int(r.group(1)), self_id=int(bot.self_id)) diff --git a/plugins/word_bank/__init__.py b/plugins/word_bank/__init__.py new file mode 100644 index 00000000..38fd6cdc --- /dev/null +++ b/plugins/word_bank/__init__.py @@ -0,0 +1,16 @@ +from configs.config import Config +import nonebot + + +Config.add_plugin_config( + "word_bank", + "WORD_BANK_LEVEL [LEVEL]", + 5, + name="词库问答", + help_="设置增删词库的权限等级", + default_value=5 +) + + +nonebot.load_plugins("plugins/word_bank") + diff --git a/plugins/word_bank/data_source.py b/plugins/word_bank/data_source.py new file mode 100644 index 00000000..fd9bcbc5 --- /dev/null +++ b/plugins/word_bank/data_source.py @@ -0,0 +1,52 @@ +from .model import WordBank +from typing import Union + + +class WordBankBuilder: + + def __init__(self, user_id: int, group_id: int, problem: str): + + self._data = { + "user_id": user_id, + "group_id": group_id, + "problem": problem + } + + def set_placeholder(self, id_: int, placeholder: Union[str, int]): + """ + 设置占位符 + :param id_: 站位id + :param placeholder: 占位符内容 + """ + if self._data.get("placeholder") is None: + self._data["placeholder"] = [] + self._data["placeholder"].append((id_, placeholder)) + + def set_answer(self, answer: str): + """ + 设置回答 + :param answer: 回答 + """ + self._data["answer"] = answer + + async def save(self): + user_id = self._data["user_id"] + group_id = self._data["group_id"] + problem = self._data["problem"] + answer = self._data["answer"] + placeholder = self._data.get("placeholder") + await WordBank.add_problem_answer(user_id, group_id, problem, answer, placeholder) + + def __str__(self): + return str(self._data) + + + + + + + + + + + diff --git a/plugins/word_bank/message_handle.py b/plugins/word_bank/message_handle.py new file mode 100644 index 00000000..117e461e --- /dev/null +++ b/plugins/word_bank/message_handle.py @@ -0,0 +1,44 @@ +from utils.utils import get_message_text +from utils.message_builder import image, at +from .rule import check +from .model import WordBank +from configs.path_config import DATA_PATH +from pathlib import Path +from nonebot.adapters.cqhttp import ( + Bot, + GroupMessageEvent, +) +from nonebot.typing import T_State +from nonebot import on_message + + +__zx_plugin_name__ = "词库问答回复操作 [Hidden]" + + +data_dir = Path(DATA_PATH) / "word_bank" +data_dir.mkdir(parents=True, exist_ok=True) + + +message_handle = on_message(priority=7, block=True, rule=check) + + +@message_handle.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + path = data_dir / f"{event.group_id}" + q = await WordBank.check(event.group_id, get_message_text(event.json())) + placeholder_list = [ + (x.split("<_s>")[0], x.split("<_s>")[1]) + for x in q.format.split("")[:-1] + ] if q.format else [] + answer = "" + _a = q.answer + if not placeholder_list: + answer = _a + else: + for idx, placeholder in placeholder_list: + if placeholder.endswith("jpg"): + answer += _a[:_a.find(f"[__placeholder_{idx}]")] + image(path / placeholder) + else: + answer += _a[:_a.find(f"[__placeholder_{idx}]")] + at(placeholder) + _a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):] + await message_handle.send(answer) diff --git a/plugins/word_bank/model.py b/plugins/word_bank/model.py new file mode 100644 index 00000000..b4d2e265 --- /dev/null +++ b/plugins/word_bank/model.py @@ -0,0 +1,189 @@ +from services.db_context import db +from typing import Optional, List, Union, Tuple +from datetime import datetime +from pathlib import Path +from configs.path_config import DATA_PATH +import random + + +class WordBank(db.Model): + __tablename__ = "word_bank" + + user_qq = db.Column(db.BigInteger(), nullable=False) + group_id = db.Column(db.Integer()) + search_type = db.Column(db.Integer(), nullable=False, default=0) + problem = db.Column(db.String(), nullable=False) + answer = db.Column(db.String(), nullable=False) + format = db.Column(db.String()) + create_time = db.Column(db.DateTime(), nullable=False) + update_time = db.Column(db.DateTime(), nullable=False) + + @classmethod + async def add_problem_answer( + cls, + user_id: int, + group_id: Optional[int], + problem: str, + answer: str, + format_: Optional[List[Tuple[int, Union[int, str]]]], + ) -> bool: + """ + 添加或新增一个问答 + :param user_id: 用户id + :param group_id: 群号 + :param problem: 问题 + :param answer: 回答 + :param format_: 格式化数据 + """ + _str = None + if format_: + _str = "" + for x, y in format_: + _str += f"{x}<_s>{y}" + return await cls._problem_answer_handle( + user_id, group_id, problem, "add", answer=answer, format_=_str + ) + + @classmethod + async def delete_problem_answer( + cls, user_id: int, group_id: Optional[int], problem: str, index: Optional[int] + ) -> str: + """ + 删除某问题一个或全部回答 + :param user_id: 用户id + :param group_id: 群号 + :param problem: 问题 + :param index: 回答下标 + """ + return await cls._problem_answer_handle( + user_id, group_id, problem, "delete", index=index + ) + + @classmethod + async def get_problem_answer( + cls, user_id: int, group_id: Optional[int], problem: str + ) -> List[str]: + """ + 获取问题的所有回答 + :param user_id: 用户id + :param group_id: 群号 + :param problem: 问题 + """ + return await cls._problem_answer_handle(user_id, group_id, problem, "get") + + @classmethod + async def get_group_all_answer(cls, group_id: int, problem: str) -> List[str]: + """ + 获取群聊指定词条所有回答 + :param group_id: 群号 + :param problem: 问题 + """ + if problem.startswith("id:"): + problem_index = int(problem.split(":")[-1]) + q = await cls.get_group_all_problem(group_id) + if len(q) > problem_index: + problem = q[problem_index] + q = await cls.query.where( + (cls.group_id == group_id) & (cls.problem == problem) + ).gino.all() + return [x.answer for x in q] if q else None + + @classmethod + async def get_group_all_problem(cls, group_id: int) -> List[str]: + """ + 获取群聊所有词条 + :param group_id: 群号 + """ + q = await cls.query.where(cls.group_id == group_id).gino.all() + q = [x.problem for x in q] + q.sort() + return list(set(q)) + + @classmethod + async def check(cls, group_id: int, problem: str) -> Optional["WordBank"]: + """ + 检测词条并随机返回 + :param group_id: 群号 + :param problem: 问题 + """ + q = await cls.query.where( + (cls.group_id == group_id) & (cls.problem == problem) + ).gino.all() + return random.choice(q) if q else None + + @classmethod + async def _problem_answer_handle( + cls, + user_id: int, + group_id: Optional[int], + problem: str, + type_: str, + *, + answer: Optional[str] = None, + index: Optional[int] = None, + format_: Optional[str] = None, + ) -> Union[List[Union[str, Tuple[str, str]]], bool, str]: + """ + 添加或新增一个问答 + :param user_id: 用户id + :param group_id: 群号 + :param problem: 问题 + :param type_: 操作类型 + :param answer: 回答 + :param format_: 格式化数据 + """ + if problem.startswith("id:"): + problem_index = int(problem.split(":")[-1]) + q = await cls.get_group_all_problem(group_id) + if not q: + return [] + if len(q) > problem_index: + problem = q[problem_index] + if group_id: + q = cls.query.where((cls.group_id == group_id) & (cls.problem == problem)) + else: + q = cls.query.where((cls.user_qq == user_id) & (cls.problem == problem)) + if type_ == "add": + q = await q.where(cls.answer == answer).gino.all() + if not q or ".jpg" in format_: + await cls.create( + user_qq=user_id, + group_id=group_id, + problem=problem, + answer=answer, + format=format_, + create_time=datetime.now().date(), + update_time=datetime.now().date(), + ) + return True + elif type_ == "delete": + q = await q.with_for_update().gino.all() + if q: + path = Path(DATA_PATH) / "word_bank" / f"{group_id}" + if index is not None: + _q = [x.problem for x in q] + _q.sort() + prob = _q[index] + index = [x.problem for x in q].index(prob) + q = [q[index]] + answer = "\n".join([x.answer for x in q]) + for x in q: + format_ = x.format + if format_: + for sp in format_.split("")[:-1]: + _, image_name = sp.split("<_s>") + if image_name.endswith("jpg"): + _path = path / image_name + if _path.exists(): + _path.unlink() + await cls.delete.where( + (cls.problem == problem) + & (cls.answer == x.answer) + & (cls.group_id == group_id) + ).gino.status() + return answer + elif type_ == "get": + q = await q.gino.all() + if q: + return [(x.answer, x.format.split("")[:-1]) for x in q] + return False diff --git a/plugins/word_bank/rule.py b/plugins/word_bank/rule.py new file mode 100644 index 00000000..b93a9e89 --- /dev/null +++ b/plugins/word_bank/rule.py @@ -0,0 +1,12 @@ +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, Event +from utils.utils import get_message_text +from nonebot.typing import T_State +from .model import WordBank + + +async def check(bot: Bot, event: Event, state: T_State) -> bool: + if isinstance(event, GroupMessageEvent): + return bool( + await WordBank.check(event.group_id, get_message_text(event.json())) + ) + return False diff --git a/plugins/word_bank/word_hanlde.py b/plugins/word_bank/word_hanlde.py new file mode 100644 index 00000000..2ef837b2 --- /dev/null +++ b/plugins/word_bank/word_hanlde.py @@ -0,0 +1,155 @@ +from utils.utils import get_message_at, is_number, get_message_imgs, get_message_text +from services.log import logger +from configs.path_config import DATA_PATH +from utils.http_utils import AsyncHttpx +from .data_source import WordBankBuilder +from configs.config import Config +from .model import WordBank +from nonebot.adapters.cqhttp import ( + Bot, + GroupMessageEvent, +) +from pathlib import Path +from nonebot.typing import T_State +from nonebot import on_command +import random +import re + +__zx_plugin_name__ = "词库问答 [Admin]" +__plugin_usage__ = """ +usage: + 对指定问题的随机回答,对相同问题可以设置多个不同回答 + 删除词条后每个词条的id可能会变化,请查看后再删除 + 指令: + 添加词条问...答...:添加问答词条,可重复添加相同问题的不同回答 + 删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答 + 查看词条 ?[问题/下标]:查看全部词条或对应词条回答 + 示例:添加词条问谁是萝莉答是我 + 示例:删除词条 谁是萝莉 + 示例:删除词条 谁是萝莉 0 + 示例:删除词条 id:0 + 示例:查看词条 + 示例:查看词条 谁是萝莉 + 示例:查看词条 id:0 +""".strip() +__plugin_des__ = "自定义词条内容随机回复" +__plugin_cmd__ = [ + "添加词条问...答..", + "删除词条 [问题/下标] ?[下标]", + "查看词条 ?[问题/下标]", +] +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL"), + "cmd": ["词库问答", "添加词条", "删除词条", "查看词条"] +} + +data_dir = Path(DATA_PATH) / "word_bank" +data_dir.mkdir(parents=True, exist_ok=True) + + +add_word = on_command("添加词条", priority=5, block=True) + +delete_word = on_command("删除词条", priority=5, block=True) + +show_word = on_command("显示词条", aliases={"查看词条"}, priority=5, block=True) + + +@add_word.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + msg = str(event.get_message()).strip() + r = re.search(r"^问(.+)\s?答(.*)", msg) + if not r: + await add_word.finish("未检测到词条问题...") + problem = r.group(1).strip() + answer = r.group(2).strip() + if not answer: + await add_word.finish("未检测到词条回答...") + idx = 0 + _builder = WordBankBuilder(event.user_id, event.group_id, problem) + for at_ in get_message_at(event.json()): + r = re.search(rf"\[CQ:at,qq={at_}]", answer) + if r: + answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1) + _builder.set_placeholder(idx, at_) + idx += 1 + for img in get_message_imgs(event.json()): + _x = img.split("?")[0] + r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer) + if r: + rand = random.randint(1, 10000) + random.randint(1, 14514) + for i in range(3): + answer = answer.replace(f",subType={i}", "") + answer = answer.replace( + rf"[CQ:image,file={r.group(1)},url={img}]", f"[__placeholder_{idx}]", + ) + await AsyncHttpx.download_file( + img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg" + ) + _builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg") + idx += 1 + _builder.set_answer(answer) + await _builder.save() + logger.info(f"已保存词条 问:{problem} 答:{msg}") + await add_word.send(f"已保存词条:{problem}") + + +@delete_word.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看") + index = None + _sp_msg = msg.split() + if len(_sp_msg) > 1: + if is_number(_sp_msg[-1]): + index = int(_sp_msg[-1]) + msg = " ".join(_sp_msg[:-1]) + problem = msg + if problem.startswith("id:"): + x = problem.split(":")[-1] + if not is_number(x) or int(x) < 0: + await delete_word.finish("id必须为数字且符合规范!") + p = (await WordBank.get_group_all_problem(event.group_id)) + if p: + problem = p[int(x)] + try: + if answer := await WordBank.delete_problem_answer(event.user_id, event.group_id, problem, index): + await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}") + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 删除词条: {problem}" + ) + else: + await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在") + except IndexError: + await delete_word.send("指定下标错误...请通过查看词条来确定..") + + +@show_word.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + _problem_list = await WordBank.get_group_all_problem(event.group_id) + if not _problem_list: + await show_word.finish("该群未收录任何词条..") + _problem_list = [f"{i}. {x}" for i, x in enumerate(_problem_list)] + await show_word.send( + "该群已收录的词条:\n" + "\n".join(_problem_list) + ) + else: + _answer_list = await WordBank.get_group_all_answer(event.group_id, msg) + if not _answer_list: + await show_word.send( + "未收录该词条..." + ) + else: + _answer_list = [f"{i}. {x}" for i, x in enumerate(_answer_list)] + await show_word.send( + f"词条 {msg} 回答:\n" + "\n".join(_answer_list) + ) + + + diff --git a/plugins/yiqing/__init__.py b/plugins/yiqing/__init__.py old mode 100644 new mode 100755 diff --git a/plugins/yiqing/data_source.py b/plugins/yiqing/data_source.py old mode 100644 new mode 100755 index f56c638e..02a7381f --- a/plugins/yiqing/data_source.py +++ b/plugins/yiqing/data_source.py @@ -1,9 +1,8 @@ -from utils.user_agent import get_user_agent from configs.path_config import TEXT_PATH from typing import List from pathlib import Path +from utils.http_utils import AsyncHttpx import ujson as json -import aiohttp china_city = Path(TEXT_PATH) / "china_city.json" @@ -14,6 +13,10 @@ url = "https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5" async def get_yiqing_data(area: str): + """ + 查看疫情数据 + :param area: 省份/城市 + """ global data province = None city = None @@ -32,31 +35,29 @@ async def get_yiqing_data(area: str): if area in data[p]: province = p city = area - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, timeout=7) as response: - epidemic_data = json.loads((await response.json())["data"]) - last_update_time = epidemic_data["lastUpdateTime"] - if area == "中国": - data_ = epidemic_data["areaTree"][0] - else: - data_ = [ - x - for x in epidemic_data["areaTree"][0]["children"] - if x["name"] == province - ][0] - if city: - try: - data_ = [x for x in data_["children"] if x["name"] == city][0] - except IndexError: - return "未查询到..." - confirm = data_["total"]["confirm"] # 累计确诊 - heal = data_["total"]["heal"] # 累计治愈 - dead = data_["total"]["dead"] # 累计死亡 - dead_rate = data_["total"]["deadRate"] # 死亡率 - heal_rate = data_["total"]["healRate"] # 治愈率 - now_confirm = data_["total"]["nowConfirm"] # 目前确诊 - suspect = data_["total"]["suspect"] # 疑似 - add_confirm = data_["today"]["confirm"] # 新增确诊 + epidemic_data = json.loads((await AsyncHttpx.get(url)).json()["data"]) + last_update_time = epidemic_data["lastUpdateTime"] + if area == "中国": + data_ = epidemic_data["areaTree"][0] + else: + data_ = [ + x + for x in epidemic_data["areaTree"][0]["children"] + if x["name"] == province + ][0] + if city: + try: + data_ = [x for x in data_["children"] if x["name"] == city][0] + except IndexError: + return "未查询到..." + confirm = data_["total"]["confirm"] # 累计确诊 + heal = data_["total"]["heal"] # 累计治愈 + dead = data_["total"]["dead"] # 累计死亡 + dead_rate = data_["total"]["deadRate"] # 死亡率 + heal_rate = data_["total"]["healRate"] # 治愈率 + now_confirm = data_["total"]["nowConfirm"] # 目前确诊 + suspect = data_["total"]["suspect"] # 疑似 + add_confirm = data_["today"]["confirm"] # 新增确诊 x = f"{city}市" if city else f"{province}{province_type}" return ( f"{x} 疫情数据:\n" @@ -75,6 +76,9 @@ async def get_yiqing_data(area: str): def get_city_and_province_list() -> List[str]: + """ + 获取城市省份列表 + """ global data if not data: try: diff --git a/update_info.json b/update_info.json index 394dbad2..74df5904 100644 --- a/update_info.json +++ b/update_info.json @@ -1,12 +1,10 @@ { "update_file": [ "plugins", - "configs", "models", - "services", + "basic_plugins", "utils", - "bot.py" ], - "add_file": ["basic_plugins", "resources/img/background/check"], + "add_file": [], "delete_file": [] } diff --git a/utils/__init__.py b/utils/__init__.py old mode 100644 new mode 100755 diff --git a/utils/browser.py b/utils/browser.py old mode 100644 new mode 100755 index b08f728b..f5563ce6 --- a/utils/browser.py +++ b/utils/browser.py @@ -1,41 +1,41 @@ -from typing import Optional -from nonebot.log import logger -from playwright.async_api import Browser, async_playwright -import nonebot -from nonebot import Driver -from services.log import logger -import platform - - -driver: Driver = nonebot.get_driver() - - -_browser: Optional[Browser] = None - - -async def init(**kwargs) -> Optional[Browser]: - if platform.system() == "Windows": - return None - try: - global _browser - browser = await async_playwright().start() - _browser = await browser.chromium.launch(**kwargs) - return _browser - except NotImplementedError: - logger.warning("win环境下 初始化playwright失败....请替换环境至linux") - return None - - -async def get_browser(**kwargs) -> Browser: - return _browser or await init(**kwargs) - - -@driver.on_startup -def install(): - """自动安装、更新 Chromium""" - logger.info("正在检查 Chromium 更新") - import sys - from playwright.__main__ import main - - sys.argv = ["", "install", "chromium"] - main() +from typing import Optional +from nonebot.log import logger +from playwright.async_api import Browser, async_playwright +import nonebot +from nonebot import Driver +from services.log import logger +import platform + + +driver: Driver = nonebot.get_driver() + + +_browser: Optional[Browser] = None + + +async def init(**kwargs) -> Optional[Browser]: + if platform.system() == "Windows": + return None + try: + global _browser + browser = await async_playwright().start() + _browser = await browser.chromium.launch(**kwargs) + return _browser + except NotImplementedError: + logger.warning("win环境下 初始化playwright失败....请替换环境至linux") + return None + + +async def get_browser(**kwargs) -> Browser: + return _browser or await init(**kwargs) + + +@driver.on_startup +def install(): + """自动安装、更新 Chromium""" + logger.info("正在检查 Chromium 更新") + import sys + from playwright.__main__ import main + + sys.argv = ["", "install", "chromium"] + main() diff --git a/utils/data_utils.py b/utils/data_utils.py old mode 100644 new mode 100755 diff --git a/utils/http_utils.py b/utils/http_utils.py new file mode 100644 index 00000000..9140d743 --- /dev/null +++ b/utils/http_utils.py @@ -0,0 +1,325 @@ +from typing import Dict, Union, Optional, List, Any, Literal +from utils.user_agent import get_user_agent +from .utils import get_local_proxy +from services.log import logger +from pathlib import Path +from httpx import Response +from asyncio.exceptions import TimeoutError +from nonebot.adapters.cqhttp import MessageSegment +from playwright.async_api import Page +from .message_builder import image +from .browser import get_browser +import asyncio +import aiofiles +import httpx + + +class AsyncHttpx: + + proxy = {"http://": get_local_proxy()} + + @classmethod + async def get( + cls, + url: str, + *, + params: Optional[Dict[str, Any]] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Optional[Dict[str, str]] = None, + use_proxy: bool = True, + proxy: Dict[str, str] = None, + allow_redirects: bool = True, + timeout: Optional[int] = 30, + ) -> Response: + """ + Get + :param url: url + :param params: params + :param headers: 请求头 + :param cookies: cookies + :param use_proxy: 使用默认代理 + :param proxy: 指定代理 + :param allow_redirects: allow_redirects + :param timeout: 超时时间 + """ + if not headers: + headers = get_user_agent() + proxy = proxy if proxy else cls.proxy if use_proxy else None + async with httpx.AsyncClient(proxies=proxy) as client: + return await client.get( + url, + params=params, + headers=headers, + cookies=cookies, + allow_redirects=allow_redirects, + timeout=timeout, + ) + + @classmethod + async def post( + cls, + url: str, + *, + data: Optional[Dict[str, str]] = None, + content: Any = None, + files: Any = None, + use_proxy: bool = True, + proxy: Dict[str, str] = None, + json: Optional[Dict[str, Union[Any]]] = None, + params: Optional[Dict[str, str]] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Optional[Dict[str, str]] = None, + allow_redirects: bool = True, + timeout: Optional[int] = 30, + ) -> Response: + """ + Post + :param url: url + :param data: data + :param content: content + :param files: files + :param use_proxy: 是否默认代理 + :param proxy: 指定代理 + :param json: json + :param params: params + :param headers: 请求头 + :param cookies: cookies + :param allow_redirects: allow_redirects + :param timeout: 超时时间 + """ + if not headers: + headers = get_user_agent() + proxy = proxy if proxy else cls.proxy if use_proxy else None + async with httpx.AsyncClient(proxies=proxy) as client: + return await client.post( + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + allow_redirects=allow_redirects, + timeout=timeout, + ) + + @classmethod + async def download_file( + cls, + url: str, + path: Union[str, Path], + *, + params: Optional[Dict[str, str]] = None, + use_proxy: bool = True, + proxy: Dict[str, str] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Optional[Dict[str, str]] = None, + timeout: Optional[int] = 30, + ) -> bool: + """ + 下载文件 + :param url: url + :param path: 存储路径 + :param params: params + :param use_proxy: 使用代理 + :param proxy: 指定代理 + :param headers: 请求头 + :param cookies: cookies + :param timeout: 超时时间 + """ + if isinstance(path, str): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + try: + for _ in range(3): + try: + content = ( + await cls.get( + url, + params=params, + headers=headers, + cookies=cookies, + use_proxy=use_proxy, + proxy=proxy, + timeout=timeout, + ) + ).content + async with aiofiles.open(path, "wb") as wf: + await wf.write(content) + logger.info(f"下载图片 {url} 成功.. Path:{path.absolute()}") + return True + except TimeoutError: + pass + else: + logger.error(f"下载图片 {url} 下载超时.. Path:{path.absolute()}") + except Exception as e: + logger.error(f"下载图片 {url} 未知错误 {type(e)}:{e}.. Path:{path.absolute()}") + return False + + @classmethod + async def gather_download_file( + cls, + url_list: List[str], + path_list: List[Union[str, Path]], + *, + limit_async_number: Optional[int] = None, + params: Optional[Dict[str, str]] = None, + use_proxy: bool = True, + proxy: Dict[str, str] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Optional[Dict[str, str]] = None, + timeout: Optional[int] = 30, + ) -> List[bool]: + """ + 分组同时下载文件 + :param url_list: url列表 + :param path_list: 存储路径列表 + :param limit_async_number: 限制同时请求数量 + :param params: params + :param use_proxy: 使用代理 + :param proxy: 指定代理 + :param headers: 请求头 + :param cookies: cookies + :param timeout: 超时时间 + :return: + """ + if n := len(url_list) != len(path_list): + raise UrlPathNumberNotEqual( + f"Url数量与Path数量不对等,Url:{len(url_list)},Path:{len(path_list)}" + ) + if limit_async_number and n > limit_async_number: + m = float(n) / limit_async_number + x = 0 + j = limit_async_number + _split_url_list = [] + _split_path_list = [] + for _ in range(int(m)): + _split_url_list.append(url_list[x:j]) + _split_path_list.append(path_list[x:j]) + x += limit_async_number + j += limit_async_number + if int(m) < m: + _split_url_list.append(url_list[j:]) + _split_path_list.append(path_list[j:]) + else: + _split_url_list = [url_list] + _split_path_list = [path_list] + tasks = [] + result_ = [] + for x, y in zip(_split_url_list, _split_path_list): + for url, path in zip(x, y): + tasks.append( + asyncio.create_task( + cls.download_file( + url, + path, + params=params, + headers=headers, + cookies=cookies, + use_proxy=use_proxy, + timeout=timeout, + proxy=proxy + ) + ) + ) + _x = await asyncio.gather(*tasks) + result_ = result_ + list(_x) + tasks.clear() + return result_ + + +class AsyncPlaywright: + + @classmethod + async def _new_page(cls, user_agent: Optional[str] = None) -> Page: + """ + 获取一个新页面 + :param user_agent: 请求头 + """ + browser = await get_browser() + if browser: + return await browser.new_page(user_agent=user_agent) + raise BrowserIsNone("获取Browser失败...") + + @classmethod + async def goto( + cls, + url: str, + *, + timeout: Optional[float] = 100000, + wait_until: Optional[ + Literal["domcontentloaded", "load", "networkidle"] + ] = "networkidle", + referer: str = None, + ) -> Optional[Page]: + """ + goto + :param url: 网址 + :param timeout: 超时限制 + :param wait_until: 等待类型 + :param referer: + """ + page = None + try: + page = await cls._new_page() + await page.goto(url, timeout=timeout, wait_until=wait_until, referer=referer) + return page + except Exception as e: + logger.warning(f"Playwright 访问 url:{url} 发生错误 {type(e)}:{e}") + if page: + await page.close() + return None + + @classmethod + async def screenshot( + cls, + url: str, + path: Union[Path, str], + element: str, + *, + sleep: Optional[int] = None, + viewport_size: Dict[str, int] = None, + wait_until: Optional[ + Literal["domcontentloaded", "load", "networkidle"] + ] = "networkidle", + timeout: float = None, + type_: Literal["jpeg", "png"] = None, + ) -> Optional[MessageSegment]: + """ + 截图,该方法仅用于简单快捷截图,复杂截图请操作 page + :param url: 网址 + :param path: 存储路径 + :param element: 元素选择 + :param sleep: 延迟截取 + :param viewport_size: 窗口大小 + :param wait_until: 等待类型 + :param timeout: 超时限制 + :param type_: 保存类型 + """ + page = None + if viewport_size is None: + viewport_size = dict(width=2560, height=1080) + if isinstance(path, str): + path = Path(path) + try: + page = await cls.goto(url, wait_until=wait_until) + await page.set_viewport_size(viewport_size) + if sleep: + await asyncio.sleep(sleep) + card = await page.query_selector(element) + await card.screenshot(path=path, timeout=timeout, type=type_) + return image(path) + except Exception as e: + logger.warning(f"Playwright 截图 url:{url} element:{element} 发生错误 {type(e)}:{e}") + if page: + await page.close() + return None + + +class UrlPathNumberNotEqual(Exception): + pass + + +class BrowserIsNone(Exception): + pass diff --git a/utils/image_utils.py b/utils/image_utils.py old mode 100644 new mode 100755 index 6db72d32..50096e9f --- a/utils/image_utils.py +++ b/utils/image_utils.py @@ -3,7 +3,7 @@ from PIL import Image, ImageFile, ImageDraw, ImageFont, ImageFilter from imagehash import ImageHash from io import BytesIO from matplotlib import pyplot as plt -from typing import Tuple, Optional, Union, List +from typing import Tuple, Optional, Union, List, Literal from pathlib import Path from math import ceil import random @@ -38,7 +38,7 @@ def compare_image_with_hash( return False -def get_img_hash(image_file: str) -> ImageHash: +def get_img_hash(image_file: Union[str, Path]) -> ImageHash: """ 说明: 获取图片的hash值 @@ -225,7 +225,7 @@ class CreateImg: img: "CreateImg" or Image, pos: Tuple[int, int] = None, alpha: bool = False, - center_type: Optional[str] = None, + center_type: Optional[Literal["center", "by_height", "by_width"]] = None, ): """ 说明: @@ -313,7 +313,7 @@ class CreateImg: pos: Tuple[int, int], text: str, fill: Tuple[int, int, int] = (0, 0, 0), - center_type: Optional[str] = None, + center_type: Optional[Literal["center", "by_height", "by_width"]] = None, ): """ 说明: @@ -867,7 +867,10 @@ class CreateMat: font_h = self.markImg.getsize(str(y[i]))[1] self.markImg.text( ( - self.padding_w + int(y[i] * self._p * self._deviation) + 2 + 5, + self.padding_w + + int(y[i] * self._p * self._deviation) + + 2 + + 5, current_h - int(font_h / 2) - 1, ), f"{y[i]:.2f}" if isinstance(y[i], float) else f"{y[i]}", @@ -937,7 +940,9 @@ class CreateMat: padding_h = self.padding_h line_length = self.line_length background = random.choice(self.background) if self.background else None - A = CreateImg(self.w, self.h, font_size=font_size, font=self.font, background=background) + A = CreateImg( + self.w, self.h, font_size=font_size, font=self.font, background=background + ) if background: _tmp = CreateImg(self.w, self.h) _tmp.transparent(2) @@ -950,7 +955,7 @@ class CreateMat: color=(255, 255, 255, 0), font_size=35, font_color=self._color.get("title"), - font=self.font + font=self.font, ) A.paste(title, (0, 25), True, "by_width") A.line( @@ -993,7 +998,7 @@ class CreateMat: plain_text=f"{_x}", font_size=self.font_size, color=(255, 255, 255, 0), - font=self.font + font=self.font, ) text.rotate(self.x_rotate, True) A.paste(text, (current_w - w, padding_h + line_length + 10), alpha=True) @@ -1013,7 +1018,7 @@ class CreateMat: plain_text=f"{_y}", font_size=self.font_size, color=(255, 255, 255, 0), - font=self.font + font=self.font, ) idx = 0 while text.size[0] > self.padding_w - 10 and idx < 3: @@ -1023,7 +1028,7 @@ class CreateMat: plain_text=f"{_y}", font_size=int(self.font_size * 0.75), color=(255, 255, 255, 0), - font=self.font + font=self.font, ) w, _ = text.getsize(f"{_y}") idx += 1 @@ -1033,8 +1038,10 @@ class CreateMat: A.text((int(padding_w / 2), int(padding_w / 2)), x_name) if y_name: A.text( - (int(padding_w + line_length + 50 - A.getsize(y_name)[0]), - int(padding_h + line_length + 50 + x_rotate_height)), + ( + int(padding_w + line_length + 50 - A.getsize(y_name)[0]), + int(padding_h + line_length + 50 + x_rotate_height), + ), y_name, ) return A diff --git a/utils/langconv.py b/utils/langconv.py old mode 100644 new mode 100755 diff --git a/utils/manager/__init__.py b/utils/manager/__init__.py old mode 100644 new mode 100755 index ce83637b..15ab6c27 --- a/utils/manager/__init__.py +++ b/utils/manager/__init__.py @@ -1,71 +1,72 @@ -from typing import Optional -from .group_manager import GroupManager -from pathlib import Path -from .data_class import StaticData -from .withdraw_message_manager import WithdrawMessageManager -from .plugins2cd_manager import Plugins2cdManager -from .plugins2block_manager import Plugins2blockManager -from .plugins2count_manager import Plugins2countManager -from .plugins2settings_manager import Plugins2settingsManager -from .plugins_manager import PluginsManager -from .resources_manager import ResourcesManager -from .admin_manager import AdminManager -from .none_plugin_count_manager import NonePluginCountManager -from .requests_manager import RequestManager -from configs.path_config import DATA_PATH -from nonebot import Driver -import nonebot - -driver: Driver = nonebot.get_driver() - -# 群功能开关 | 群被动技能 | 群权限 管理 -group_manager: Optional[GroupManager] = GroupManager( - Path(DATA_PATH) / "manager" / "group_manager.json" -) -# 撤回消息管理 -withdraw_message_manager: Optional[WithdrawMessageManager] = WithdrawMessageManager() - -# 插件管理 -plugins_manager: Optional[PluginsManager] = PluginsManager( - Path(DATA_PATH) / "manager" / "plugins_manager.json" -) - -# 插件基本设置管理 -plugins2settings_manager: Optional[Plugins2settingsManager] = Plugins2settingsManager( - Path(DATA_PATH) / "configs" / "plugins2settings.yaml" -) - -# 插件命令 cd 管理 -plugins2cd_manager: Optional[Plugins2cdManager] = Plugins2cdManager( - Path(DATA_PATH) / "configs" / "plugins2cd.yaml" -) - -# 插件命令 阻塞 管理 -plugins2block_manager: Optional[Plugins2blockManager] = Plugins2blockManager( - Path(DATA_PATH) / "configs" / "plugins2block.yaml" -) - -# 插件命令 每次次数限制 管理 -plugins2count_manager: Optional[Plugins2countManager] = Plugins2countManager( - Path(DATA_PATH) / "configs" / "plugins2count.yaml" -) - -# 资源管理 -resources_manager: Optional[ResourcesManager] = ResourcesManager( - Path(DATA_PATH) / "manager" / "resources_manager.json" -) - -# 插件加载容忍管理 -none_plugin_count_manager: Optional[NonePluginCountManager] = NonePluginCountManager( - Path(DATA_PATH) / "manager" / "none_plugin_count_manager.json" -) - -# 好友请求/群聊邀请 管理 -requests_manager: Optional[RequestManager] = RequestManager( - Path(DATA_PATH) / "manager" / "requests_manager.json" -) - - -# 管理员命令管理器 -admin_manager = AdminManager() - +from typing import Optional +from .group_manager import GroupManager +from pathlib import Path +from .data_class import StaticData +from .withdraw_message_manager import WithdrawMessageManager +from .plugins2cd_manager import Plugins2cdManager +from .plugins2block_manager import Plugins2blockManager +from .plugins2count_manager import Plugins2countManager +from .plugins2settings_manager import Plugins2settingsManager +from .plugins_manager import PluginsManager +from .resources_manager import ResourcesManager +from .admin_manager import AdminManager +from .none_plugin_count_manager import NonePluginCountManager +from .requests_manager import RequestManager +from configs.path_config import DATA_PATH +from nonebot import Driver +import nonebot + +driver: Driver = nonebot.get_driver() + +# 群功能开关 | 群被动技能 | 群权限 管理 +group_manager: Optional[GroupManager] = GroupManager( + Path(DATA_PATH) / "manager" / "group_manager.json" +) + +# 撤回消息管理 +withdraw_message_manager: Optional[WithdrawMessageManager] = WithdrawMessageManager() + +# 插件管理 +plugins_manager: Optional[PluginsManager] = PluginsManager( + Path(DATA_PATH) / "manager" / "plugins_manager.json" +) + +# 插件基本设置管理 +plugins2settings_manager: Optional[Plugins2settingsManager] = Plugins2settingsManager( + Path(DATA_PATH) / "configs" / "plugins2settings.yaml" +) + +# 插件命令 cd 管理 +plugins2cd_manager: Optional[Plugins2cdManager] = Plugins2cdManager( + Path(DATA_PATH) / "configs" / "plugins2cd.yaml" +) + +# 插件命令 阻塞 管理 +plugins2block_manager: Optional[Plugins2blockManager] = Plugins2blockManager( + Path(DATA_PATH) / "configs" / "plugins2block.yaml" +) + +# 插件命令 每次次数限制 管理 +plugins2count_manager: Optional[Plugins2countManager] = Plugins2countManager( + Path(DATA_PATH) / "configs" / "plugins2count.yaml" +) + +# 资源管理 +resources_manager: Optional[ResourcesManager] = ResourcesManager( + Path(DATA_PATH) / "manager" / "resources_manager.json" +) + +# 插件加载容忍管理 +none_plugin_count_manager: Optional[NonePluginCountManager] = NonePluginCountManager( + Path(DATA_PATH) / "manager" / "none_plugin_count_manager.json" +) + +# 好友请求/群聊邀请 管理 +requests_manager: Optional[RequestManager] = RequestManager( + Path(DATA_PATH) / "manager" / "requests_manager.json" +) + + +# 管理员命令管理器 +admin_manager = AdminManager() + diff --git a/utils/manager/admin_manager.py b/utils/manager/admin_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/configs_manager.py b/utils/manager/configs_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/data_class.py b/utils/manager/data_class.py old mode 100644 new mode 100755 index 6507be8f..8e78d913 --- a/utils/manager/data_class.py +++ b/utils/manager/data_class.py @@ -1,77 +1,76 @@ -from typing import Union, Optional -from pathlib import Path -from ruamel.yaml import YAML -import ujson as json - -yaml = YAML(typ="safe") - - -class StaticData: - """ - 静态数据共享类 - """ - - def __init__(self, file: Optional[Path]): - self._data: dict = {} - if file: - file.parent.mkdir(exist_ok=True, parents=True) - self.file = file - if file.exists(): - with open(file, "r", encoding="utf8") as f: - if file.name.endswith("json"): - self._data: dict = json.load(f) - elif file.name.endswith("yaml"): - self._data = yaml.load(f) - - def set(self, key, value): - self._data[key] = value - self.save() - - def set_module_data(self, module, key, value): - if module in self._data.keys(): - self._data[module][key] = value - self.save() - - def get(self, key): - return self._data.get(key) - - def keys(self): - return self._data.keys() - - def delete(self, key): - if self._data.get(key) is not None: - del self._data[key] - self.save() - - def get_data(self) -> dict: - return self._data - - def save(self, path: Union[str, Path] = None): - path = path if path else self.file - if isinstance(path, str): - path = Path(path) - if path: - with open(path, "w", encoding="utf8") as f: - json.dump(self._data, f, ensure_ascii=False, indent=4) - - def reload(self): - if self.file.exists(): - if self.file.name.endswith("json"): - self._data: dict = json.load(open(self.file, "r", encoding="utf8")) - elif self.file.name.endswith("yaml"): - self._data: dict = yaml.load(open(self.file, "r", encoding="utf8")) - - def is_exists(self): - return self.file.exists() - - def is_empty(self): - return bool(len(self._data)) - - def __str__(self): - return str(self._data) - - def __setitem__(self, key, value): - self._data[key] = value - - def __getitem__(self, key): - return self._data[key] +from typing import Union, Optional +from pathlib import Path +from ruamel.yaml import YAML +import ujson as json + +yaml = YAML(typ="safe") + + +class StaticData: + """ + 静态数据共享类 + """ + + def __init__(self, file: Optional[Path]): + self._data: dict = {} + if file: + file.parent.mkdir(exist_ok=True, parents=True) + self.file = file + if file.exists(): + with open(file, "r", encoding="utf8") as f: + if file.name.endswith("json"): + self._data: dict = json.load(f) + elif file.name.endswith("yaml"): + self._data = yaml.load(f) + + def set(self, key, value): + self._data[key] = value + self.save() + + def set_module_data(self, module, key, value): + if module in self._data.keys(): + self._data[module][key] = value + self.save() + + def get(self, key): + return self._data.get(key) + + def keys(self): + return self._data.keys() + + def delete(self, key): + if self._data.get(key) is not None: + del self._data[key] + + def get_data(self) -> dict: + return self._data + + def save(self, path: Union[str, Path] = None): + path = path if path else self.file + if isinstance(path, str): + path = Path(path) + if path: + with open(path, "w", encoding="utf8") as f: + json.dump(self._data, f, ensure_ascii=False, indent=4) + + def reload(self): + if self.file.exists(): + if self.file.name.endswith("json"): + self._data: dict = json.load(open(self.file, "r", encoding="utf8")) + elif self.file.name.endswith("yaml"): + self._data: dict = yaml.load(open(self.file, "r", encoding="utf8")) + + def is_exists(self): + return self.file.exists() + + def is_empty(self): + return bool(len(self._data)) + + def __str__(self): + return str(self._data) + + def __setitem__(self, key, value): + self._data[key] = value + + def __getitem__(self, key): + return self._data[key] diff --git a/utils/manager/group_manager.py b/utils/manager/group_manager.py old mode 100644 new mode 100755 index a0db6af0..e0104311 --- a/utils/manager/group_manager.py +++ b/utils/manager/group_manager.py @@ -1,295 +1,295 @@ -from typing import Optional, List, Union, Dict -from pathlib import Path -from .data_class import StaticData -from utils.utils import get_matchers, get_bot -import nonebot - - -class GroupManager(StaticData): - """ - 群权限 | 功能 | 聊天时间 管理器 - """ - - def __init__(self, file: Path): - super().__init__(file) - if not self._data: - self._data = { - "super": {"white_group_list": []}, - "group_manager": {}, - } - self._task = {} - - def block_plugin(self, module: str, group_id: int): - """ - 说明: - 锁定插件 - 参数: - :param module: 功能模块名 - :param group_id: 群组,None时为超级用户禁用 - """ - self._set_plugin_status(module, "block", group_id) - - def unblock_plugin(self, module: str, group_id: int): - """ - 说明: - 解锁插件 - 参数: - :param module: 功能模块名 - :param group_id: 群组 - """ - self._set_plugin_status(module, "unblock", group_id) - - def set_group_level(self, group_id: int, level: int): - """ - 说明: - 设置群权限 - 参数: - :param group_id: 群组 - :param level: 权限等级 - """ - group_id = str(group_id) - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - self._data["group_manager"][group_id]["level"] = level - self.save() - - def get_plugin_status(self, module: str, group_id: int) -> bool: - """ - 说明: - 获取插件状态 - 参数: - :param module: 功能模块名 - :param group_id: 群组 - """ - group_id = str(group_id) if group_id else group_id - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - return True - if module in self._data["group_manager"][group_id]["close_plugins"]: - return False - return True - - def get_group_level(self, group_id: int) -> int: - """ - 说明: - 获取群等级 - 参数: - :param group_id: 群号 - """ - group_id = str(group_id) - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - return self._data["group_manager"][group_id]["level"] - - def check_group_is_white(self, group_id: int) -> bool: - """ - 说明: - 检测群聊是否在白名单 - 参数: - :param group_id: 群号 - """ - return group_id in self._data["super"]["white_group_list"] - - def add_group_white_list(self, group_id: int): - """ - 说明: - 将群聊加入白名单 - 参数: - :param group_id: 群号 - """ - if group_id not in self._data["super"]["white_group_list"]: - self._data["super"]["white_group_list"].append(group_id) - - def delete_group_white_list(self, group_id: int): - """ - 说明: - 将群聊从白名单中删除 - 参数: - :param group_id: 群号 - """ - if group_id in self._data["super"]["white_group_list"]: - self._data["super"]["white_group_list"].remove(group_id) - - def get_group_white_list(self) -> List[str]: - """ - 说明: - 获取所有群白名单 - """ - return self._data["super"]["white_group_list"] - - def delete_group(self, group_id: int): - """ - 删除群配置 - :param group_id: 群号 - """ - if group_id in self._data["group_manager"]: - del self._data["group_manager"][str(group_id)] - if group_id in self._data["super"]["white_group_list"]: - self._data["super"]["white_group_list"].remove(group_id) - self.save() - - async def open_group_task(self, group_id: int, task: str): - """ - 开启群被动技能 - :param group_id: 群号 - :param task: 被动技能名称 - """ - await self._set_group_task_status(group_id, task, True) - - async def close_group_task(self, group_id: int, task: str): - """ - 关闭群被动技能 - :param group_id: 群号 - :param task: 被动技能名称 - """ - await self._set_group_task_status(group_id, task, False) - - async def check_group_task_status(self, group_id: int, task: str) -> bool: - """ - 查看群被动技能状态 - :param group_id: 群号 - :param task: 被动技能名称 - """ - group_id = str(group_id) - if ( - not self._data["group_manager"][group_id].get("group_task_status") - or self._data["group_manager"][group_id]["group_task_status"].get(task) - is None - ): - await self.init_group_task(group_id) - return self._data["group_manager"][group_id]["group_task_status"][task] - - def get_task_data(self) -> Dict[str, str]: - """ - 获取所有被动任务 - """ - return self._task - - async def group_task_status(self, group_id: int) -> str: - """ - 查看群被全部动技能状态 - :param group_id: 群号 - """ - x = "[群被动技能]:\n" - group_id = str(group_id) - if not self._data["group_manager"][group_id].get("group_task_status"): - await self.init_group_task(group_id) - for key in self._data["group_manager"][group_id]["group_task_status"].keys(): - x += f'{self._task[key]}:{"√" if await self.check_group_task_status(int(group_id), key) else "×"}\n' - return x[:-1] - - async def _set_group_task_status(self, group_id: int, task: str, status: bool): - """ - 管理群被动技能状态 - :param group_id: 群号 - :param task: 被动技能 - :param status: 状态 - """ - group_id = str(group_id) - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - if ( - not self._data["group_manager"][group_id].get("group_task_status") - or self._data["group_manager"][group_id]["group_task_status"].get(task) - is None - ): - await self.init_group_task(group_id) - self._data["group_manager"][group_id]["group_task_status"][task] = status - self.save() - - async def init_group_task(self, group_id: Optional[Union[int, str]] = None): - """ - 初始化群聊 被动技能 状态 - """ - if not self._task: - for matcher in get_matchers(): - _plugin = nonebot.plugin.get_plugin(matcher.module) - try: - _module = _plugin.module - plugin_task = _module.__getattribute__("__plugin_task__") - for key in plugin_task.keys(): - self._task[key] = plugin_task[key] - except AttributeError: - pass - bot = get_bot() - if bot or group_id: - if group_id: - _group_list = [group_id] - else: - _group_list = [x["group_id"] for x in await bot.get_group_list()] - for group_id in _group_list: - group_id = str(group_id) - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - if not self._data["group_manager"][group_id].get("group_task_status"): - self._data["group_manager"][group_id]["group_task_status"] = {} - for task in self._task: - if ( - self._data["group_manager"][group_id]["group_task_status"].get( - task - ) - is None - ): - self._data["group_manager"][group_id]["group_task_status"][ - task - ] = True - for task in list( - self._data["group_manager"][group_id]["group_task_status"] - ): - if task not in self._task: - del self._data["group_manager"][group_id]["group_task_status"][ - task - ] - self.save() - - def _set_plugin_status( - self, - module: str, - status: str, - group_id: int, - ): - """ - 说明: - 设置功能开关状态 - 参数: - :param module: 功能模块名 - :param status: 功能状态 - :param group_id: 群组 - """ - group_id = str(group_id) if group_id else group_id - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - if status == "block": - if module not in self._data["group_manager"][group_id]["close_plugins"]: - self._data["group_manager"][group_id]["close_plugins"].append(module) - else: - if module in self._data["group_manager"][group_id]["close_plugins"]: - self._data["group_manager"][group_id]["close_plugins"].remove(module) - self.save() - - def _init_group(self, group_id: str): - """ - 说明: - 初始化群数据 - 参数: - :param group_id: 群号 - """ - default_group_level = 5 # Config.get_config("group_manager") - if not default_group_level: - default_group_level = 5 - if not self._data["group_manager"].get(group_id): - self._data["group_manager"][group_id] = { - "level": default_group_level, - "close_plugins": [], - "group_task_status": {}, - } - - def get_super_old_data(self) -> Optional[dict]: - """ - 获取旧数据,平时使用请不要调用 - """ - if self._data["super"].get("close_plugins"): - _x = self._data["super"].get("close_plugins") - del self._data["super"]["close_plugins"] - return _x - return None +from typing import Optional, List, Union, Dict +from pathlib import Path +from .data_class import StaticData +from utils.utils import get_matchers, get_bot +import nonebot + + +class GroupManager(StaticData): + """ + 群权限 | 功能 | 聊天时间 管理器 + """ + + def __init__(self, file: Path): + super().__init__(file) + if not self._data: + self._data = { + "super": {"white_group_list": []}, + "group_manager": {}, + } + self._task = {} + + def block_plugin(self, module: str, group_id: int): + """ + 说明: + 锁定插件 + 参数: + :param module: 功能模块名 + :param group_id: 群组,None时为超级用户禁用 + """ + self._set_plugin_status(module, "block", group_id) + + def unblock_plugin(self, module: str, group_id: int): + """ + 说明: + 解锁插件 + 参数: + :param module: 功能模块名 + :param group_id: 群组 + """ + self._set_plugin_status(module, "unblock", group_id) + + def set_group_level(self, group_id: int, level: int): + """ + 说明: + 设置群权限 + 参数: + :param group_id: 群组 + :param level: 权限等级 + """ + group_id = str(group_id) + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) + self._data["group_manager"][group_id]["level"] = level + self.save() + + def get_plugin_status(self, module: str, group_id: int) -> bool: + """ + 说明: + 获取插件状态 + 参数: + :param module: 功能模块名 + :param group_id: 群组 + """ + group_id = str(group_id) if group_id else group_id + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) + return True + if module in self._data["group_manager"][group_id]["close_plugins"]: + return False + return True + + def get_group_level(self, group_id: int) -> int: + """ + 说明: + 获取群等级 + 参数: + :param group_id: 群号 + """ + group_id = str(group_id) + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) + return self._data["group_manager"][group_id]["level"] + + def check_group_is_white(self, group_id: int) -> bool: + """ + 说明: + 检测群聊是否在白名单 + 参数: + :param group_id: 群号 + """ + return group_id in self._data["super"]["white_group_list"] + + def add_group_white_list(self, group_id: int): + """ + 说明: + 将群聊加入白名单 + 参数: + :param group_id: 群号 + """ + if group_id not in self._data["super"]["white_group_list"]: + self._data["super"]["white_group_list"].append(group_id) + + def delete_group_white_list(self, group_id: int): + """ + 说明: + 将群聊从白名单中删除 + 参数: + :param group_id: 群号 + """ + if group_id in self._data["super"]["white_group_list"]: + self._data["super"]["white_group_list"].remove(group_id) + + def get_group_white_list(self) -> List[str]: + """ + 说明: + 获取所有群白名单 + """ + return self._data["super"]["white_group_list"] + + def delete_group(self, group_id: int): + """ + 删除群配置 + :param group_id: 群号 + """ + if group_id in self._data["group_manager"]: + del self._data["group_manager"][str(group_id)] + if group_id in self._data["super"]["white_group_list"]: + self._data["super"]["white_group_list"].remove(group_id) + self.save() + + async def open_group_task(self, group_id: int, task: str): + """ + 开启群被动技能 + :param group_id: 群号 + :param task: 被动技能名称 + """ + await self._set_group_task_status(group_id, task, True) + + async def close_group_task(self, group_id: int, task: str): + """ + 关闭群被动技能 + :param group_id: 群号 + :param task: 被动技能名称 + """ + await self._set_group_task_status(group_id, task, False) + + async def check_group_task_status(self, group_id: int, task: str) -> bool: + """ + 查看群被动技能状态 + :param group_id: 群号 + :param task: 被动技能名称 + """ + group_id = str(group_id) + if ( + not self._data["group_manager"][group_id].get("group_task_status") + or self._data["group_manager"][group_id]["group_task_status"].get(task) + is None + ): + await self.init_group_task(group_id) + return self._data["group_manager"][group_id]["group_task_status"][task] + + def get_task_data(self) -> Dict[str, str]: + """ + 获取所有被动任务 + """ + return self._task + + async def group_task_status(self, group_id: int) -> str: + """ + 查看群被全部动技能状态 + :param group_id: 群号 + """ + x = "[群被动技能]:\n" + group_id = str(group_id) + if not self._data["group_manager"][group_id].get("group_task_status"): + await self.init_group_task(group_id) + for key in self._data["group_manager"][group_id]["group_task_status"].keys(): + x += f'{self._task[key]}:{"√" if await self.check_group_task_status(int(group_id), key) else "×"}\n' + return x[:-1] + + async def _set_group_task_status(self, group_id: int, task: str, status: bool): + """ + 管理群被动技能状态 + :param group_id: 群号 + :param task: 被动技能 + :param status: 状态 + """ + group_id = str(group_id) + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) + if ( + not self._data["group_manager"][group_id].get("group_task_status") + or self._data["group_manager"][group_id]["group_task_status"].get(task) + is None + ): + await self.init_group_task(group_id) + self._data["group_manager"][group_id]["group_task_status"][task] = status + self.save() + + async def init_group_task(self, group_id: Optional[Union[int, str]] = None): + """ + 初始化群聊 被动技能 状态 + """ + if not self._task: + for matcher in get_matchers(): + _plugin = nonebot.plugin.get_plugin(matcher.module) + try: + _module = _plugin.module + plugin_task = _module.__getattribute__("__plugin_task__") + for key in plugin_task.keys(): + self._task[key] = plugin_task[key] + except AttributeError: + pass + bot = get_bot() + if bot or group_id: + if group_id: + _group_list = [group_id] + else: + _group_list = [x["group_id"] for x in await bot.get_group_list()] + for group_id in _group_list: + group_id = str(group_id) + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) + if not self._data["group_manager"][group_id].get("group_task_status"): + self._data["group_manager"][group_id]["group_task_status"] = {} + for task in self._task: + if ( + self._data["group_manager"][group_id]["group_task_status"].get( + task + ) + is None + ): + self._data["group_manager"][group_id]["group_task_status"][ + task + ] = True + for task in list( + self._data["group_manager"][group_id]["group_task_status"] + ): + if task not in self._task: + del self._data["group_manager"][group_id]["group_task_status"][ + task + ] + self.save() + + def _set_plugin_status( + self, + module: str, + status: str, + group_id: int, + ): + """ + 说明: + 设置功能开关状态 + 参数: + :param module: 功能模块名 + :param status: 功能状态 + :param group_id: 群组 + """ + group_id = str(group_id) if group_id else group_id + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) + if status == "block": + if module not in self._data["group_manager"][group_id]["close_plugins"]: + self._data["group_manager"][group_id]["close_plugins"].append(module) + else: + if module in self._data["group_manager"][group_id]["close_plugins"]: + self._data["group_manager"][group_id]["close_plugins"].remove(module) + self.save() + + def _init_group(self, group_id: str): + """ + 说明: + 初始化群数据 + 参数: + :param group_id: 群号 + """ + default_group_level = 5 # Config.get_config("group_manager") + if not default_group_level: + default_group_level = 5 + if not self._data["group_manager"].get(group_id): + self._data["group_manager"][group_id] = { + "level": default_group_level, + "close_plugins": [], + "group_task_status": {}, + } + + def get_super_old_data(self) -> Optional[dict]: + """ + 获取旧数据,平时使用请不要调用 + """ + if self._data["super"].get("close_plugins"): + _x = self._data["super"].get("close_plugins") + del self._data["super"]["close_plugins"] + return _x + return None diff --git a/utils/manager/none_plugin_count_manager.py b/utils/manager/none_plugin_count_manager.py old mode 100644 new mode 100755 index 88f5260e..2da14d9f --- a/utils/manager/none_plugin_count_manager.py +++ b/utils/manager/none_plugin_count_manager.py @@ -1,51 +1,51 @@ -from .data_class import StaticData -from typing import Optional -from pathlib import Path - - -class NonePluginCountManager(StaticData): - - """ - 插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据 - """ - - def __init__(self, file: Optional[Path], max_count: int = 5): - """ - :param file: 存储路径 - :param max_count: 容忍最大次数 - """ - super().__init__(file) - self._max_count = max_count - - def add_count(self, module: str, count: int = 1): - """ - 添加次数 - :param module: 模块 - :param count: 次数,无特殊情况均为 1 - """ - if module not in self._data.keys(): - self._data[module] = count - else: - self._data[module] += count - - def reset(self, module: str): - """ - 重置次数 - :param module: 模块 - """ - if module in self._data.keys(): - self._data[module] = 0 - - def check(self, module: str): - """ - 检查容忍次数是否到达最大值 - :param module: 模块 - """ - if module in self._data.keys(): - return self._data.keys() > self._max_count - return False - - - - - +from .data_class import StaticData +from typing import Optional +from pathlib import Path + + +class NonePluginCountManager(StaticData): + + """ + 插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据 + """ + + def __init__(self, file: Optional[Path], max_count: int = 5): + """ + :param file: 存储路径 + :param max_count: 容忍最大次数 + """ + super().__init__(file) + self._max_count = max_count + + def add_count(self, module: str, count: int = 1): + """ + 添加次数 + :param module: 模块 + :param count: 次数,无特殊情况均为 1 + """ + if module not in self._data.keys(): + self._data[module] = count + else: + self._data[module] += count + + def reset(self, module: str): + """ + 重置次数 + :param module: 模块 + """ + if module in self._data.keys(): + self._data[module] = 0 + + def check(self, module: str): + """ + 检查容忍次数是否到达最大值 + :param module: 模块 + """ + if module in self._data.keys(): + return self._data.keys() > self._max_count + return False + + + + + diff --git a/utils/manager/plugins2block_manager.py b/utils/manager/plugins2block_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/plugins2cd_manager.py b/utils/manager/plugins2cd_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/plugins2count_manager.py b/utils/manager/plugins2count_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/plugins2settings_manager.py b/utils/manager/plugins2settings_manager.py old mode 100644 new mode 100755 index d5f49fe6..0a794978 --- a/utils/manager/plugins2settings_manager.py +++ b/utils/manager/plugins2settings_manager.py @@ -14,6 +14,13 @@ class Plugins2settingsManager(StaticData): def __init__(self, file: Path): self.file = file super().__init__(None) + if file.exists(): + with open(file, "r", encoding="utf8") as f: + self._data = yaml.load(f) + if "PluginSettings" in self._data.keys(): + self._data = ( + self._data["PluginSettings"] if self._data["PluginSettings"] else {} + ) def add_plugin_settings( self, diff --git a/utils/manager/plugins_manager.py b/utils/manager/plugins_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/requests_manager.py b/utils/manager/requests_manager.py old mode 100644 new mode 100755 index 842926dd..1842bbd3 --- a/utils/manager/requests_manager.py +++ b/utils/manager/requests_manager.py @@ -1,246 +1,246 @@ -from utils.manager.data_class import StaticData -from nonebot.adapters.cqhttp import Bot -from nonebot.adapters.cqhttp.exception import ActionFailed -from services.log import logger -from typing import Optional -from utils.image_utils import CreateImg -from utils.utils import get_user_avatar -from pathlib import Path -from io import BytesIO - - -class RequestManager(StaticData): - - """ - 好友请求/邀请请求 管理 - """ - - def __init__(self, file: Optional[Path]): - super().__init__(file) - if not self._data: - self._data = {"private": {}, "group": {}} - - def add_request( - self, - id_: int, - type_: str, - flag: str, - *, - nickname: Optional[str] = None, - level: Optional[int] = None, - sex: Optional[str] = None, - age: Optional[str] = None, - from_: Optional[str] = "", - comment: Optional[str] = None, - invite_group: Optional[int] = None, - group_name: Optional[str] = None, - ): - """ - 添加一个请求 - :param id_: id,用户id或群id - :param type_: 类型,private 或 group - :param flag: event.flag - :param nickname: 用户昵称 - :param level: 等级 - :param sex: 性别 - :param age: 年龄 - :param from_: 请求来自 - :param comment: 附加消息 - :param invite_group: 邀请群聊 - :param group_name: 群聊名称 - """ - self._data[type_][str(len(self._data[type_].keys()))] = { - "id": id_, - "flag": flag, - "nickname": nickname, - "level": level, - "sex": sex, - "age": age, - "from": from_, - "comment": comment, - "invite_group": invite_group, - "group_name": group_name, - } - self.save() - - def remove_request(self, type_: str, id_: int): - """ - 删除一个请求数据 - :param type_: 类型 - :param id_: id,user_id 或 group_id - """ - for x in self._data[type_].keys(): - if self._data[type_][x].get("id") == id_: - del self._data[type_][x] - break - self.save() - - async def approve(self, bot: Bot, id_: int, type_: str) -> Optional[int]: - """ - 同意请求 - :param bot: Bot - :param id_: id - :param type_: 类型,private 或 group - """ - return await self._set_add_request(bot, id_, type_, True) - - async def refused(self, bot: Bot, id_: int, type_: str) -> Optional[int]: - """ - 拒绝请求 - :param bot: Bot - :param id_: id - :param type_: 类型,private 或 group - """ - return await self._set_add_request(bot, id_, type_, False) - - def clear(self): - """ - 清空所有请求信息,无视请求 - """ - self._data = {"private": {}, "group": {}} - self.save() - - def set_group_name(self, group_name: str, group_id: int): - """ - 设置群聊名称 - :param group_name: 名称 - :param group_id: id - """ - for id_ in self._data["group"].keys(): - if self._data["group"][id_]["invite_group"] == group_id: - self._data["group"][id_]["group_name"] = group_name - break - self.save() - - async def show(self, type_: str) -> Optional[str]: - """ - 请求可视化 - """ - data = self._data[type_] - if not data: - return None - img_list = [] - id_list = list(data.keys()) - id_list.reverse() - for id_ in id_list: - age = data[id_]["age"] - nickname = data[id_]["nickname"] - comment = data[id_]["comment"] if type_ == "private" else "" - from_ = data[id_]["from"] - sex = data[id_]["sex"] - ava = CreateImg( - 80, 80, background=BytesIO(await get_user_avatar(data[id_]["id"])) - ) - ava.circle() - age_bk = CreateImg( - len(str(age)) * 10 - 5, - 15, - color="#04CAF7" if sex == "male" else "#F983C1", - ) - age_bk.text((3, 1), f"{age}", fill=(255, 255, 255)) - x = CreateImg( - 90, 32, font_size=15, color="#EEEFF4", font="HYWenHei-85W.ttf" - ) - x.text((0, 0), "同意/拒绝", center_type="center") - x.circle_corner(10) - A = CreateImg(500, 100, font_size=24, font="msyh.ttf") - A.paste(ava, (15, 0), alpha=True, center_type="by_height") - A.text((120, 15), nickname) - A.paste(age_bk, (120, 50), True) - A.paste( - CreateImg( - 200, - 0, - font_size=12, - plain_text=f"对方留言:{comment}", - font_color=(140, 140, 143), - ), - (120 + age_bk.w + 10, 49), - True, - ) - if type_ == "private": - A.paste( - CreateImg( - 200, - 0, - font_size=12, - plain_text=f"来源:{from_}", - font_color=(140, 140, 143), - ), - (120, 70), - True, - ) - else: - A.paste( - CreateImg( - 200, - 0, - font_size=12, - plain_text=f"邀请你加入:{data[id_]['group_name']}({data[id_]['invite_group']})", - font_color=(140, 140, 143), - ), - (120, 70), - True, - ) - A.paste(x, (380, 35), True) - A.paste( - CreateImg( - 0, - 0, - plain_text=f"id:{id_}", - font_size=13, - font_color=(140, 140, 143), - ), - (400, 10), - True, - ) - img_list.append(A) - A = CreateImg(500, len(img_list) * 100, 500, 100) - for img in img_list: - A.paste(img) - bk = CreateImg(A.w, A.h + 50, color="#F8F9FB", font_size=20) - bk.paste(A, (0, 50)) - bk.text( - (15, 13), "好友请求" if type_ == "private" else "群聊请求", fill=(140, 140, 143) - ) - return bk.pic2bs4() - - async def _set_add_request( - self, bot: Bot, id_: int, type_: str, approve: bool - ) -> Optional[int]: - """ - 处理请求 - :param bot: Bot - :param id_: id - :param type_: 类型,private 或 group - :param approve: 是否同意 - """ - id_ = str(id_) - if id_ in self._data[type_]: - try: - if type_ == "private": - await bot.set_friend_add_request( - flag=self._data[type_][id_]["flag"], approve=approve - ) - rid = self._data[type_][id_]["id"] - else: - await bot.set_group_add_request( - flag=self._data[type_][id_]["flag"], - sub_type="invite", - approve=approve, - ) - rid = self._data[type_][id_]["invite_group"] - except ActionFailed: - logger.info( - f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" - f"的{'好友' if type_ == 'private' else '入群'}请求失败了..." - ) - return None - logger.info( - f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" - f"的{'好友' if type_ == 'private' else '入群'}请求..." - ) - del self._data[type_][id_] - self.save() - return rid - return None +from utils.manager.data_class import StaticData +from nonebot.adapters.cqhttp import Bot +from nonebot.adapters.cqhttp.exception import ActionFailed +from services.log import logger +from typing import Optional +from utils.image_utils import CreateImg +from utils.utils import get_user_avatar +from pathlib import Path +from io import BytesIO + + +class RequestManager(StaticData): + + """ + 好友请求/邀请请求 管理 + """ + + def __init__(self, file: Optional[Path]): + super().__init__(file) + if not self._data: + self._data = {"private": {}, "group": {}} + + def add_request( + self, + id_: int, + type_: str, + flag: str, + *, + nickname: Optional[str] = None, + level: Optional[int] = None, + sex: Optional[str] = None, + age: Optional[str] = None, + from_: Optional[str] = "", + comment: Optional[str] = None, + invite_group: Optional[int] = None, + group_name: Optional[str] = None, + ): + """ + 添加一个请求 + :param id_: id,用户id或群id + :param type_: 类型,private 或 group + :param flag: event.flag + :param nickname: 用户昵称 + :param level: 等级 + :param sex: 性别 + :param age: 年龄 + :param from_: 请求来自 + :param comment: 附加消息 + :param invite_group: 邀请群聊 + :param group_name: 群聊名称 + """ + self._data[type_][str(len(self._data[type_].keys()))] = { + "id": id_, + "flag": flag, + "nickname": nickname, + "level": level, + "sex": sex, + "age": age, + "from": from_, + "comment": comment, + "invite_group": invite_group, + "group_name": group_name, + } + self.save() + + def remove_request(self, type_: str, id_: int): + """ + 删除一个请求数据 + :param type_: 类型 + :param id_: id,user_id 或 group_id + """ + for x in self._data[type_].keys(): + if self._data[type_][x].get("id") == id_: + del self._data[type_][x] + break + self.save() + + async def approve(self, bot: Bot, id_: int, type_: str) -> Optional[int]: + """ + 同意请求 + :param bot: Bot + :param id_: id + :param type_: 类型,private 或 group + """ + return await self._set_add_request(bot, id_, type_, True) + + async def refused(self, bot: Bot, id_: int, type_: str) -> Optional[int]: + """ + 拒绝请求 + :param bot: Bot + :param id_: id + :param type_: 类型,private 或 group + """ + return await self._set_add_request(bot, id_, type_, False) + + def clear(self): + """ + 清空所有请求信息,无视请求 + """ + self._data = {"private": {}, "group": {}} + self.save() + + def set_group_name(self, group_name: str, group_id: int): + """ + 设置群聊名称 + :param group_name: 名称 + :param group_id: id + """ + for id_ in self._data["group"].keys(): + if self._data["group"][id_]["invite_group"] == group_id: + self._data["group"][id_]["group_name"] = group_name + break + self.save() + + async def show(self, type_: str) -> Optional[str]: + """ + 请求可视化 + """ + data = self._data[type_] + if not data: + return None + img_list = [] + id_list = list(data.keys()) + id_list.reverse() + for id_ in id_list: + age = data[id_]["age"] + nickname = data[id_]["nickname"] + comment = data[id_]["comment"] if type_ == "private" else "" + from_ = data[id_]["from"] + sex = data[id_]["sex"] + ava = CreateImg( + 80, 80, background=BytesIO(await get_user_avatar(data[id_]["id"])) + ) + ava.circle() + age_bk = CreateImg( + len(str(age)) * 10 - 5, + 15, + color="#04CAF7" if sex == "male" else "#F983C1", + ) + age_bk.text((3, 1), f"{age}", fill=(255, 255, 255)) + x = CreateImg( + 90, 32, font_size=15, color="#EEEFF4", font="HYWenHei-85W.ttf" + ) + x.text((0, 0), "同意/拒绝", center_type="center") + x.circle_corner(10) + A = CreateImg(500, 100, font_size=24, font="msyh.ttf") + A.paste(ava, (15, 0), alpha=True, center_type="by_height") + A.text((120, 15), nickname) + A.paste(age_bk, (120, 50), True) + A.paste( + CreateImg( + 200, + 0, + font_size=12, + plain_text=f"对方留言:{comment}", + font_color=(140, 140, 143), + ), + (120 + age_bk.w + 10, 49), + True, + ) + if type_ == "private": + A.paste( + CreateImg( + 200, + 0, + font_size=12, + plain_text=f"来源:{from_}", + font_color=(140, 140, 143), + ), + (120, 70), + True, + ) + else: + A.paste( + CreateImg( + 200, + 0, + font_size=12, + plain_text=f"邀请你加入:{data[id_]['group_name']}({data[id_]['invite_group']})", + font_color=(140, 140, 143), + ), + (120, 70), + True, + ) + A.paste(x, (380, 35), True) + A.paste( + CreateImg( + 0, + 0, + plain_text=f"id:{id_}", + font_size=13, + font_color=(140, 140, 143), + ), + (400, 10), + True, + ) + img_list.append(A) + A = CreateImg(500, len(img_list) * 100, 500, 100) + for img in img_list: + A.paste(img) + bk = CreateImg(A.w, A.h + 50, color="#F8F9FB", font_size=20) + bk.paste(A, (0, 50)) + bk.text( + (15, 13), "好友请求" if type_ == "private" else "群聊请求", fill=(140, 140, 143) + ) + return bk.pic2bs4() + + async def _set_add_request( + self, bot: Bot, id_: int, type_: str, approve: bool + ) -> Optional[int]: + """ + 处理请求 + :param bot: Bot + :param id_: id + :param type_: 类型,private 或 group + :param approve: 是否同意 + """ + id_ = str(id_) + if id_ in self._data[type_]: + try: + if type_ == "private": + await bot.set_friend_add_request( + flag=self._data[type_][id_]["flag"], approve=approve + ) + rid = self._data[type_][id_]["id"] + else: + await bot.set_group_add_request( + flag=self._data[type_][id_]["flag"], + sub_type="invite", + approve=approve, + ) + rid = self._data[type_][id_]["invite_group"] + except ActionFailed: + logger.info( + f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" + f"的{'好友' if type_ == 'private' else '入群'}请求失败了..." + ) + return None + logger.info( + f"同意{self._data[type_][id_]['nickname']}({self._data[type_][id_]['id']})" + f"的{'好友' if type_ == 'private' else '入群'}请求..." + ) + del self._data[type_][id_] + self.save() + return rid + return None diff --git a/utils/manager/resources_manager.py b/utils/manager/resources_manager.py old mode 100644 new mode 100755 diff --git a/utils/manager/withdraw_message_manager.py b/utils/manager/withdraw_message_manager.py old mode 100644 new mode 100755 diff --git a/utils/message_builder.py b/utils/message_builder.py old mode 100644 new mode 100755 diff --git a/utils/user_agent.py b/utils/user_agent.py old mode 100644 new mode 100755 diff --git a/utils/utils.py b/utils/utils.py old mode 100644 new mode 100755 index c5c469ec..96fa0efb --- a/utils/utils.py +++ b/utils/utils.py @@ -301,7 +301,7 @@ def is_chinese(word: str) -> bool: return True -async def get_user_avatar(qq: int) -> bytes: +async def get_user_avatar(qq: int) -> Optional[bytes]: """ 说明: 快捷获取用户头像 @@ -315,9 +315,10 @@ async def get_user_avatar(qq: int) -> bytes: return (await client.get(url)).content except TimeoutError: pass + return None -async def get_group_avatar(group_id: int) -> bytes: +async def get_group_avatar(group_id: int) -> Optional[bytes]: """ 说明: 快捷获取用群头像 @@ -331,6 +332,7 @@ async def get_group_avatar(group_id: int) -> bytes: return (await client.get(url)).content except TimeoutError: pass + return None def cn2py(word: str) -> str: @@ -346,18 +348,26 @@ def cn2py(word: str) -> str: return temp -def change_picture_links(url: str, mode: str): +def change_pixiv_image_links( + url: str, size: Optional[str] = None, nginx_url: Optional[str] = None +): """ 说明: - 根据配置改变图片大小 + 根据配置改变图片大小和反代链接 参数: :param url: 图片原图链接 - :param mode: 模式 + :param size: 模式 + :param nginx_url: 反代 """ - if mode == "master": + if size == "master": img_sp = url.rsplit(".", maxsplit=1) url = img_sp[0] img_type = img_sp[1] url = url.replace("original", "master") + f"_master1200.{img_type}" + if nginx_url: + url = ( + url.replace("i.pximg.net", nginx_url) + .replace("i.pixiv.cat", nginx_url) + .replace("_webp", "") + ) return url - diff --git a/utils/zh_wiki.py b/utils/zh_wiki.py old mode 100644 new mode 100755