diff --git a/admin_manager.py b/admin_manager.py deleted file mode 100644 index c050ad164..000000000 --- a/admin_manager.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Optional, Dict -from .data_class import StaticData -from utils.utils import FreqLimiter -from services.log import logger -from pathlib import Path - - -class AdminManager(StaticData): - """ - 管理员命令 管理器 - """ - - def __init__(self): - super().__init__(None) - - def add_admin_command(self, plugin: str, level: int): - """ - 添加一个管理员命令 - :param plugin: 模块名 - :param level: 权限等级 - """ - self._data[plugin] = level - - def remove_admin_command(self, plugin: str): - """ - 删除一个管理员命令 - :param plugin: 模块名 - """ - if plugin in self._data.keys(): - del self._data[plugin] - - def check(self, plugin: str, level: int) -> bool: - """ - 检查是否满足权限 - :param plugin: 模块名 - :param level: 权限等级 - """ - if plugin in self._data.keys(): - return level >= self._data[plugin] - return True - - def get_plugin_level(self, plugin: str) -> int: - """ - 获取插件等级 - :param plugin: 模块名 - """ - if plugin in self._data.keys(): - return self._data[plugin] - return 0 - - diff --git a/basic_plugins/__init__.py b/basic_plugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/basic_plugins/admin_bot_manage/__init__.py b/basic_plugins/admin_bot_manage/__init__.py new file mode 100644 index 000000000..0449b3f49 --- /dev/null +++ b/basic_plugins/admin_bot_manage/__init__.py @@ -0,0 +1,30 @@ +from configs.config import Config +import nonebot + + +Config.add_plugin_config( + "admin_bot_manage:custom_welcome_message", + "SET_GROUP_WELCOME_MESSAGE_LEVEL", + 2, + name="群管理员操作", + help_="设置群欢迎消息权限", + default_value=2, +) + +Config.add_plugin_config( + "admin_bot_manage:switch_rule", + "CHANGE_GROUP_SWITCH_LEVEL [LEVEL]", + 2, + help_="开关群功能权限", + default_value=2, +) + +Config.add_plugin_config( + "admin_bot_manage", + "ADMIN_DEFAULT_AUTH", + 5, + help_="默认群管理员权限", + default_value=5 +) + +nonebot.load_plugins("basic_plugins/admin_bot_manage") diff --git a/plugins/admin_config.py b/basic_plugins/admin_bot_manage/admin_config.py similarity index 69% rename from plugins/admin_config.py rename to basic_plugins/admin_bot_manage/admin_config.py index 82a750e61..bd34b3d5b 100644 --- a/plugins/admin_config.py +++ b/basic_plugins/admin_bot_manage/admin_config.py @@ -1,31 +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 ADMIN_DEFAULT_AUTH - - -__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, ADMIN_DEFAULT_AUTH) - logger.info(f"为新晋管理员 {nickname}({event.user_id}) 添加权限等级:{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/plugins/admin_bot_manage/custom_welcome_message.py b/basic_plugins/admin_bot_manage/custom_welcome_message.py similarity index 93% rename from plugins/admin_bot_manage/custom_welcome_message.py rename to basic_plugins/admin_bot_manage/custom_welcome_message.py index 1a570c2e4..6eca8539d 100644 --- a/plugins/admin_bot_manage/custom_welcome_message.py +++ b/basic_plugins/admin_bot_manage/custom_welcome_message.py @@ -4,7 +4,7 @@ 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 OC_LEVEL +from configs.config import Config from services.log import logger @@ -22,7 +22,7 @@ __plugin_version__ = 0.1 __plugin_author__ = 'HibiKier' __plugin_settings__ = { - "admin_level": OC_LEVEL, + "admin_level": Config.get_config("admin_bot_manage", "SET_GROUP_WELCOME_MESSAGE_LEVEL"), } custom_welcome = on_command( diff --git a/plugins/admin_bot_manage/data_source.py b/basic_plugins/admin_bot_manage/data_source.py similarity index 85% rename from plugins/admin_bot_manage/data_source.py rename to basic_plugins/admin_bot_manage/data_source.py index ceaf1c0ab..e0954eaa8 100644 --- a/plugins/admin_bot_manage/data_source.py +++ b/basic_plugins/admin_bot_manage/data_source.py @@ -1,273 +1,296 @@ -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 ADMIN_DEFAULT_AUTH -from utils.manager import group_manager, plugins2settings_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() - 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 == "开启": - group_manager.unblock_plugin(module) - else: - group_manager.block_plugin(module, block_type=block_type) - - -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 plugins2settings_manager.get_data(): - flag = group_manager.get_plugin_block_type(module) - flag = flag.upper() + " CLOSE" if flag else "OPEN" - try: - rst += f'{plugins2settings_manager.get(module)["cmd"][0]}\n' - except IndexError: - rst += f"{module}\n" - flag_str += f"{flag}\n" - height = len(rst.split("\n")) * 24 - a = CreateImg(150, height, font_size=20) - a.text((10, 10), rst) - b = CreateImg(200, height, font_size=20) - b.text((10, 10), flag_str) - A = CreateImg(380, height) - A.paste(a) - A.paste(b, (150, 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"], 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 +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 diff --git a/plugins/admin_bot_manage/rule.py b/basic_plugins/admin_bot_manage/rule.py similarity index 100% rename from plugins/admin_bot_manage/rule.py rename to basic_plugins/admin_bot_manage/rule.py diff --git a/plugins/admin_bot_manage/switch_rule.py b/basic_plugins/admin_bot_manage/switch_rule.py similarity index 95% rename from plugins/admin_bot_manage/switch_rule.py rename to basic_plugins/admin_bot_manage/switch_rule.py index 2f2a5b84e..fee7660b1 100644 --- a/plugins/admin_bot_manage/switch_rule.py +++ b/basic_plugins/admin_bot_manage/switch_rule.py @@ -8,7 +8,7 @@ group_current_status, ) from services.log import logger -from configs.config import NICKNAME, OC_LEVEL +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 @@ -47,7 +47,8 @@ __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { - "admin_level": OC_LEVEL, + "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) diff --git a/plugins/admin_bot_manage/timing_task.py b/basic_plugins/admin_bot_manage/timing_task.py similarity index 89% rename from plugins/admin_bot_manage/timing_task.py rename to basic_plugins/admin_bot_manage/timing_task.py index ad9635d49..5bb1aaddc 100644 --- a/plugins/admin_bot_manage/timing_task.py +++ b/basic_plugins/admin_bot_manage/timing_task.py @@ -2,7 +2,7 @@ from .data_source import update_member_info from services.log import logger from models.group_info import GroupInfo -from asyncpg.exceptions import ConnectionDoesNotExistError +from asyncpg.exceptions import ConnectionDoesNotExistError, UndefinedColumnError __zx_plugin_name__ = '管理方面定时任务 [Hidden]' @@ -47,5 +47,5 @@ async def _(): if g not in all_group: await update_member_info(g, False) logger.info(f"快速更新群信息以及权限:{g}") - except (IndexError, ConnectionDoesNotExistError): + except (IndexError, ConnectionDoesNotExistError, UndefinedColumnError): pass diff --git a/plugins/admin_bot_manage/update_group_member_info.py b/basic_plugins/admin_bot_manage/update_group_member_info.py similarity index 100% rename from plugins/admin_bot_manage/update_group_member_info.py rename to basic_plugins/admin_bot_manage/update_group_member_info.py diff --git a/plugins/admin_help/__init__.py b/basic_plugins/admin_help/__init__.py similarity index 96% rename from plugins/admin_help/__init__.py rename to basic_plugins/admin_help/__init__.py index 0b110162e..1262eb226 100644 --- a/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/plugins/admin_help/data_source.py b/basic_plugins/admin_help/data_source.py similarity index 97% rename from plugins/admin_help/data_source.py rename to basic_plugins/admin_help/data_source.py index 48d878327..9d8959dce 100644 --- a/plugins/admin_help/data_source.py +++ b/basic_plugins/admin_help/data_source.py @@ -18,13 +18,16 @@ @driver.on_bot_connect -async def create_help_image(bot: Bot = None): - """ - 创建管理员帮助图片 - """ +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 ) diff --git a/plugins/group_apscheduler/__init__.py b/basic_plugins/apscheduler/__init__.py similarity index 98% rename from plugins/group_apscheduler/__init__.py rename to basic_plugins/apscheduler/__init__.py index 3f0b6168d..5eb8463be 100644 --- a/plugins/group_apscheduler/__init__.py +++ b/basic_plugins/apscheduler/__init__.py @@ -8,7 +8,7 @@ from configs.config import NICKNAME from utils.manager import group_manager -__zx_plugin_name__ = "群定时任务相关 [Hidden]" +__zx_plugin_name__ = "定时任务相关 [Hidden]" __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_task__ = {'zwa': '早晚安'} @@ -106,7 +106,7 @@ async def _(): logger.error(f"自动更新群组信息错误 e:{e}") -# 一次性任务 + # 一次性任务 # 固定时间触发,仅触发一次: # # from datetime import datetime diff --git a/plugins/auto_invite/__init__.py b/basic_plugins/auto_invite/__init__.py similarity index 53% rename from plugins/auto_invite/__init__.py rename to basic_plugins/auto_invite/__init__.py index 74009e7d8..04d019838 100644 --- a/plugins/auto_invite/__init__.py +++ b/basic_plugins/auto_invite/__init__.py @@ -1,87 +1,150 @@ -from nonebot import on_request -from nonebot.adapters.cqhttp import Bot, FriendRequestEvent, GroupRequestEvent -from models.friend_user import FriendUser -from datetime import datetime -from configs.config import AUTO_ADD_FRIEND, NICKNAME -from nonebot.adapters.cqhttp.exception import ActionFailed -from utils.utils import scheduler -import time - -__zx_plugin_name__ = "好友群聊处理请求 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' - -friend_req = on_request(priority=5) - - -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"] - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"*****一份好友申请*****\n" - f"昵称:{nickname}({event.user_id})\n" - f"自动同意:{'√' if AUTO_ADD_FRIEND else '×'}\n" - f"日期:{str(datetime.now()).split('.')[0]}\n" - f"备注:{event.comment}", - ) - if AUTO_ADD_FRIEND: - await bot.set_friend_add_request(flag=event.flag, approve=True) - await FriendUser.add_friend_info(user["user_id"], user["nickname"]) - - -group_req = on_request(priority=5, block=True) - - -@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: - await bot.set_group_add_request( - flag=event.flag, sub_type="invite", approve=True - ) - except ActionFailed: - pass - else: - 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" - "等待管理员处理吧!", - ) - - -@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=5, block=False) + +exists_data = {"private": {}, "group": {}} + + +@friend_req.handle() +async def _(bot: Bot, event: FriendRequestEvent, state: dict): + print(event) + 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: + await bot.set_group_add_request( + flag=event.flag, sub_type="invite", approve=True + ) + 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, + ) + 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/plugins/ban/__init__.py b/basic_plugins/ban/__init__.py similarity index 95% rename from plugins/ban/__init__.py rename to basic_plugins/ban/__init__.py index f05900b96..7592c44ad 100644 --- a/plugins/ban/__init__.py +++ b/basic_plugins/ban/__init__.py @@ -1,189 +1,197 @@ -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, BAN_LEVEL -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": BAN_LEVEL, -} - - -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 +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的对象..') + diff --git a/plugins/broadcast/__init__.py b/basic_plugins/broadcast/__init__.py similarity index 96% rename from plugins/broadcast/__init__.py rename to basic_plugins/broadcast/__init__.py index 328d73c7a..b97d2a6cd 100644 --- a/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/plugins/group_handle/__init__.py b/basic_plugins/group_handle/__init__.py similarity index 66% rename from plugins/group_handle/__init__.py rename to basic_plugins/group_handle/__init__.py index ec6c348cb..d83ae8176 100644 --- a/plugins/group_handle/__init__.py +++ b/basic_plugins/group_handle/__init__.py @@ -1,141 +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 -from models.group_info import GroupInfo -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': '退群提醒' -} - - -# 群员增加处理 -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): - if 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'): - 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 - 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( + "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 diff --git a/plugins/help/__init__.py b/basic_plugins/help/__init__.py similarity index 60% rename from plugins/help/__init__.py rename to basic_plugins/help/__init__.py index 6fc4deb45..b28325bb2 100644 --- a/plugins/help/__init__.py +++ b/basic_plugins/help/__init__.py @@ -1,61 +1,72 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp import ( - Bot, - MessageEvent, -) -from nonebot.typing import T_State -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_img, get_plugin_help -from utils.utils import get_message_text -from pathlib import Path - - -__zx_plugin_name__ = "帮助" - - -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() - -_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(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', '') - msg = get_plugin_help(msg, is_super) - if msg: - await _help.send(image(b64=msg)) - else: - await _help.send("没有此功能的帮助信息...") - 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(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() +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/plugins/help/data_source.py b/basic_plugins/help/data_source.py similarity index 67% rename from plugins/help/data_source.py rename to basic_plugins/help/data_source.py index 2512f321c..64d0276d8 100644 --- a/plugins/help/data_source.py +++ b/basic_plugins/help/data_source.py @@ -1,289 +1,367 @@ -from utils.image_utils import CreateImg -from configs.path_config import IMAGE_PATH -from utils.manager import plugins2settings_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(help_image: Path, simple_help_image: Path): - """ - 生成帮助图片 - :param help_image: 图片路径 - :param simple_help_image: 简易帮助图片路径 - """ - return await asyncio.get_event_loop().run_in_executor( - None, _create_help_img, help_image, simple_help_image - ) - - -def _create_help_img(help_image: Path, simple_help_image: Path): - """ - 生成帮助图片 - :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] = { - "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): - simple_help_str += f"{i+1}.{k}\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)[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", - ) - 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 - 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, - color=(255, 255, 255, 0), - font='yuanshen.ttf' - ) - B.paste(text, (w, h), True) - h += 50 - if msg == '目前支持的功能列表:': - w += 50 - 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 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__") - 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 new file mode 100644 index 000000000..d1c21d413 --- /dev/null +++ b/basic_plugins/hooks/__init__.py @@ -0,0 +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") diff --git a/basic_plugins/hooks/auth_hook.py b/basic_plugins/hooks/auth_hook.py new file mode 100644 index 000000000..1dadbaa2c --- /dev/null +++ b/basic_plugins/hooks/auth_hook.py @@ -0,0 +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("此功能正在维护...") diff --git a/basic_plugins/hooks/ban_hook.py b/basic_plugins/hooks/ban_hook.py new file mode 100644 index 000000000..e7d131b0d --- /dev/null +++ b/basic_plugins/hooks/ban_hook.py @@ -0,0 +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 + raise IgnoredException("用户处于黑名单中") \ No newline at end of file diff --git a/basic_plugins/hooks/chkdsk_hook.py b/basic_plugins/hooks/chkdsk_hook.py new file mode 100644 index 000000000..3fa532851 --- /dev/null +++ b/basic_plugins/hooks/chkdsk_hook.py @@ -0,0 +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"]}') diff --git a/basic_plugins/hooks/limit_hook.py b/basic_plugins/hooks/limit_hook.py new file mode 100644 index 000000000..eff6760c7 --- /dev/null +++ b/basic_plugins/hooks/limit_hook.py @@ -0,0 +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 diff --git a/basic_plugins/hooks/other_hook.py b/basic_plugins/hooks/other_hook.py new file mode 100644 index 000000000..b2a859195 --- /dev/null +++ b/basic_plugins/hooks/other_hook.py @@ -0,0 +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("有命令就别说话了") + diff --git a/basic_plugins/hooks/utils.py b/basic_plugins/hooks/utils.py new file mode 100644 index 000000000..5231b8f9f --- /dev/null +++ b/basic_plugins/hooks/utils.py @@ -0,0 +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) + diff --git a/basic_plugins/hooks/withdraw_message_hook.py b/basic_plugins/hooks/withdraw_message_hook.py new file mode 100644 index 000000000..763565426 --- /dev/null +++ b/basic_plugins/hooks/withdraw_message_hook.py @@ -0,0 +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)) diff --git a/basic_plugins/init_plugin_config/__init__.py b/basic_plugins/init_plugin_config/__init__.py new file mode 100644 index 000000000..0b4efb6cc --- /dev/null +++ b/basic_plugins/init_plugin_config/__init__.py @@ -0,0 +1,51 @@ +from .init_group_manager import init_group_manager, group_manager +from .init_plugins_config import init_plugins_config +from .init_plugins_data import init_plugins_data, plugins_manager +from .init_none_plugin_count_manager import init_none_plugin_count_manager +from .init_plugins_resources import init_plugins_resources +from .init_plugins_settings import init_plugins_settings +from .init_plugins_limit import ( + init_plugins_block_limit, + init_plugins_count_limit, + init_plugins_cd_limit, +) +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 nonebot import Driver +import nonebot + + +__zx_plugin_name__ = "初始化插件数据 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +driver: Driver = nonebot.get_driver() + + +@driver.on_startup +def _(): + """ + 初始化数据 + """ + init_plugins_settings(DATA_PATH) + init_plugins_cd_limit(DATA_PATH) + init_plugins_block_limit(DATA_PATH) + init_plugins_count_limit(DATA_PATH) + init_plugins_data(DATA_PATH) + init_plugins_config(DATA_PATH) + init_plugins_resources() + init_none_plugin_count_manager() + x = group_manager.get_super_old_data() + if x: + for key in x.keys(): + plugins_manager.block_plugin(key, block_type=x[key]) + logger.info("初始化数据完成...") + + +@driver.on_bot_connect +async def _(bot: Bot): + await init_group_manager() + await check_plugin_status(bot) diff --git a/basic_plugins/init_plugin_config/check_plugin_status.py b/basic_plugins/init_plugin_config/check_plugin_status.py new file mode 100644 index 000000000..ad2bd049d --- /dev/null +++ b/basic_plugins/init_plugin_config/check_plugin_status.py @@ -0,0 +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] + ) diff --git a/basic_plugins/init_plugin_config/init_group_manager.py b/basic_plugins/init_plugin_config/init_group_manager.py new file mode 100644 index 000000000..3b91ae03a --- /dev/null +++ b/basic_plugins/init_plugin_config/init_group_manager.py @@ -0,0 +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() 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 new file mode 100644 index 000000000..b89dd422d --- /dev/null +++ b/basic_plugins/init_plugin_config/init_none_plugin_count_manager.py @@ -0,0 +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}" + ) diff --git a/basic_plugins/init_plugin_config/init_plugins_config.py b/basic_plugins/init_plugin_config/init_plugins_config.py new file mode 100644 index 000000000..addd86021 --- /dev/null +++ b/basic_plugins/init_plugin_config/init_plugins_config.py @@ -0,0 +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() diff --git a/basic_plugins/init_plugin_config/init_plugins_data.py b/basic_plugins/init_plugin_config/init_plugins_data.py new file mode 100644 index 000000000..e90d5238b --- /dev/null +++ b/basic_plugins/init_plugin_config/init_plugins_data.py @@ -0,0 +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() diff --git a/basic_plugins/init_plugin_config/init_plugins_limit.py b/basic_plugins/init_plugin_config/init_plugins_limit.py new file mode 100644 index 000000000..459f747cd --- /dev/null +++ b/basic_plugins/init_plugin_config/init_plugins_limit.py @@ -0,0 +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() diff --git a/basic_plugins/init_plugin_config/init_plugins_resources.py b/basic_plugins/init_plugin_config/init_plugins_resources.py new file mode 100644 index 000000000..36e2f47b9 --- /dev/null +++ b/basic_plugins/init_plugin_config/init_plugins_resources.py @@ -0,0 +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() + + + + + + + + + + + + diff --git a/basic_plugins/init_plugin_config/init_plugins_settings.py b/basic_plugins/init_plugin_config/init_plugins_settings.py new file mode 100644 index 000000000..c1ec6c11f --- /dev/null +++ b/basic_plugins/init_plugin_config/init_plugins_settings.py @@ -0,0 +1,130 @@ +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())} 个非限制插件.") diff --git a/plugins/nickname.py b/basic_plugins/nickname.py similarity index 97% rename from plugins/nickname.py rename to basic_plugins/nickname.py index f06018b8b..75e497d44 100644 --- a/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 new file mode 100644 index 000000000..2a8879e65 --- /dev/null +++ b/basic_plugins/scripts.py @@ -0,0 +1,111 @@ +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}") + + + + + diff --git a/basic_plugins/super_cmd/__init__.py b/basic_plugins/super_cmd/__init__.py new file mode 100644 index 000000000..b4997936f --- /dev/null +++ b/basic_plugins/super_cmd/__init__.py @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..def933c89 --- /dev/null +++ b/basic_plugins/super_cmd/bot_friend_group.py @@ -0,0 +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("已清空所有好友/群聊请求..") diff --git a/basic_plugins/super_cmd/clear_data.py b/basic_plugins/super_cmd/clear_data.py new file mode 100644 index 000000000..b77237adb --- /dev/null +++ b/basic_plugins/super_cmd/clear_data.py @@ -0,0 +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)) diff --git a/plugins/super_cmd/data_source.py b/basic_plugins/super_cmd/data_source.py similarity index 97% rename from plugins/super_cmd/data_source.py rename to basic_plugins/super_cmd/data_source.py index 532448232..86c06db06 100644 --- a/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/plugins/group_manager/__init__.py b/basic_plugins/super_cmd/manager_group.py similarity index 57% rename from plugins/group_manager/__init__.py rename to basic_plugins/super_cmd/manager_group.py index b7d738557..36d4fb569 100644 --- a/plugins/group_manager/__init__.py +++ b/basic_plugins/super_cmd/manager_group.py @@ -1,34 +1,45 @@ from nonebot import on_command, on_regex -from utils.utils import get_message_text, is_number +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 nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP -from nonebot.typing import T_State -from nonebot.permission import SUPERUSER from configs.config import NICKNAME -from utils.manager import group_manager, plugins2settings_manager +from nonebot.adapters.cqhttp.exception import ActionFailed -try: - import ujson as json -except ModuleNotFoundError: - import json -__zx_plugin_name__ = "群权限操作 [Superuser]" +__zx_plugin_name__ = "管理群操作 [Superuser]" __plugin_usage__ = """ usage: - 对群权限 | 群白名单 的操作 + 群权限 | 群白名单 | 退出群 操作 指令: - 修改群权限 [group] [等级] - 添加群白名单 *[group] - 删除群白名单 *[group] + 退群 [group_id] + 修改群权限 [group_id] [等级] + 添加群白名单 *[group_id] + 删除群白名单 *[group_id] + 添加群认证 *[group_id] + 删除群认证 *[group_id] 查看群白名单 """.strip() -__plugin_des__ = "对群权限 | 群白名单 的操作" -__plugin_cmd__ = ["修改群权限 [group] [等级]", "添加群白名单 *[group]", "删除群白名单 *[group]", "查看群白名单"] +__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 @@ -40,7 +51,6 @@ permission=GROUP, block=True, ) - manager_group_whitelist = on_command( "添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True ) @@ -49,6 +59,29 @@ "查看群白名单", 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): @@ -130,3 +163,38 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): 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 new file mode 100644 index 000000000..b392f9131 --- /dev/null +++ b/basic_plugins/super_cmd/reload_setting.py @@ -0,0 +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("重载完成...") diff --git a/basic_plugins/super_cmd/set_admin_permissions.py b/basic_plugins/super_cmd/set_admin_permissions.py new file mode 100644 index 000000000..f075dd948 --- /dev/null +++ b/basic_plugins/super_cmd/set_admin_permissions.py @@ -0,0 +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}") diff --git a/basic_plugins/super_cmd/super_task_switch.py b/basic_plugins/super_cmd/super_task_switch.py new file mode 100644 index 000000000..391d57784 --- /dev/null +++ b/basic_plugins/super_cmd/super_task_switch.py @@ -0,0 +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: + 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 new file mode 100644 index 000000000..091e89f8d --- /dev/null +++ b/basic_plugins/super_cmd/update_friend_group_info.py @@ -0,0 +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]}") diff --git a/plugins/super_help/__init__.py b/basic_plugins/super_help/__init__.py similarity index 96% rename from plugins/super_help/__init__.py rename to basic_plugins/super_help/__init__.py index 1bdbaf694..49aa7058f 100644 --- a/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/plugins/super_help/data_source.py b/basic_plugins/super_help/data_source.py similarity index 99% rename from plugins/super_help/data_source.py rename to basic_plugins/super_help/data_source.py index c0c98448d..730ff1523 100644 --- a/plugins/super_help/data_source.py +++ b/basic_plugins/super_help/data_source.py @@ -33,8 +33,8 @@ def _create_help_image(): width = 0 help_str = "超级用户帮助\n\n* 注: ‘*’ 代表可有多个相同参数 ‘?’ 代表可省略该参数 *\n\n" tmp_img = CreateImg(0, 0, plain_text='1', font_size=24) - plugin_name = "" for matcher in _matchers: + plugin_name = "" try: _plugin = nonebot.plugin.get_plugin(matcher.module) _module = _plugin.module diff --git a/plugins/update_info.py b/basic_plugins/update_info.py similarity index 96% rename from plugins/update_info.py rename to basic_plugins/update_info.py index 8d11d1446..e86e82afd 100644 --- a/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/bot.py b/bot.py index d384928d7..c8f1a34bb 100644 --- a/bot.py +++ b/bot.py @@ -9,6 +9,9 @@ config = driver.config driver.on_startup(init) driver.on_shutdown(disconnect) +# 优先加载定时任务插件 +nonebot.load_plugin("nonebot_plugin_apscheduler") +nonebot.load_plugins("basic_plugins") nonebot.load_plugins("plugins") diff --git a/configs/config.py b/configs/config.py index a727a5726..ead748f6b 100644 --- a/configs/config.py +++ b/configs/config.py @@ -1,21 +1,11 @@ -from typing import List, Optional, Tuple -from services.service_config import TL_M_KEY, SYSTEM_M_PROXY, ALAPI_M_TOKEN -try: - import ujson as json -except ModuleNotFoundError: - import json +from typing import Optional +from pathlib import Path +from .utils import ConfigsManager # 回复消息名称 NICKNAME: str = "小真寻" -# API KEY(必要) -RSSHUBAPP: str = "https://rsshub.app" # rsshub -ALAPI_TOKEN: str = "" # ALAPI https://admin.alapi.cn/user/login -HIBIAPI: str = "https://api.obfs.dev" -# 图灵 -TL_KEY: List[str] = [] - # 数据库(必要) # 如果填写了bind就不需要再填写后面的字段了#) # 示例:"bind": "postgresql://user:password@127.0.0.1:5432/database" @@ -27,113 +17,8 @@ port: str = "" # 数据库端口 database: str = "" # 数据库名称 -# 代理 -SYSTEM_PROXY: Optional[str] = None # 全局代理 -BUFF_PROXY: Optional[str] = None # Buff代理 - -# 公开图库列表 -IMAGE_DIR_LIST: List[str] = ["美图", "萝莉", "壁纸"] - -# 对被ban用户发送的消息 -BAN_RESULT: str = "才不会给你发消息." - -# PIX图库下载的画质 可能的值:original:原图,master:缩略图(加快发送速度) -PIX_IMAGE_SIZE: str = "master" - - -# 插件配置 -MAXINFO_REIMU: int = 7 # 上车(reimu)功能查找目的地的最大数 -COUNT_PER_DAY_REIMU: int = 5 # 每日上车(reimu)次数限制 -MAXINFO_BT: int = 10 # bt功能单次查找最大数 -MAXINFO_PRIVATE_ANIME: int = 20 # 私聊搜索动漫返回的最大数量 -MAXINFO_GROUP_ANIME: int = 5 # 群搜索动漫返回的最大数量 -MAX_FIND_IMG_COUNT: int = 3 # 识图最大返回数 -# 参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊) -WITHDRAW_SETU_TIME: Tuple[int, int] = (0, 1) -# 参1:延迟撤回PIX图片时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊) -WITHDRAW_PIX_TIME: Tuple[int, int] = (0, 1) - -# PIX图库 与 额外图库OmegaPixivIllusts 混合搜索的比例 参1:PIX图库 参2:OmegaPixivIllusts扩展图库(没有此图库请设置为0) -PIX_OMEGA_PIXIV_RATIO: Tuple[int, int] = (10, 0) - -# 各种卡池的开关 -PRTS_FLAG = True # 明日方舟 -GENSHIN_FLAG = True # 原神 -PRETTY_FLAG = True # 赛马娘 -GUARDIAN_FLAG = True # 坎公骑冠剑 -PCR_FLAG = True # 公主连结 -AZUR_FLAG = True # 碧蓝航线 -FGO_FLAG = True # 命运-冠位指定(FGO) -ONMYOJI_FLAG = True # 阴阳师 - -PCR_TAI = True # pcr是否开启台服卡池 -SEMAPHORE = 5 # 限制碧蓝航线和FGO并发数 - -ADMIN_DEFAULT_AUTH: int = 5 # 默认群管理员权限 - -MAX_SIGN_GOLD: int = 200 # 签到好感度加成额外获得的最大金币数 -MAX_RUSSIAN_BET_GOLD: int = 1000 # 俄罗斯轮盘最大赌注金额 - -INITIAL_SETU_PROBABILITY: float = 0.7 # 色图概率 -FUDU_PROBABILITY: float = 0.7 # 复读概率 - -INITIAL_OPEN_CASE_COUNT: int = 20 # 初始开箱次数 -MUTE_DEFAULT_COUNT: int = 10 # 刷屏禁言默认检测次数 -MUTE_DEFAULT_TIME: int = 7 # 刷屏检测默认规定时间 -MUTE_DEFAULT_DURATION: int = 10 # 刷屏检测默禁言时长(分钟) - -CHECK_NOTICE_INFO_CD = 300 # 群检测,个人权限检测等各种检测提示信息cd - -# 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被ban MALICIOUS_BAN_TIME 分钟 -MALICIOUS_BAN_TIME: int = 30 # 恶意命令触发检测触发后ban的时长(分钟) -MALICIOUS_BAN_COUNT: int = 6 # 恶意命令触发检测最大触发次数 -MALICIOUS_CHECK_TIME: int = 5 # 恶意命令触发检测规定时间内(秒) - -# LEVEL -DELETE_IMG_LEVEL: int = 7 # 删除图片权限 -MOVE_IMG_LEVEL: int = 7 # 移动图片权限 -UPLOAD_IMG_LEVEL: int = 6 # 上传图片权限 -BAN_LEVEL: int = 5 # BAN权限 -OC_LEVEL: int = 2 # 开关群功能权限 -MUTE_LEVEL: int = 5 # 更改禁言设置权限 -MEMBER_ACTIVITY_LEVEL = 5 # 群员活跃检测设置权限 -GROUP_BILIBILI_SUB_LEVEL = 5 # 群内bilibili订阅需要的权限 - -DEFAULT_GROUP_LEVEL = 5 # 默认群等级 - -# 是否开启HIBIAPI搜图功能(该功能会搜索群友提交的xp) -HIBIAPI_FLAG: bool = True -# HIBIAPI搜图图片的最低收藏 -HIBIAPI_BOOKMARKS: int = 5000 - -# 需要为哪些群更新最新版gocq吗?(上传最新版gocq) -# 示例:[434995955, 239483248] -UPDATE_GOCQ_GROUP: List[int] = [] - -# 是否存储色图 -DOWNLOAD_SETU: bool = True -# 仅仅使用本地色图 -ONLY_USE_LOCAL_SETU: bool = False -# 是否自动同意好友添加 -AUTO_ADD_FRIEND: bool = True -# 当含有ALAPI_TOKEN时是否检测文本合规,开启检测会减慢回复速度 -ALAPI_AI_CHECK: bool = True -# 导入商店自带的三个商品 -IMPORT_DEFAULT_SHOP_GOODS: bool = True -# 真寻是否自动更新 -AUTO_UPDATE_ZHENXUN: bool = True - - -if TL_M_KEY: - TL_KEY = TL_M_KEY -if SYSTEM_M_PROXY: - SYSTEM_PROXY = SYSTEM_M_PROXY -if ALAPI_M_TOKEN: - ALAPI_TOKEN = ALAPI_M_TOKEN - - -HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI -RSSHUBAPP = RSSHUBAPP[:-1] if RSSHUBAPP[-1] == "/" else RSSHUBAPP - +# 全局代理,例如 "http://127.0.0.1:7890" +SYSTEM_PROXY: Optional[str] = None +Config = ConfigsManager(Path() / "data" / "configs" / "plugins2config.yaml") diff --git a/configs/utils/__init__.py b/configs/utils/__init__.py new file mode 100644 index 000000000..1cd427778 --- /dev/null +++ b/configs/utils/__init__.py @@ -0,0 +1,159 @@ +from typing import Optional, Any, Union +from pathlib import Path +from ruamel.yaml import YAML +from ruamel import yaml + + +class ConfigsManager: + """ + 插件配置 与 资源 管理器 + """ + + def __init__(self, file: Path): + self._data: dict = {} + self._admin_level_data = [] + if file: + file.parent.mkdir(exist_ok=True, parents=True) + self.file = file + if file.exists(): + _yaml = YAML() + with open(file, "r", encoding="utf8") as f: + self._data = _yaml.load(f) + + def add_plugin_config( + self, + module: str, + key: str, + value: Optional[Any], + *, + name: Optional[str] = None, + help_: Optional[str] = None, + default_value: Optional[str] = None, + _override: bool = False + ): + """ + 为插件添加一个配置,不会被覆盖,只有第一个生效 + :param module: 模块 + :param key: 键 + :param value: 值 + :param name: 插件名称 + :param help_: 配置注解 + :param default_value: 默认值 + :param _override: 覆盖前值 + """ + if ( + not (module in self._data.keys() and self._data[module].get(key)) + or _override + ): + _module = None + if ":" in module: + module = module.split(":") + _module = module[-1] + module = module[0] + if "[LEVEL]" in key and _module: + key = key.replace("[LEVEL]", "").strip() + self._admin_level_data.append((_module, value)) + if self._data.get(module) is None: + self._data[module] = {} + key = key.upper() + self._data[module][key] = { + "value": value, + "name": name.strip() if isinstance(name, str) else name, + "help": help_.strip() if isinstance(help_, str) else help_, + "default_value": default_value, + } + + def remove_plugin_config(self, module: str): + """ + 为插件删除一个配置 + :param module: 模块名 + """ + if module in self._data.keys(): + del self._data[module] + + def set_config(self, module: str, key: str, value: str): + """ + 设置配置值 + :param module: 模块名 + :param key: 配置名称 + :param value: 值 + """ + if module in self._data.keys(): + if self._data[module].get(key) is not None: + self._data[module][key]["value"] = value + + def set_help(self, module: str, key: str, help_: str): + """ + 设置配置注释 + :param module: 模块名 + :param key: 配置名称 + :param help_: 注释文本 + """ + if module in self._data.keys(): + if self._data[module].get(key) is not None: + self._data[module][key]["help"] = help_ + + def set_default_value(self, module: str, key: str, value: str): + """ + 设置配置默认值 + :param module: 模块名 + :param key: 配置名称 + :param value: 值 + """ + if module in self._data.keys(): + if self._data[module].get(key) is not None: + self._data[module][key]["default_value"] = value + + def get_config(self, module: str, key: str) -> Optional[Any]: + """ + 获取指定配置值 + :param module: 模块名 + :param key: 配置名称 + """ + key = key.upper() + if module in self._data.keys(): + if self._data[module].get(key) is not None: + if self._data[module][key]["value"] is None: + return self._data[module][key]["default_value"] + return self._data[module][key]["value"] + return None + + def get(self, key: str): + """ + 获取插件配置数据 + :param key: 名称 + """ + if key in self._data.keys(): + return self._data[key] + + def save(self, path: Union[str, Path] = None): + """ + 保存数据 + :param path: 路径 + """ + path = path if path else self.file + with open(path, "w", encoding="utf8") as f: + yaml.dump( + self._data, f, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True + ) + + def get_admin_level_data(self): + """ + 获取管理插件等级 + """ + return self._admin_level_data + + def is_empty(self) -> bool: + return not bool(self._data) + + def keys(self): + return self._data.keys() + + 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/docs/ban.png b/docs/ban.png deleted file mode 100644 index 087ac9bab..000000000 Binary files a/docs/ban.png and /dev/null differ diff --git a/docs/beidong.png b/docs/beidong.png deleted file mode 100644 index f269e60b8..000000000 Binary files a/docs/beidong.png and /dev/null differ diff --git a/docs/buff.png b/docs/buff.png deleted file mode 100644 index aa7948f7c..000000000 Binary files a/docs/buff.png and /dev/null differ diff --git a/docs/check.png b/docs/check.png deleted file mode 100644 index 4d3c614bb..000000000 Binary files a/docs/check.png and /dev/null differ diff --git a/docs/coser.png b/docs/coser.png deleted file mode 100644 index c546a5307..000000000 Binary files a/docs/coser.png and /dev/null differ diff --git a/docs/daoju.png b/docs/daoju.png deleted file mode 100644 index e41403f8f..000000000 Binary files a/docs/daoju.png and /dev/null differ diff --git a/docs/epic.png b/docs/epic.png deleted file mode 100644 index 96ae4510a..000000000 Binary files a/docs/epic.png and /dev/null differ diff --git a/docs/fanyi.png b/docs/fanyi.png deleted file mode 100644 index 8919e0d5d..000000000 Binary files a/docs/fanyi.png and /dev/null differ diff --git a/docs/guanli.png b/docs/guanli.png deleted file mode 100644 index 9cab74788..000000000 Binary files a/docs/guanli.png and /dev/null differ diff --git a/docs/help.PNG b/docs/help.PNG deleted file mode 100644 index 157a16b32..000000000 Binary files a/docs/help.PNG and /dev/null differ diff --git a/docs/huifu.png b/docs/huifu.png deleted file mode 100644 index 93760157a..000000000 Binary files a/docs/huifu.png and /dev/null differ diff --git a/docs/info.png b/docs/info.png deleted file mode 100644 index 172a6f024..000000000 Binary files a/docs/info.png and /dev/null differ diff --git a/docs/jiaxiaoxi.png b/docs/jiaxiaoxi.png deleted file mode 100644 index 7a9a598e6..000000000 Binary files a/docs/jiaxiaoxi.png and /dev/null differ diff --git a/docs/jieshao.png b/docs/jieshao.png deleted file mode 100644 index 423ef585c..000000000 Binary files a/docs/jieshao.png and /dev/null differ diff --git a/docs/jitang.png b/docs/jitang.png deleted file mode 100644 index a6d98fa53..000000000 Binary files a/docs/jitang.png and /dev/null differ diff --git a/docs/kaixiang.png b/docs/kaixiang.png deleted file mode 100644 index 8ddcf0231..000000000 Binary files a/docs/kaixiang.png and /dev/null differ diff --git a/docs/kg1.png b/docs/kg1.png deleted file mode 100644 index c7a0dd0f2..000000000 Binary files a/docs/kg1.png and /dev/null differ diff --git a/docs/kg2.png b/docs/kg2.png deleted file mode 100644 index caec2c2a8..000000000 Binary files a/docs/kg2.png and /dev/null differ diff --git a/docs/kg3.png b/docs/kg3.png deleted file mode 100644 index 962692d26..000000000 Binary files a/docs/kg3.png and /dev/null differ diff --git a/docs/luxun.png b/docs/luxun.png deleted file mode 100644 index 2d72cd504..000000000 Binary files a/docs/luxun.png and /dev/null differ diff --git a/docs/mawo.png b/docs/mawo.png deleted file mode 100644 index 979b9fac1..000000000 Binary files a/docs/mawo.png and /dev/null differ diff --git a/docs/nicheng1.png b/docs/nicheng1.png deleted file mode 100644 index 971f80019..000000000 Binary files a/docs/nicheng1.png and /dev/null differ diff --git a/docs/nicheng2.png b/docs/nicheng2.png deleted file mode 100644 index d3fdc0a37..000000000 Binary files a/docs/nicheng2.png and /dev/null differ diff --git a/docs/ocgn.png b/docs/ocgn.png deleted file mode 100644 index 8f882b06d..000000000 Binary files a/docs/ocgn.png and /dev/null differ diff --git a/docs/ocgn2.png b/docs/ocgn2.png deleted file mode 100644 index dbb2ee9bb..000000000 Binary files a/docs/ocgn2.png and /dev/null differ diff --git a/docs/one_firend.png b/docs/one_firend.png deleted file mode 100644 index 48aa0427b..000000000 Binary files a/docs/one_firend.png and /dev/null differ diff --git a/docs/p_rank.png b/docs/p_rank.png deleted file mode 100644 index 989a59f49..000000000 Binary files a/docs/p_rank.png and /dev/null differ diff --git a/docs/p_sou.png b/docs/p_sou.png deleted file mode 100644 index 27d419527..000000000 Binary files a/docs/p_sou.png and /dev/null differ diff --git a/docs/qhyxx.png b/docs/qhyxx.png deleted file mode 100644 index 85922e42c..000000000 Binary files a/docs/qhyxx.png and /dev/null differ diff --git a/docs/qunhuanying.png b/docs/qunhuanying.png deleted file mode 100644 index 09050fbd0..000000000 Binary files a/docs/qunhuanying.png and /dev/null differ diff --git a/docs/redbag0.png b/docs/redbag0.png deleted file mode 100644 index 11a19ca7e..000000000 Binary files a/docs/redbag0.png and /dev/null differ diff --git a/docs/redbag1.png b/docs/redbag1.png deleted file mode 100644 index 9883f0682..000000000 Binary files a/docs/redbag1.png and /dev/null differ diff --git a/docs/redbag2.png b/docs/redbag2.png deleted file mode 100644 index e6ecbd3b8..000000000 Binary files a/docs/redbag2.png and /dev/null differ diff --git a/docs/russian0.png b/docs/russian0.png deleted file mode 100644 index c9ae5d0e4..000000000 Binary files a/docs/russian0.png and /dev/null differ diff --git a/docs/russian1.png b/docs/russian1.png deleted file mode 100644 index 01598a982..000000000 Binary files a/docs/russian1.png and /dev/null differ diff --git a/docs/russian2.png b/docs/russian2.png deleted file mode 100644 index 9fe9a0556..000000000 Binary files a/docs/russian2.png and /dev/null differ diff --git a/docs/send_img.png b/docs/send_img.png deleted file mode 100644 index 932939052..000000000 Binary files a/docs/send_img.png and /dev/null differ diff --git a/docs/shifan.png b/docs/shifan.png deleted file mode 100644 index 071b515e2..000000000 Binary files a/docs/shifan.png and /dev/null differ diff --git a/docs/shitu.png b/docs/shitu.png deleted file mode 100644 index 452b3eff2..000000000 Binary files a/docs/shitu.png and /dev/null differ diff --git a/docs/shop.png b/docs/shop.png deleted file mode 100644 index 4b2480b6a..000000000 Binary files a/docs/shop.png and /dev/null differ diff --git a/docs/sign.png b/docs/sign.png deleted file mode 100644 index b136c413c..000000000 Binary files a/docs/sign.png and /dev/null differ diff --git a/docs/tupian.png b/docs/tupian.png deleted file mode 100644 index 04bef568d..000000000 Binary files a/docs/tupian.png and /dev/null differ diff --git a/docs/w2b.png b/docs/w2b.png deleted file mode 100644 index dc1719aaa..000000000 Binary files a/docs/w2b.png and /dev/null differ diff --git a/docs/zhenxun.jpg b/docs/zhenxun.jpg deleted file mode 100644 index 61171ed87..000000000 Binary files a/docs/zhenxun.jpg and /dev/null differ diff --git a/models/count_user.py b/models/count_user.py deleted file mode 100644 index 39d597714..000000000 --- a/models/count_user.py +++ /dev/null @@ -1,63 +0,0 @@ -from services.db_context import db - - -class UserCount(db.Model): - __tablename__ = "count_users" - - user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True) - reimu_count = db.Column(db.Integer(), nullable=False, default=0) - setu_r18_count = db.Column(db.Integer(), nullable=False, default=0) - - _idx1 = db.Index("sign_reimu_users_idx1", "user_qq", unique=True) - - @classmethod - async def add_count(cls, user_qq: int, name: str, count: int = 1): - """ - 说明: - 用户添加次数 - 参数: - :param user_qq: qq号 - :param name: 目标名称 - :param count: 增加次数 - """ - query = cls.query.where((cls.user_qq == user_qq)) - query = query.with_for_update() - user = await query.gino.first() - user = user if user else await cls.create(user_qq=user_qq) - if name == "reimu": - await user.update(reimu_count=cls.reimu_count + count).apply() - if name == "setu_r18": - await user.update(setu_r18_count=cls.setu_r18_count + count).apply() - - @classmethod - async def check_count(cls, user_qq: int, name: str, max_count: int) -> bool: - """ - 说明: - 检测次数是否到达最大值 - 参数: - :param user_qq: qq号 - :param name: 目标名称 - :param max_count: 最大值 - """ - query = cls.query.where((cls.user_qq == user_qq)) - user = await query.gino.first() - user = user if user else await cls.create(user_qq=user_qq) - if name == "reimu": - if user.reimu_count == max_count: - return True - else: - return False - if name == "setu_r18": - if user.setu_r18_count == max_count: - return True - else: - return False - - @classmethod - async def reset_count(cls): - """ - 说明: - 重置每日次数 - """ - for user in await cls.query.gino.all(): - await user.update(reimu_count=0, setu_r18_count=0).apply() diff --git a/models/group_info.py b/models/group_info.py index c89994740..960c695a8 100644 --- a/models/group_info.py +++ b/models/group_info.py @@ -1,5 +1,6 @@ from services.db_context import db -from typing import List +from services.log import logger +from typing import List, Optional class GroupInfo(db.Model): @@ -9,6 +10,7 @@ class GroupInfo(db.Model): group_name = db.Column(db.Unicode(), nullable=False, default="") max_member_count = db.Column(db.Integer(), nullable=False, default=0) member_count = db.Column(db.Integer(), nullable=False, default=0) + group_flag = db.Column(db.Integer(), nullable=False, default=0) _idx1 = db.Index("group_info_idx1", "group_id", unique=True) @@ -25,7 +27,12 @@ async def get_group_info(cls, group_id: int) -> "GroupInfo": @classmethod async def add_group_info( - cls, group_id: int, group_name: str, max_member_count: int, member_count: int + cls, + group_id: int, + group_name: str, + max_member_count: int, + member_count: int, + group_flag: Optional[int] = None, ) -> bool: """ 说明: @@ -35,6 +42,7 @@ async def add_group_info( :param group_name: 群名称 :param max_member_count: 群员最大数量 :param member_count: 群员数量 + :param group_flag: 群认证,0为未认证,1为认证 """ try: group = ( @@ -43,36 +51,35 @@ async def add_group_info( .gino.first() ) if group: - await cls.update( - group_id=group_id, + await group.update( group_name=group_name, max_member_count=max_member_count, member_count=member_count, ).apply() + if group_flag is not None: + await group.update(group_flag=group_flag).apply() else: await cls.create( group_id=group_id, group_name=group_name, max_member_count=max_member_count, member_count=member_count, + group_flag=group_flag, ) return True - except Exception: + except Exception as e: + logger.info(f"GroupInfo 调用 add_group_info 发生错误 {type(e)}:{e}") return False @classmethod - async def delete_group_info(cls, group_id: int) -> bool: + async def delete_group_info(cls, group_id: int): """ 说明: 删除群信息 参数: :param group_id: 群号 """ - try: - await cls.delete.where(cls.group_id == group_id).gino.status() - return True - except Exception: - return False + await cls.delete.where(cls.group_id == group_id).gino.status() @classmethod async def get_all_group(cls) -> List["GroupInfo"]: @@ -82,3 +89,23 @@ async def get_all_group(cls) -> List["GroupInfo"]: """ query = await cls.query.gino.all() return query + + @classmethod + async def set_group_flag(cls, group_id: int, group_flag: int) -> bool: + """ + 设置群认证 + :param group_id: 群号 + :param group_flag: 群认证,0为未认证,1为认证 + """ + group = ( + await cls.query.where(cls.group_id == group_id) + .with_for_update() + .gino.first() + ) + if group: + if group.group_flag != group_flag: + await group.update( + group_flag=group_flag, + ).apply() + return True + return False diff --git a/models/group_remind.py b/models/group_remind.py deleted file mode 100644 index 0dab8985e..000000000 --- a/models/group_remind.py +++ /dev/null @@ -1,105 +0,0 @@ -from services.db_context import db - - -class GroupRemind(db.Model): - __tablename__ = "group_reminds" - - id = db.Column(db.Integer(), primary_key=True) - group_id = db.Column(db.BigInteger(), nullable=False) - hy = db.Column(db.Boolean(), default=False) # 进群欢迎 - kxcz = db.Column(db.Boolean(), default=False) # 开箱重置 - zwa = db.Column(db.Boolean(), default=False) # 早晚安 - gb = db.Column(db.Boolean(), default=True) # 广播 - blpar = db.Column(db.Boolean(), default=True) # bilibili转发解析 - pa = db.Column(db.Boolean(), default=True) # 爬 - epic = db.Column(db.Boolean(), default=False) # epic - almanac = db.Column(db.Boolean(), default=False) # 原神黄历 - - _idx1 = db.Index("info_group_reminds_idx1", "group_id", unique=True) - - @classmethod - async def get_status(cls, group_id: int, name: str) -> bool: - """ - 说明: - 获取群通知状态 - 参数: - :param group_id: 群号 - :param name: 目标名称 - """ - group = await cls.query.where((cls.group_id == group_id)).gino.first() - if not group: - group = await cls.create( - group_id=group_id, - ) - if name == "hy": - return group.hy - if name == "kxcz": - return group.kxcz - if name == "zwa": - return group.zwa - if name == "gb": - return group.gb - if name == "blpar": - return group.blpar - if name == "epic": - return group.epic - if name == "pa": - return group.pa - if name == "almanac": - return group.almanac - - @classmethod - async def set_status(cls, group_id: int, name: str, status: bool) -> bool: - """ - 说明: - 设置群通知状态 - 参数: - :param group_id: 群号 - :param name: 目标名称 - :param status: 通知状态 - """ - try: - group = ( - await cls.query.where((cls.group_id == group_id)) - .with_for_update() - .gino.first() - ) - if not group: - group = await cls.create( - group_id=group_id, - ) - if name == "hy": - await group.update( - hy=status, - ).apply() - if name == "kxcz": - await group.update( - kxcz=status, - ).apply() - if name == "zwa": - await group.update( - zwa=status, - ).apply() - if name == "gb": - await group.update( - gb=status, - ).apply() - if name == "blpar": - await group.update( - blpar=status, - ).apply() - if name == "epic": - await group.update( - epic=status, - ).apply() - if name == "pa": - await group.update( - pa=status, - ).apply() - if name == "almanac": - await group.update( - almanac=status, - ).apply() - return True - except Exception as e: - return False \ No newline at end of file diff --git a/plugins/Yu-Gi-Oh/__init__.py b/plugins/Yu-Gi-Oh/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/admin_bot_manage/__init__.py b/plugins/admin_bot_manage/__init__.py deleted file mode 100644 index 4985070ce..000000000 --- a/plugins/admin_bot_manage/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import nonebot - -nonebot.load_plugins("plugins/admin_bot_manage") diff --git a/plugins/ai/__init__.py b/plugins/ai/__init__.py index 96af27e74..fc55b73e1 100644 --- a/plugins/ai/__init__.py +++ b/plugins/ai/__init__.py @@ -13,7 +13,7 @@ from services.log import logger from utils.utils import get_message_text, get_message_imgs from .data_source import get_chat_result, hello, no_result -from configs.config import NICKNAME +from configs.config import NICKNAME, Config __zx_plugin_name__ = "AI" __plugin_usage__ = f""" @@ -28,6 +28,23 @@ "limit_superuser": False, "cmd": ["Ai", "ai", "AI", "aI"], } +__plugin_configs__ = { + "TL_KEY": { + "value": [], + "help": "图灵Key" + }, + "ALAPI_AI_CHECK": { + "value": False, + "help": "是否检测青云客骂娘回复", + "default_value": False + } +} +Config.add_plugin_config( + "alapi", + "ALAPI_TOKEN", + None, + help_="在https://admin.alapi.cn/user/login登录后获取token" +) ai = on_message(rule=to_me(), priority=8) @@ -36,7 +53,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) imgs = get_message_imgs(event.json()) - if "CQ:xml" in msg: + if "CQ:xml" in str(event.get_message()): return # 打招呼 if (not msg and not imgs) or msg in [ diff --git a/plugins/ai/data_source.py b/plugins/ai/data_source.py index d197b83f5..d36005ae0 100644 --- a/plugins/ai/data_source.py +++ b/plugins/ai/data_source.py @@ -4,11 +4,10 @@ import aiohttp from aiohttp.client import ClientSession - -from configs.config import TL_KEY, ALAPI_TOKEN, ALAPI_AI_CHECK, NICKNAME 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 try: import ujson as json @@ -70,6 +69,7 @@ async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) -> :return: 图灵回复 """ global index + TL_KEY = Config.get_config("ai", "TL_KEY") try: if text: req = { @@ -149,7 +149,7 @@ async def xie_ai(text: str, sess: ClientSession) -> str: break return ( content - if not content and not ALAPI_AI_CHECK + if not content and not Config.get_config("ai", "ALAPI_AI_CHECK") else await check_text(content, sess) ) @@ -200,9 +200,9 @@ async def check_text(text: str, sess: ClientSession) -> str: :param text: 回复 :param sess: AIOHTTP SESSION """ - if not ALAPI_TOKEN: + if not Config.get_config("alapi", "ALAPI_TOKEN"): return text - params = {"token": ALAPI_TOKEN, "text": 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() diff --git a/plugins/alapi/__init__.py b/plugins/alapi/__init__.py index f4c7b35fe..ddabb5d52 100644 --- a/plugins/alapi/__init__.py +++ b/plugins/alapi/__init__.py @@ -1,3 +1,11 @@ +from configs.config import Config import nonebot nonebot.load_plugins("plugins/alapi") + +Config.add_plugin_config( + "alapi", + "ALAPI_TOKEN", + None, + help_="在https://admin.alapi.cn/user/login登录后获取token" +) diff --git a/plugins/alapi/comments_163.py b/plugins/alapi/comments_163.py index 12d10ba8e..b88cc643e 100644 --- a/plugins/alapi/comments_163.py +++ b/plugins/alapi/comments_163.py @@ -1,7 +1,6 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State -from configs.config import ALAPI_TOKEN from .data_source import get_data from services.log import logger @@ -34,8 +33,7 @@ @comments_163.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - params = {"token": ALAPI_TOKEN} - data, code = await get_data(comments_163_url, params) + data, code = await get_data(comments_163_url) if code != 200: await comments_163.finish(data, at_sender=True) data = data["data"] diff --git a/plugins/alapi/cover.py b/plugins/alapi/cover.py index 48efbbcc1..8d0ce8c95 100644 --- a/plugins/alapi/cover.py +++ b/plugins/alapi/cover.py @@ -1,53 +1,48 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent from nonebot.typing import T_State -from configs.config import ALAPI_TOKEN 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: +__zx_plugin_name__ = "b封面" +__plugin_usage__ = """ +usage: b封面 [链接/av/bv/cv/直播id] 示例:b封面 av86863038 -""" -__plugin_des__ = '快捷的b站视频封面获取方式' -__plugin_cmd__ = ['b封面/B封面'] -__plugin_type__ = ('一些工具',) +""".strip() +__plugin_des__ = "快捷的b站视频封面获取方式" +__plugin_cmd__ = ["b封面/B封面"] +__plugin_type__ = ("一些工具",) __plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' +__plugin_author__ = "HibiKier" __plugin_settings__ = { "level": 5, "default_status": True, "limit_superuser": False, - "cmd": ["b封面", 'B封面'], + "cmd": ["b封面", "B封面"], } -cover = on_command('b封面', aliases={'B封面'}, priority=5, block=True) +cover = on_command("b封面", aliases={"B封面"}, priority=5, block=True) -cover_url = 'https://v2.alapi.cn/api/bilibili/cover' +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 = { - 'token': ALAPI_TOKEN, - 'c': msg - } + 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)}')) + 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}") - - - + f" 获取b站封面: {title} url:{img}" + ) diff --git a/plugins/alapi/data_source.py b/plugins/alapi/data_source.py index 7c13ab5c7..0571e802f 100644 --- a/plugins/alapi/data_source.py +++ b/plugins/alapi/data_source.py @@ -2,21 +2,23 @@ from utils.image_utils import CreateImg from utils.message_builder import image from configs.path_config import IMAGE_PATH -from typing import Union +from typing import Optional +from configs.config import Config import aiohttp -async def get_data(url: str, params: dict) -> 'Union[dict, str], int': +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: + async with session.get(url, timeout=2, params=params) as response: data = await response.json() if data["code"] == 200: if not data["data"]: @@ -33,13 +35,13 @@ 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 = 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') + 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'] + 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) diff --git a/plugins/alapi/poetry.py b/plugins/alapi/poetry.py index 009ef2266..05395c2c7 100644 --- a/plugins/alapi/poetry.py +++ b/plugins/alapi/poetry.py @@ -1,48 +1,42 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State -from configs.config import ALAPI_TOKEN from services.log import logger from .data_source import get_data -__zx_plugin_name__ = '古诗' +__zx_plugin_name__ = "古诗" __plugin_usage__ = """usage: 平白无故念首诗 示例:念诗/来首诗/念首诗 """ -__plugin_des__ = '为什么突然文艺起来了!' -__plugin_cmd__ = ['念诗/来首诗/念首诗'] +__plugin_des__ = "为什么突然文艺起来了!" +__plugin_cmd__ = ["念诗/来首诗/念首诗"] __plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' +__plugin_author__ = "HibiKier" __plugin_settings__ = { "level": 5, "default_status": True, "limit_superuser": False, - "cmd": ['念诗', '来首诗', '念首诗'], + "cmd": ["念诗", "来首诗", "念首诗"], } -poetry = on_command("念诗", aliases={'来首诗', '念首诗'}, priority=5, block=True) +poetry = on_command("念诗", aliases={"来首诗", "念首诗"}, priority=5, block=True) -poetry_url = 'https://v2.alapi.cn/api/shici' +poetry_url = "https://v2.alapi.cn/api/shici" @poetry.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - params = { - 'token': ALAPI_TOKEN - } - data, code = await get_data(poetry_url, params) + 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}》') + 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}》'") - - - + f" 发送古诗: f'{content}\n\t--{author}《{title}》'" + ) diff --git a/plugins/alapi/wbtop.py b/plugins/alapi/wbtop.py index deca46041..e367e5331 100644 --- a/plugins/alapi/wbtop.py +++ b/plugins/alapi/wbtop.py @@ -1,7 +1,6 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State -from configs.config import ALAPI_TOKEN from services.log import logger from .data_source import get_data, gen_wbtop_pic from utils.browser import get_browser @@ -43,10 +42,7 @@ 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: - params = { - 'token': ALAPI_TOKEN - } - data, code = await get_data(wbtop_url, params) + data, code = await get_data(wbtop_url) if code != 200: await wbtop.finish(data, at_sender=True) wbtop_data = data['data'] @@ -67,6 +63,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): 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() diff --git a/plugins/bilibili_sub/__init__.py b/plugins/bilibili_sub/__init__.py index e13d08593..6ee0138c2 100644 --- a/plugins/bilibili_sub/__init__.py +++ b/plugins/bilibili_sub/__init__.py @@ -9,11 +9,11 @@ get_media_id, get_sub_status, SubManager, + BilibiliSub ) from models.level_user import LevelUser -from configs.config import GROUP_BILIBILI_SUB_LEVEL +from configs.config import Config from utils.utils import get_message_text, is_number, scheduler, get_bot -from models.bilibili_sub import BilibiliSub from typing import Optional from services.log import logger from nonebot import Driver @@ -34,20 +34,27 @@ 示例:添加订阅番剧 125344 <-(番剧id) 示例:删除订阅 2324344 <-(任意id,通过查看订阅获取) """.strip() -__plugin_des__ = '非常便利的B站订阅通知' -__plugin_cmd__ = ['添加订阅 [主播/UP/番剧] [id/链接/番名]', '删除订阅 [id]', '查看订阅'] +__plugin_des__ = "非常便利的B站订阅通知" +__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"] __plugin_version__ = 0.1 -__plugin_author__ = 'HibiKier' +__plugin_author__ = "HibiKier" __plugin_settings__ = { "level": 5, "default_status": True, "limit_superuser": False, - "cmd": ["B站订阅", 'b站订阅', '添加订阅', '删除订阅', '查看订阅'], + "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) +show_sub_info = on_command("查看订阅", priority=5, block=True) driver: Driver = nonebot.get_driver() @@ -79,10 +86,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): id_ = "" if isinstance(event, GroupMessageEvent): if not await LevelUser.check_level( - event.user_id, event.group_id, GROUP_BILIBILI_SUB_LEVEL + event.user_id, + event.group_id, + Config.get_config("bilibili_sub", "GROUP_BILIBILI_SUB_LEVEL"), ): await add_sub.finish( - f"您的权限不足,群内订阅的需要 {GROUP_BILIBILI_SUB_LEVEL} 级权限..", at_sender=True + f"您的权限不足,群内订阅的需要 {Config.get_config('bilibili_sub', 'GROUP_BILIBILI_SUB_LEVEL')} 级权限..", + at_sender=True, ) sub_user = f"{event.user_id}:{event.group_id}" else: @@ -134,44 +144,52 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): 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}' + 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} 成功...') + 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} 失败...') + 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}' + 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 = '' + 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 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 = '您目前没有任何订阅...' + live_rst = "您目前没有任何订阅..." await show_sub_info.send(live_rst + up_rst + season_rst) diff --git a/plugins/bilibili_sub/data_source.py b/plugins/bilibili_sub/data_source.py index a99251e29..3261df3e6 100644 --- a/plugins/bilibili_sub/data_source.py +++ b/plugins/bilibili_sub/data_source.py @@ -1,6 +1,7 @@ from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException +from utils.manager import resources_manager from asyncio.exceptions import TimeoutError -from models.bilibili_sub import BilibiliSub +from .model import BilibiliSub from bilibili_api.live import LiveRoom from bilibili_api import bangumi from utils.message_builder import image @@ -23,6 +24,9 @@ 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: """ 添加直播订阅 diff --git a/models/bilibili_sub.py b/plugins/bilibili_sub/model.py similarity index 100% rename from models/bilibili_sub.py rename to plugins/bilibili_sub/model.py diff --git a/plugins/bt/__init__.py b/plugins/bt/__init__.py index f99020e6e..d155550e3 100644 --- a/plugins/bt/__init__.py +++ b/plugins/bt/__init__.py @@ -32,6 +32,13 @@ __plugin_block_limit__ = { "rst": "您有bt任务正在进行,请等待结束." } +__plugin_configs__ = { + "BT_MAX_NUM": { + "value": 10, + "help": "单次BT搜索返回最大消息数量", + "default_value": 10, + } +} bt = on_command("bt", permission=PRIVATE, priority=5, block=True) diff --git a/plugins/bt/data_source.py b/plugins/bt/data_source.py index 80a5433b7..3ea7270eb 100644 --- a/plugins/bt/data_source.py +++ b/plugins/bt/data_source.py @@ -1,6 +1,6 @@ from utils.user_agent import get_user_agent import aiohttp -from configs.config import MAXINFO_BT +from configs.config import Config from bs4 import BeautifulSoup from utils.utils import get_local_proxy import platform @@ -24,7 +24,9 @@ async def get_bt_info(keyword: str, page: str): return soup = BeautifulSoup(text, "lxml") item_lst = soup.find_all("div", {"class": "search-item"}) - for item in item_lst[:MAXINFO_BT]: + 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) diff --git a/plugins/check/__init__.py b/plugins/check/__init__.py index 0861ffb29..dd008217f 100644 --- a/plugins/check/__init__.py +++ b/plugins/check/__init__.py @@ -4,6 +4,7 @@ from nonebot.typing import T_State from nonebot.rule import to_me from nonebot.permission import SUPERUSER +from utils.message_builder import image __zx_plugin_name__ = "服务器自我检查 [Superuser]" @@ -29,4 +30,4 @@ @check_.handle() async def _(bot: Bot, event: Event, state: T_State): - await check_.send(await check.show()) + await check_.send(image(b64=await check.show())) diff --git a/plugins/check/data_source.py b/plugins/check/data_source.py index baa0af81c..81b156eef 100644 --- a/plugins/check/data_source.py +++ b/plugins/check/data_source.py @@ -6,6 +6,9 @@ from asyncio.exceptions import TimeoutError from aiohttp.client_exceptions import ClientConnectorError from utils.utils import get_local_proxy +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH +from pathlib import Path import asyncio from services.log import logger @@ -57,6 +60,7 @@ def check_user(self): async def show(self): await self.check_all() + A = CreateImg(0, 0, font_size=24) rst = ( f'[Time] {str(datetime.now()).split(".")[0]}\n' f"-----System-----\n" @@ -69,4 +73,17 @@ async def show(self): ) if self.user: rst += "-----User-----\n" + self.user - return rst + width = 0 + height = 0 + for x in rst.split('\n'): + w, h = A.getsize(x) + if w > width: + width = w + height += 30 + A = CreateImg(width + 50, height + 10, font_size=24, font="HWZhongSong.ttf") + A.transparent(1) + A.text((10, 10), rst) + _x = max(width, height) + bk = CreateImg(_x + 100, _x + 100, background=Path(IMAGE_PATH) / "background" / "check" / "0.jpg") + bk.paste(A, alpha=True, center_type='center') + return bk.pic2bs4() diff --git a/plugins/check_zhenxun_update/__init__.py b/plugins/check_zhenxun_update/__init__.py index 525fbf4b8..1014025bc 100644 --- a/plugins/check_zhenxun_update/__init__.py +++ b/plugins/check_zhenxun_update/__init__.py @@ -6,7 +6,7 @@ from services.log import logger from utils.utils import scheduler, get_bot from pathlib import Path -from configs.config import AUTO_UPDATE_ZHENXUN +from configs.config import Config from nonebot.rule import to_me import platform import os @@ -24,6 +24,13 @@ __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) @@ -82,7 +89,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): minute=0, ) async def _(): - if AUTO_UPDATE_ZHENXUN: + if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"): _version = "v0.0.0" _version_file = Path() / "__version__" if _version_file.exists(): diff --git a/plugins/coser/__init__.py b/plugins/coser/__init__.py index 9e5d78cc1..1d67c0190 100644 --- a/plugins/coser/__init__.py +++ b/plugins/coser/__init__.py @@ -41,7 +41,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): try: for _ in range(3): try: - async with session.get(url, proxy=get_local_proxy(), timeout=2) as response: + 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 diff --git a/plugins/csgo/__init__.py b/plugins/csgo/__init__.py new file mode 100644 index 000000000..e955a89ff --- /dev/null +++ b/plugins/csgo/__init__.py @@ -0,0 +1,69 @@ +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 new file mode 100644 index 000000000..b6c6969e7 --- /dev/null +++ b/plugins/csgo/data_source.py @@ -0,0 +1,130 @@ +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 index cfb0a8184..e8ad4c1e8 100644 --- a/plugins/dialogue/__init__.py +++ b/plugins/dialogue/__init__.py @@ -4,6 +4,7 @@ from nonebot.permission import SUPERUSER from utils.utils import get_message_text, is_number, get_message_imgs from utils.message_builder import image +from utils.message_builder import text as _text from services.log import logger from utils.message_builder import at @@ -52,7 +53,7 @@ dialogue_data = {} -dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=1, block=True) +dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=5, block=True) reply = on_command("/t", priority=1, permission=SUPERUSER, block=True) @@ -61,7 +62,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): uid = event.user_id coffee = int(list(bot.config.superusers)[0]) text = get_message_text(event.json()) - img_msg = "" + img_msg = _text("") for img in get_message_imgs(event.json()): img_msg += image(img) if not text or text in ["帮助"]: @@ -78,15 +79,16 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): nickname = event.sender.card if event.sender.card else event.sender.nickname await bot.send_private_msg( user_id=coffee, - message=Message( + message=_text( f"*****一份交流报告*****\n" f"昵称:{nickname}({uid})\n" f"群聊:{group_name}({group_id})\n" - f"消息:{text} {img_msg}" - ), + f"消息:{text}" + ) + + img_msg, ) await dialogue.send( - Message(f"您的话已发送至管理员!\n======\n{text}{img_msg}"), at_sender=True + _text(f"您的话已发送至管理员!\n======\n{text}") + img_msg, at_sender=True ) nickname = event.sender.nickname if event.sender.nickname else event.sender.card dialogue_data[len(dialogue_data)] = { @@ -94,7 +96,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): "user_id": event.user_id, "group_id": group_id, "group_name": group_name, - "msg": f"{text} {img_msg}", + "msg": _text(text) + img_msg, } # print(dialogue_data) logger.info(f"Q{uid}@群{group_id} 联系管理员:{coffee} text:{text}") diff --git a/plugins/draw_card/config.py b/plugins/draw_card/config.py index 2340d01ee..e3a91db02 100644 --- a/plugins/draw_card/config.py +++ b/plugins/draw_card/config.py @@ -1,31 +1,56 @@ import nonebot from pathlib import Path from configs.path_config import DATA_PATH -from configs.config import FGO_FLAG, PCR_FLAG, AZUR_FLAG, PRTS_FLAG,\ - PRETTY_FLAG, GUARDIAN_FLAG, GENSHIN_FLAG, ONMYOJI_FLAG, PCR_TAI, SEMAPHORE +from configs.config import Config + try: import ujson as json except ModuleNotFoundError: import json -DRAW_PATH = DATA_PATH + '/draw_card/' +DRAW_PATH = DATA_PATH + "/draw_card/" _draw_config = Path(rf"{DRAW_PATH}/draw_card_config/draw_card_config.json") -# 开关 -PRTS_FLAG = PRTS_FLAG -GENSHIN_FLAG = GENSHIN_FLAG -PRETTY_FLAG = PRETTY_FLAG -GUARDIAN_FLAG = GUARDIAN_FLAG -PCR_FLAG = PCR_FLAG -AZUR_FLAG = AZUR_FLAG -FGO_FLAG = FGO_FLAG -ONMYOJI_FLAG = ONMYOJI_FLAG +for game_flag, game_name in zip( + [ + "PRTS_FLAG", + "GENSHIN_FLAG", + "PRETTY_FLAG", + "GUARDIAN_FLAG", + "PCR_FLAG", + "AZUR_FLAG", + "FGO_FLAG", + "ONMYOJI_FLAG", + "PCR_TAI", + ], + ["明日方舟", "原神", "赛马娘", "坎公骑冠剑", "公主连结", "碧蓝航线", "命运-冠位指定(FGO)", "阴阳师", "pcr台服卡池"], +): + Config.add_plugin_config( + "draw_card", + game_flag, + True, + name="游戏抽卡", + help_=f"{game_name} 抽卡开关", + default_value=True, + ) +Config.add_plugin_config( + "draw_card", "SEMAPHORE", 5, help_=f"异步数据下载数量限制", default_value=5 +) +PRTS_FLAG = Config.get_config("draw_card", "PRTS_FLAG") +GENSHIN_FLAG = Config.get_config("draw_card", "GENSHIN_FLAG") +PRETTY_FLAG = Config.get_config("draw_card", "PRETTY_FLAG") +GUARDIAN_FLAG = Config.get_config("draw_card", "GUARDIAN_FLAG") +PCR_FLAG = Config.get_config("draw_card", "PCR_FLAG") +AZUR_FLAG = Config.get_config("draw_card", "AZUR_FLAG") +FGO_FLAG = Config.get_config("draw_card", "FGO_FLAG") +ONMYOJI_FLAG = Config.get_config("draw_card", "ONMYOJI_FLAG") + +PCR_TAI = Config.get_config("draw_card", "PCR_TAI") +SEMAPHORE = Config.get_config("draw_card", "SEMAPHORE") -PCR_TAI = PCR_TAI -SEMAPHORE = SEMAPHORE # 方舟概率 PRTS_SIX_P = 0.02 @@ -97,244 +122,290 @@ path_dict = { - 'genshin': '原神', - 'prts': '明日方舟', - 'pretty': '赛马娘', - 'guardian': '坎公骑冠剑', - 'pcr': '公主连结', - 'azur': '碧蓝航线', - 'fgo': '命运-冠位指定', - 'onmyoji': '阴阳师', + "genshin": "原神", + "prts": "明日方舟", + "pretty": "赛马娘", + "guardian": "坎公骑冠剑", + "pcr": "公主连结", + "azur": "碧蓝航线", + "fgo": "命运-冠位指定", + "onmyoji": "阴阳师", } driver: nonebot.Driver = nonebot.get_driver() config_default_data = { - - 'path_dict': { - 'genshin': '原神', - 'prts': '明日方舟', - 'pretty': '赛马娘', - 'guardian': '坎公骑冠剑', - 'pcr': '公主连结', - 'azur': '碧蓝航线', - 'fgo': '命运-冠位指定', - 'onmyoji': '阴阳师', + "path_dict": { + "genshin": "原神", + "prts": "明日方舟", + "pretty": "赛马娘", + "guardian": "坎公骑冠剑", + "pcr": "公主连结", + "azur": "碧蓝航线", + "fgo": "命运-冠位指定", + "onmyoji": "阴阳师", }, - - 'prts': { - 'PRTS_SIX_P': 0.02, - 'PRTS_FIVE_P': 0.08, - 'PRTS_FOUR_P': 0.48, - 'PRTS_THREE_P': 0.42, + "prts": { + "PRTS_SIX_P": 0.02, + "PRTS_FIVE_P": 0.08, + "PRTS_FOUR_P": 0.48, + "PRTS_THREE_P": 0.42, }, - - 'genshin': { - 'GENSHIN_FIVE_P': 0.006, - 'GENSHIN_FOUR_P': 0.051, - 'GENSHIN_THREE_P': 0.43, - 'GENSHIN_G_FIVE_P': 0.13, - 'GENSHIN_G_FOUR_P': 0.016, - 'I72_ADD': 0.0585, + "genshin": { + "GENSHIN_FIVE_P": 0.006, + "GENSHIN_FOUR_P": 0.051, + "GENSHIN_THREE_P": 0.43, + "GENSHIN_G_FIVE_P": 0.13, + "GENSHIN_G_FOUR_P": 0.016, + "I72_ADD": 0.0585, }, - - 'pretty': { - 'PRETTY_THREE_P': 0.03, - 'PRETTY_TWO_P': 0.18, - 'PRETTY_ONE_P': 0.79, + "pretty": { + "PRETTY_THREE_P": 0.03, + "PRETTY_TWO_P": 0.18, + "PRETTY_ONE_P": 0.79, }, - - 'guardian': { - 'GUARDIAN_THREE_CHAR_P': 0.0275, - 'GUARDIAN_TWO_CHAR_P': 0.19, - 'GUARDIAN_ONE_CHAR_P': 0.7825, - - 'GUARDIAN_THREE_CHAR_UP_P': 0.01375, - 'GUARDIAN_THREE_CHAR_OTHER_P': 0.01375, - - 'GUARDIAN_EXCLUSIVE_ARMS_P': 0.03, - 'GUARDIAN_FIVE_ARMS_P': 0.03, - 'GUARDIAN_FOUR_ARMS_P': 0.09, - 'GUARDIAN_THREE_ARMS_P': 0.27, - 'GUARDIAN_TWO_ARMS_P': 0.58, - - 'GUARDIAN_EXCLUSIVE_ARMS_UP_P': 0.01, - 'GUARDIAN_EXCLUSIVE_ARMS_OTHER_P': 0.02, + "guardian": { + "GUARDIAN_THREE_CHAR_P": 0.0275, + "GUARDIAN_TWO_CHAR_P": 0.19, + "GUARDIAN_ONE_CHAR_P": 0.7825, + "GUARDIAN_THREE_CHAR_UP_P": 0.01375, + "GUARDIAN_THREE_CHAR_OTHER_P": 0.01375, + "GUARDIAN_EXCLUSIVE_ARMS_P": 0.03, + "GUARDIAN_FIVE_ARMS_P": 0.03, + "GUARDIAN_FOUR_ARMS_P": 0.09, + "GUARDIAN_THREE_ARMS_P": 0.27, + "GUARDIAN_TWO_ARMS_P": 0.58, + "GUARDIAN_EXCLUSIVE_ARMS_UP_P": 0.01, + "GUARDIAN_EXCLUSIVE_ARMS_OTHER_P": 0.02, }, - - 'pcr': { - 'PCR_THREE_P': 0.025, - 'PCR_TWO_P': 0.18, - 'PCR_ONE_P': 0.795, + "pcr": { + "PCR_THREE_P": 0.025, + "PCR_TWO_P": 0.18, + "PCR_ONE_P": 0.795, }, - - 'azur': { - 'AZUR_FIVE_P': 0.012, - 'AZUR_FOUR_P': 0.07, - 'AZUR_THREE_P': 0.12, - 'AZUR_TWO_P': 0.51, - 'AZUR_ONE_P': 0.3, + "azur": { + "AZUR_FIVE_P": 0.012, + "AZUR_FOUR_P": 0.07, + "AZUR_THREE_P": 0.12, + "AZUR_TWO_P": 0.51, + "AZUR_ONE_P": 0.3, }, - - 'fgo': { - 'FGO_SERVANT_FIVE_P': 0.01, - 'FGO_SERVANT_FOUR_P': 0.03, - 'FGO_SERVANT_THREE_P': 0.4, - 'FGO_CARD_FIVE_P': 0.04, - 'FGO_CARD_FOUR_P': 0.12, - 'FGO_CARD_THREE_P': 0.4, + "fgo": { + "FGO_SERVANT_FIVE_P": 0.01, + "FGO_SERVANT_FOUR_P": 0.03, + "FGO_SERVANT_THREE_P": 0.4, + "FGO_CARD_FIVE_P": 0.04, + "FGO_CARD_FOUR_P": 0.12, + "FGO_CARD_THREE_P": 0.4, + }, + "onmyoji": { + "ONMYOJI_SP": 0.0025, + "ONMYOJI_SSR": 0.01, + "ONMYOJI_SR": 0.2, + "ONMYOJI_R": 0.7875, }, - - 'onmyoji': { - 'ONMYOJI_SP': 0.0025, - 'ONMYOJI_SSR': 0.01, - 'ONMYOJI_SR': 0.2, - 'ONMYOJI_R': 0.7875, - } } @driver.on_startup def check_config(): - global PRTS_SIX_P, PRTS_FOUR_P, PRTS_FIVE_P, PRTS_THREE_P, GENSHIN_G_FIVE_P, config_default_data, \ - GENSHIN_G_FOUR_P, GENSHIN_FOUR_P, GENSHIN_FIVE_P, I72_ADD, path_dict, PRETTY_THREE_P, \ - PRETTY_ONE_P, PRETTY_TWO_P, GENSHIN_THREE_P, GUARDIAN_THREE_CHAR_P, GUARDIAN_TWO_CHAR_P, GUARDIAN_ONE_CHAR_P, \ - GUARDIAN_THREE_CHAR_UP_P, GUARDIAN_THREE_CHAR_OTHER_P, GUARDIAN_EXCLUSIVE_ARMS_P, GUARDIAN_FIVE_ARMS_P, \ - GUARDIAN_FOUR_ARMS_P, GUARDIAN_THREE_ARMS_P, GUARDIAN_TWO_ARMS_P, GENSHIN_FLAG, PRTS_FLAG, \ - PRETTY_FLAG, GUARDIAN_FLAG, GUARDIAN_EXCLUSIVE_ARMS_UP_P, GUARDIAN_EXCLUSIVE_ARMS_OTHER_P, DRAW_PATH, \ - PCR_THREE_P, PCR_TWO_P, PCR_ONE_P, AZUR_FOUR_P, AZUR_THREE_P, AZUR_TWO_P, AZUR_ONE_P, AZUR_FIVE_P, FGO_CARD_FIVE_P,\ - FGO_CARD_FOUR_P, FGO_CARD_THREE_P, FGO_SERVANT_THREE_P, FGO_SERVANT_FOUR_P, FGO_SERVANT_FIVE_P, ONMYOJI_R, ONMYOJI_SP, \ - ONMYOJI_SSR, ONMYOJI_SR + global PRTS_SIX_P, PRTS_FOUR_P, PRTS_FIVE_P, PRTS_THREE_P, GENSHIN_G_FIVE_P, config_default_data, GENSHIN_G_FOUR_P, GENSHIN_FOUR_P, GENSHIN_FIVE_P, I72_ADD, path_dict, PRETTY_THREE_P, PRETTY_ONE_P, PRETTY_TWO_P, GENSHIN_THREE_P, GUARDIAN_THREE_CHAR_P, GUARDIAN_TWO_CHAR_P, GUARDIAN_ONE_CHAR_P, GUARDIAN_THREE_CHAR_UP_P, GUARDIAN_THREE_CHAR_OTHER_P, GUARDIAN_EXCLUSIVE_ARMS_P, GUARDIAN_FIVE_ARMS_P, GUARDIAN_FOUR_ARMS_P, GUARDIAN_THREE_ARMS_P, GUARDIAN_TWO_ARMS_P, GENSHIN_FLAG, PRTS_FLAG, PRETTY_FLAG, GUARDIAN_FLAG, GUARDIAN_EXCLUSIVE_ARMS_UP_P, GUARDIAN_EXCLUSIVE_ARMS_OTHER_P, DRAW_PATH, PCR_THREE_P, PCR_TWO_P, PCR_ONE_P, AZUR_FOUR_P, AZUR_THREE_P, AZUR_TWO_P, AZUR_ONE_P, AZUR_FIVE_P, FGO_CARD_FIVE_P, FGO_CARD_FOUR_P, FGO_CARD_THREE_P, FGO_SERVANT_THREE_P, FGO_SERVANT_FOUR_P, FGO_SERVANT_FIVE_P, ONMYOJI_R, ONMYOJI_SP, ONMYOJI_SSR, ONMYOJI_SR _draw_config.parent.mkdir(parents=True, exist_ok=True) try: - data = json.load(open(_draw_config, 'r', encoding='utf8')) + data = json.load(open(_draw_config, "r", encoding="utf8")) except (FileNotFoundError, ValueError): _draw_config.parent.mkdir(parents=True, exist_ok=True) - json.dump(config_default_data, open(_draw_config, 'w', encoding='utf8'), indent=4, ensure_ascii=False) - print('draw_card:配置文件不存在或格式错误,已重新生成配置文件.....') + json.dump( + config_default_data, + open(_draw_config, "w", encoding="utf8"), + indent=4, + ensure_ascii=False, + ) + print("draw_card:配置文件不存在或格式错误,已重新生成配置文件.....") else: try: - PRTS_SIX_P = float(data['prts']['PRTS_SIX_P']) - PRTS_FIVE_P = float(data['prts']['PRTS_FIVE_P']) - PRTS_FOUR_P = float(data['prts']['PRTS_FOUR_P']) - PRTS_THREE_P = float(data['prts']['PRTS_THREE_P']) + PRTS_SIX_P = float(data["prts"]["PRTS_SIX_P"]) + PRTS_FIVE_P = float(data["prts"]["PRTS_FIVE_P"]) + PRTS_FOUR_P = float(data["prts"]["PRTS_FOUR_P"]) + PRTS_THREE_P = float(data["prts"]["PRTS_THREE_P"]) except KeyError: - data['prts'] = {} - data['prts']['PRTS_SIX_P'] = config_default_data['prts']['PRTS_SIX_P'] - data['prts']['PRTS_FIVE_P'] = config_default_data['prts']['PRTS_FIVE_P'] - data['prts']['PRTS_FOUR_P'] = config_default_data['prts']['PRTS_FOUR_P'] - data['prts']['PRTS_THREE_P'] = config_default_data['prts']['PRTS_THREE_P'] + data["prts"] = {} + data["prts"]["PRTS_SIX_P"] = config_default_data["prts"]["PRTS_SIX_P"] + data["prts"]["PRTS_FIVE_P"] = config_default_data["prts"]["PRTS_FIVE_P"] + data["prts"]["PRTS_FOUR_P"] = config_default_data["prts"]["PRTS_FOUR_P"] + data["prts"]["PRTS_THREE_P"] = config_default_data["prts"]["PRTS_THREE_P"] try: - GENSHIN_FIVE_P = float(data['genshin']['GENSHIN_FIVE_P']) - GENSHIN_FOUR_P = float(data['genshin']['GENSHIN_FOUR_P']) - GENSHIN_THREE_P = float(data['genshin']['GENSHIN_THREE_P']) - GENSHIN_G_FIVE_P = float(data['genshin']['GENSHIN_G_FIVE_P']) - GENSHIN_G_FOUR_P = float(data['genshin']['GENSHIN_G_FOUR_P']) - I72_ADD = float(data['genshin']['I72_ADD']) + GENSHIN_FIVE_P = float(data["genshin"]["GENSHIN_FIVE_P"]) + GENSHIN_FOUR_P = float(data["genshin"]["GENSHIN_FOUR_P"]) + GENSHIN_THREE_P = float(data["genshin"]["GENSHIN_THREE_P"]) + GENSHIN_G_FIVE_P = float(data["genshin"]["GENSHIN_G_FIVE_P"]) + GENSHIN_G_FOUR_P = float(data["genshin"]["GENSHIN_G_FOUR_P"]) + I72_ADD = float(data["genshin"]["I72_ADD"]) except KeyError: - data['genshin'] = {} - data['genshin']['GENSHIN_FIVE_P'] = config_default_data['genshin']['GENSHIN_FIVE_P'] - data['genshin']['GENSHIN_FOUR_P'] = config_default_data['genshin']['GENSHIN_FOUR_P'] - data['genshin']['GENSHIN_THREE_P'] = config_default_data['genshin']['GENSHIN_THREE_P'] - data['genshin']['GENSHIN_G_FIVE_P'] = config_default_data['genshin']['GENSHIN_G_FIVE_P'] - data['genshin']['GENSHIN_G_FOUR_P'] = config_default_data['genshin']['GENSHIN_G_FOUR_P'] - data['genshin']['I72_ADD'] = config_default_data['genshin']['I72_ADD'] + data["genshin"] = {} + data["genshin"]["GENSHIN_FIVE_P"] = config_default_data["genshin"][ + "GENSHIN_FIVE_P" + ] + data["genshin"]["GENSHIN_FOUR_P"] = config_default_data["genshin"][ + "GENSHIN_FOUR_P" + ] + data["genshin"]["GENSHIN_THREE_P"] = config_default_data["genshin"][ + "GENSHIN_THREE_P" + ] + data["genshin"]["GENSHIN_G_FIVE_P"] = config_default_data["genshin"][ + "GENSHIN_G_FIVE_P" + ] + data["genshin"]["GENSHIN_G_FOUR_P"] = config_default_data["genshin"][ + "GENSHIN_G_FOUR_P" + ] + data["genshin"]["I72_ADD"] = config_default_data["genshin"]["I72_ADD"] try: - PRETTY_THREE_P = float(data['pretty']['PRETTY_THREE_P']) - PRETTY_TWO_P = float(data['pretty']['PRETTY_TWO_P']) - PRETTY_ONE_P = float(data['pretty']['PRETTY_ONE_P']) + PRETTY_THREE_P = float(data["pretty"]["PRETTY_THREE_P"]) + PRETTY_TWO_P = float(data["pretty"]["PRETTY_TWO_P"]) + PRETTY_ONE_P = float(data["pretty"]["PRETTY_ONE_P"]) except KeyError: - data['pretty'] = {} - data['pretty']['PRETTY_THREE_P'] = config_default_data['pretty']['PRETTY_THREE_P'] - data['pretty']['PRETTY_TWO_P'] = config_default_data['pretty']['PRETTY_TWO_P'] - data['pretty']['PRETTY_ONE_P'] = config_default_data['pretty']['PRETTY_ONE_P'] + data["pretty"] = {} + data["pretty"]["PRETTY_THREE_P"] = config_default_data["pretty"][ + "PRETTY_THREE_P" + ] + data["pretty"]["PRETTY_TWO_P"] = config_default_data["pretty"][ + "PRETTY_TWO_P" + ] + data["pretty"]["PRETTY_ONE_P"] = config_default_data["pretty"][ + "PRETTY_ONE_P" + ] try: - GUARDIAN_THREE_CHAR_P = float(data['guardian']['GUARDIAN_THREE_CHAR_P']) - GUARDIAN_TWO_CHAR_P = float(data['guardian']['GUARDIAN_TWO_CHAR_P']) - GUARDIAN_ONE_CHAR_P = float(data['guardian']['GUARDIAN_ONE_CHAR_P']) - GUARDIAN_THREE_CHAR_UP_P = float(data['guardian']['GUARDIAN_THREE_CHAR_UP_P']) - GUARDIAN_THREE_CHAR_OTHER_P = float(data['guardian']['GUARDIAN_THREE_CHAR_OTHER_P']) - GUARDIAN_EXCLUSIVE_ARMS_P = float(data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_P']) - GUARDIAN_FIVE_ARMS_P = float(data['guardian']['GUARDIAN_FIVE_ARMS_P']) - GUARDIAN_FOUR_ARMS_P = float(data['guardian']['GUARDIAN_FOUR_ARMS_P']) - GUARDIAN_THREE_ARMS_P = float(data['guardian']['GUARDIAN_THREE_ARMS_P']) - GUARDIAN_TWO_ARMS_P = float(data['guardian']['GUARDIAN_TWO_ARMS_P']) - GUARDIAN_EXCLUSIVE_ARMS_UP_P = float(data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_UP_P']) - GUARDIAN_EXCLUSIVE_ARMS_OTHER_P = float(data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_OTHER_P']) + GUARDIAN_THREE_CHAR_P = float(data["guardian"]["GUARDIAN_THREE_CHAR_P"]) + GUARDIAN_TWO_CHAR_P = float(data["guardian"]["GUARDIAN_TWO_CHAR_P"]) + GUARDIAN_ONE_CHAR_P = float(data["guardian"]["GUARDIAN_ONE_CHAR_P"]) + GUARDIAN_THREE_CHAR_UP_P = float( + data["guardian"]["GUARDIAN_THREE_CHAR_UP_P"] + ) + GUARDIAN_THREE_CHAR_OTHER_P = float( + data["guardian"]["GUARDIAN_THREE_CHAR_OTHER_P"] + ) + GUARDIAN_EXCLUSIVE_ARMS_P = float( + data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_P"] + ) + GUARDIAN_FIVE_ARMS_P = float(data["guardian"]["GUARDIAN_FIVE_ARMS_P"]) + GUARDIAN_FOUR_ARMS_P = float(data["guardian"]["GUARDIAN_FOUR_ARMS_P"]) + GUARDIAN_THREE_ARMS_P = float(data["guardian"]["GUARDIAN_THREE_ARMS_P"]) + GUARDIAN_TWO_ARMS_P = float(data["guardian"]["GUARDIAN_TWO_ARMS_P"]) + GUARDIAN_EXCLUSIVE_ARMS_UP_P = float( + data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_UP_P"] + ) + GUARDIAN_EXCLUSIVE_ARMS_OTHER_P = float( + data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_OTHER_P"] + ) except KeyError: - data['guardian'] = {} - data['guardian']['GUARDIAN_THREE_CHAR_P'] = config_default_data['guardian']['GUARDIAN_THREE_CHAR_P'] - data['guardian']['GUARDIAN_TWO_CHAR_P'] = config_default_data['guardian']['GUARDIAN_TWO_CHAR_P'] - data['guardian']['GUARDIAN_ONE_CHAR_P'] = config_default_data['guardian']['GUARDIAN_ONE_CHAR_P'] - data['guardian']['GUARDIAN_THREE_CHAR_UP_P'] = config_default_data['guardian']['GUARDIAN_THREE_CHAR_UP_P'] - data['guardian']['GUARDIAN_THREE_CHAR_OTHER_P'] = config_default_data['guardian']['GUARDIAN_THREE_CHAR_OTHER_P'] - data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_P'] = config_default_data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_P'] - data['guardian']['GUARDIAN_FIVE_ARMS_P'] = config_default_data['guardian']['GUARDIAN_FIVE_ARMS_P'] - data['guardian']['GUARDIAN_FOUR_ARMS_P'] = config_default_data['guardian']['GUARDIAN_FOUR_ARMS_P'] - data['guardian']['GUARDIAN_THREE_ARMS_P'] = config_default_data['guardian']['GUARDIAN_THREE_ARMS_P'] - data['guardian']['GUARDIAN_TWO_ARMS_P'] = config_default_data['guardian']['GUARDIAN_TWO_ARMS_P'] - data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_UP_P'] = config_default_data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_UP_P'] - data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_OTHER_P'] = config_default_data['guardian']['GUARDIAN_EXCLUSIVE_ARMS_OTHER_P'] + data["guardian"] = {} + data["guardian"]["GUARDIAN_THREE_CHAR_P"] = config_default_data["guardian"][ + "GUARDIAN_THREE_CHAR_P" + ] + data["guardian"]["GUARDIAN_TWO_CHAR_P"] = config_default_data["guardian"][ + "GUARDIAN_TWO_CHAR_P" + ] + data["guardian"]["GUARDIAN_ONE_CHAR_P"] = config_default_data["guardian"][ + "GUARDIAN_ONE_CHAR_P" + ] + data["guardian"]["GUARDIAN_THREE_CHAR_UP_P"] = config_default_data[ + "guardian" + ]["GUARDIAN_THREE_CHAR_UP_P"] + data["guardian"]["GUARDIAN_THREE_CHAR_OTHER_P"] = config_default_data[ + "guardian" + ]["GUARDIAN_THREE_CHAR_OTHER_P"] + data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_P"] = config_default_data[ + "guardian" + ]["GUARDIAN_EXCLUSIVE_ARMS_P"] + data["guardian"]["GUARDIAN_FIVE_ARMS_P"] = config_default_data["guardian"][ + "GUARDIAN_FIVE_ARMS_P" + ] + data["guardian"]["GUARDIAN_FOUR_ARMS_P"] = config_default_data["guardian"][ + "GUARDIAN_FOUR_ARMS_P" + ] + data["guardian"]["GUARDIAN_THREE_ARMS_P"] = config_default_data["guardian"][ + "GUARDIAN_THREE_ARMS_P" + ] + data["guardian"]["GUARDIAN_TWO_ARMS_P"] = config_default_data["guardian"][ + "GUARDIAN_TWO_ARMS_P" + ] + data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_UP_P"] = config_default_data[ + "guardian" + ]["GUARDIAN_EXCLUSIVE_ARMS_UP_P"] + data["guardian"]["GUARDIAN_EXCLUSIVE_ARMS_OTHER_P"] = config_default_data[ + "guardian" + ]["GUARDIAN_EXCLUSIVE_ARMS_OTHER_P"] try: - PCR_THREE_P = float(data['pcr']['PCR_THREE_P']) - PCR_TWO_P = float(data['pcr']['PCR_TWO_P']) - PCR_ONE_P = float(data['pcr']['PCR_ONE_P']) + PCR_THREE_P = float(data["pcr"]["PCR_THREE_P"]) + PCR_TWO_P = float(data["pcr"]["PCR_TWO_P"]) + PCR_ONE_P = float(data["pcr"]["PCR_ONE_P"]) except KeyError: - data['pcr'] = {} - data['pcr']['PCR_THREE_P'] = config_default_data['pcr']['PCR_THREE_P'] - data['pcr']['PCR_TWO_P'] = config_default_data['pcr']['PCR_TWO_P'] - data['pcr']['PCR_ONE_P'] = config_default_data['pcr']['PCR_ONE_P'] + data["pcr"] = {} + data["pcr"]["PCR_THREE_P"] = config_default_data["pcr"]["PCR_THREE_P"] + data["pcr"]["PCR_TWO_P"] = config_default_data["pcr"]["PCR_TWO_P"] + data["pcr"]["PCR_ONE_P"] = config_default_data["pcr"]["PCR_ONE_P"] try: - AZUR_FIVE_P = float(data['azur']['AZUR_FIVE_P']) - AZUR_FOUR_P = float(data['azur']['AZUR_FOUR_P']) - AZUR_THREE_P = float(data['azur']['AZUR_THREE_P']) - AZUR_TWO_P = float(data['azur']['AZUR_TWO_P']) - AZUR_ONE_P = float(data['azur']['AZUR_ONE_P']) + AZUR_FIVE_P = float(data["azur"]["AZUR_FIVE_P"]) + AZUR_FOUR_P = float(data["azur"]["AZUR_FOUR_P"]) + AZUR_THREE_P = float(data["azur"]["AZUR_THREE_P"]) + AZUR_TWO_P = float(data["azur"]["AZUR_TWO_P"]) + AZUR_ONE_P = float(data["azur"]["AZUR_ONE_P"]) except KeyError: - data['azur'] = {} - data['azur']['AZUR_FIVE_P'] = config_default_data['azur']['AZUR_FIVE_P'] - data['azur']['AZUR_FOUR_P'] = config_default_data['azur']['AZUR_FOUR_P'] - data['azur']['AZUR_THREE_P'] = config_default_data['azur']['AZUR_THREE_P'] - data['azur']['AZUR_TWO_P'] = config_default_data['azur']['AZUR_TWO_P'] - data['azur']['AZUR_ONE_P'] = config_default_data['azur']['AZUR_ONE_P'] + data["azur"] = {} + data["azur"]["AZUR_FIVE_P"] = config_default_data["azur"]["AZUR_FIVE_P"] + data["azur"]["AZUR_FOUR_P"] = config_default_data["azur"]["AZUR_FOUR_P"] + data["azur"]["AZUR_THREE_P"] = config_default_data["azur"]["AZUR_THREE_P"] + data["azur"]["AZUR_TWO_P"] = config_default_data["azur"]["AZUR_TWO_P"] + data["azur"]["AZUR_ONE_P"] = config_default_data["azur"]["AZUR_ONE_P"] try: - FGO_SERVANT_FIVE_P = float(data['fgo']['FGO_SERVANT_FIVE_P']) - FGO_SERVANT_FOUR_P = float(data['fgo']['FGO_SERVANT_FOUR_P']) - FGO_SERVANT_THREE_P = float(data['fgo']['FGO_SERVANT_THREE_P']) - FGO_CARD_FIVE_P = float(data['fgo']['FGO_CARD_FIVE_P']) - FGO_CARD_FOUR_P = float(data['fgo']['FGO_CARD_FOUR_P']) - FGO_CARD_THREE_P = float(data['fgo']['FGO_CARD_THREE_P']) + FGO_SERVANT_FIVE_P = float(data["fgo"]["FGO_SERVANT_FIVE_P"]) + FGO_SERVANT_FOUR_P = float(data["fgo"]["FGO_SERVANT_FOUR_P"]) + FGO_SERVANT_THREE_P = float(data["fgo"]["FGO_SERVANT_THREE_P"]) + FGO_CARD_FIVE_P = float(data["fgo"]["FGO_CARD_FIVE_P"]) + FGO_CARD_FOUR_P = float(data["fgo"]["FGO_CARD_FOUR_P"]) + FGO_CARD_THREE_P = float(data["fgo"]["FGO_CARD_THREE_P"]) except KeyError: - data['fgo'] = {} - data['fgo']['FGO_SERVANT_FIVE_P'] = config_default_data['fgo']['FGO_SERVANT_FIVE_P'] - data['fgo']['FGO_SERVANT_FOUR_P'] = config_default_data['fgo']['FGO_SERVANT_FOUR_P'] - data['fgo']['FGO_SERVANT_THREE_P'] = config_default_data['fgo']['FGO_SERVANT_THREE_P'] - data['fgo']['FGO_CARD_FIVE_P'] = config_default_data['fgo']['FGO_CARD_FIVE_P'] - data['fgo']['FGO_CARD_FOUR_P'] = config_default_data['fgo']['FGO_CARD_FOUR_P'] - data['fgo']['FGO_CARD_THREE_P'] = config_default_data['fgo']['FGO_CARD_THREE_P'] + data["fgo"] = {} + data["fgo"]["FGO_SERVANT_FIVE_P"] = config_default_data["fgo"][ + "FGO_SERVANT_FIVE_P" + ] + data["fgo"]["FGO_SERVANT_FOUR_P"] = config_default_data["fgo"][ + "FGO_SERVANT_FOUR_P" + ] + data["fgo"]["FGO_SERVANT_THREE_P"] = config_default_data["fgo"][ + "FGO_SERVANT_THREE_P" + ] + data["fgo"]["FGO_CARD_FIVE_P"] = config_default_data["fgo"][ + "FGO_CARD_FIVE_P" + ] + data["fgo"]["FGO_CARD_FOUR_P"] = config_default_data["fgo"][ + "FGO_CARD_FOUR_P" + ] + data["fgo"]["FGO_CARD_THREE_P"] = config_default_data["fgo"][ + "FGO_CARD_THREE_P" + ] try: - ONMYOJI_SP = float(data['onmyoji']['ONMYOJI_SP']) - ONMYOJI_SSR = float(data['onmyoji']['ONMYOJI_SSR']) - ONMYOJI_SR = float(data['onmyoji']['ONMYOJI_SR']) - ONMYOJI_R = float(data['onmyoji']['ONMYOJI_R']) + ONMYOJI_SP = float(data["onmyoji"]["ONMYOJI_SP"]) + ONMYOJI_SSR = float(data["onmyoji"]["ONMYOJI_SSR"]) + ONMYOJI_SR = float(data["onmyoji"]["ONMYOJI_SR"]) + ONMYOJI_R = float(data["onmyoji"]["ONMYOJI_R"]) except KeyError: - data['onmyoji'] = {} - data['onmyoji']['ONMYOJI_SP'] = config_default_data['onmyoji']['ONMYOJI_SP'] - data['onmyoji']['ONMYOJI_SSR'] = config_default_data['onmyoji']['ONMYOJI_SSR'] - data['onmyoji']['ONMYOJI_SR'] = config_default_data['onmyoji']['ONMYOJI_SR'] - data['onmyoji']['ONMYOJI_R'] = config_default_data['onmyoji']['ONMYOJI_R'] - - json.dump(data, open(_draw_config, 'w', encoding='utf8'), indent=4, ensure_ascii=False) - - - - - + data["onmyoji"] = {} + data["onmyoji"]["ONMYOJI_SP"] = config_default_data["onmyoji"]["ONMYOJI_SP"] + data["onmyoji"]["ONMYOJI_SSR"] = config_default_data["onmyoji"][ + "ONMYOJI_SSR" + ] + data["onmyoji"]["ONMYOJI_SR"] = config_default_data["onmyoji"]["ONMYOJI_SR"] + data["onmyoji"]["ONMYOJI_R"] = config_default_data["onmyoji"]["ONMYOJI_R"] + + json.dump( + data, open(_draw_config, "w", encoding="utf8"), indent=4, ensure_ascii=False + ) diff --git a/plugins/fudu.py b/plugins/fudu.py index 1c36f524b..56423c4ce 100644 --- a/plugins/fudu.py +++ b/plugins/fudu.py @@ -9,9 +9,9 @@ from nonebot.adapters.cqhttp import Bot, GroupMessageEvent import aiohttp import aiofiles -from asyncio.exceptions import TimeoutError -from configs.config import FUDU_PROBABILITY +from configs.config import Config from utils.manager import group_manager +from services.log import logger __zx_plugin_name__ = "复读" @@ -24,6 +24,9 @@ __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_task__ = {"fudu": "复读"} +__plugin_configs__ = { + "FUDU_PROBABILITY": {"value": 0.7, "help": "复读概率", "default_value": 0.7} +} class Fudu: @@ -98,9 +101,9 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): _fudu_list.clear(event.group_id) _fudu_list.append(event.group_id, add_msg) if _fudu_list.size(event.group_id) > 2: - if random.random() < FUDU_PROBABILITY and not _fudu_list.is_repeater( - event.group_id - ): + if random.random() < Config.get_config( + "fudu", "FUDU_PROBABILITY" + ) and not _fudu_list.is_repeater(event.group_id): if random.random() < 0.2: await fudu.finish("打断施法!") _fudu_list.set_repeater(event.group_id) @@ -126,5 +129,6 @@ async def get_fudu_img_hash(url, group_id): await f.write(await response.read()) img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg") return str(img_hash) - except TimeoutError: + except Exception as e: + logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}") return "" diff --git a/plugins/hook.py b/plugins/hook.py deleted file mode 100644 index ac244d6ec..000000000 --- a/plugins/hook.py +++ /dev/null @@ -1,467 +0,0 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException -from nonebot.adapters.cqhttp.exception import ActionFailed -from models.group_member_info import GroupInfoUser -from utils.manager import ( - plugins2cd_manager, - plugins2block_manager, - plugins2settings_manager, - admin_manager -) -from models.friend_user import FriendUser -from typing import Optional -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - Event, - MessageEvent, - PrivateMessageEvent, - GroupMessageEvent, - PokeNotifyEvent, - Message, -) -from configs.config import ( - BAN_RESULT, - MALICIOUS_BAN_TIME, - MALICIOUS_CHECK_TIME, - MALICIOUS_BAN_COUNT, - CHECK_NOTICE_INFO_CD, -) -from models.ban_user import BanUser -from utils.utils import ( - is_number, - static_flmt, - BanCheckLimiter, - FreqLimiter, -) -from utils.manager import withdraw_message_manager -from utils.message_builder import at -from services.log import logger -from models.level_user import LevelUser -from utils.manager import group_manager -import asyncio - -try: - import ujson as json -except ModuleNotFoundError: - import json - - -# 检查是否被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: - 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: - await bot.send_private_msg( - user_id=event.user_id, - message=at(event.user_id) - + BAN_RESULT - + f" 在..在 {time}后才会理你喔", - ) - except ActionFailed: - pass - raise IgnoredException("用户处于黑名单中") - - -_blmt = BanCheckLimiter(MALICIOUS_CHECK_TIME, 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, 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"]}') - - -_flmt = FreqLimiter(CHECK_NOTICE_INFO_CD) -_flmt_g = FreqLimiter(CHECK_NOTICE_INFO_CD) -_flmt_s = FreqLimiter(CHECK_NOTICE_INFO_CD) -_flmt_c = FreqLimiter(CHECK_NOTICE_INFO_CD) -_exists_msg = {} - - -ignore_rst_module = ["ai", "poke"] - - -# 权限检测 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - global _exists_msg - 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) - ): - 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 - 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 - 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 _exists_msg.get(event.group_id) is None: - _exists_msg[event.group_id] = False - # 群权限 - 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 - _exists_msg[event.group_id] = True - 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 - _exists_msg[event.group_id] = True - print(123123) - 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 - _exists_msg[event.group_id] = True - raise IgnoredException("管理员禁用了此群该功能...") - # 群聊禁用 - if not group_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 - _exists_msg[event.group_id] = True - raise IgnoredException("该插件在群聊中已被禁用...") - else: - # 私聊禁用 - if not group_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 - raise IgnoredException("该插件在私聊中已被禁用...") - # 维护 - if not group_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 isinstance(event, GroupMessageEvent): - _exists_msg[event.group_id] = True - raise IgnoredException("此功能正在维护...") - - -# 命令cd 和 命令阻塞 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - global _exists_msg - if not isinstance(event, MessageEvent) and matcher.module != "poke": - return - module = matcher.module - if isinstance(event, GroupMessageEvent) and _exists_msg.get(event.group_id) is None: - _exists_msg[event.group_id] = False - 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_) - 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 - """ - global _exists_msg - rst = await init_rst(rst, event) - try: - if isinstance(event, GroupMessageEvent): - _exists_msg[event.group_id] = True - await bot.send_group_msg(group_id=event.group_id, message=Message(rst)) - else: - _exists_msg[event.user_id] = True - await bot.send_private_msg(user_id=event.user_id, message=Message(rst)) - except ActionFailed: - pass - - -@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 - 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) - - -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 - - -# 消息撤回 -@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)) - - -# 为什么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): - global _exists_msg - if not isinstance(event, MessageEvent): - return - if matcher.type == "message": - if matcher.module == "ai": - if ( - isinstance(event, GroupMessageEvent) - and _exists_msg.get(event.group_id) is True - ): - _exists_msg[event.group_id] = False - raise IgnoredException("有命令就别说话了") - elif ( - isinstance(event, PrivateMessageEvent) - and _exists_msg.get(event.user_id) is True - ): - _exists_msg[event.user_id] = False - raise IgnoredException("有命令就别说话了") diff --git a/plugins/image_management/__init__.py b/plugins/image_management/__init__.py index 605319292..1e5ef28dc 100644 --- a/plugins/image_management/__init__.py +++ b/plugins/image_management/__init__.py @@ -1,3 +1,48 @@ +from configs.config import Config import nonebot + +Config.add_plugin_config( + "image_management", + "IMAGE_DIR_LIST", + ["美图", "萝莉", "壁纸"], + name="图库操作", + help_="公开图库列表,可自定义添加 [如果含有send_setu插件,请不要添加色图库]", + default_value=[], +) + +Config.add_plugin_config( + "image_management", + "WITHDRAW_IMAGE_MESSAGE", + (0, 1), + name="图库操作", + help_="自动撤回,参1:延迟撤回发送图库图片的时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)", + default_value=(0, 1), +) + +Config.add_plugin_config( + "image_management:delete_img", + "DELETE_IMAGE_LEVEL [LEVEL]", + 7, + help_="删除图库图片需要的管理员等级", + default_value=7, +) + +Config.add_plugin_config( + "image_management:move_img", + "MOVE_IMAGE_LEVEL [LEVEL]", + 7, + help_="移动图库图片需要的管理员等级", + default_value=7, +) + +Config.add_plugin_config( + "image_management:upload_img", + "UPLOAD_IMAGE_LEVEL [LEVEL]", + 6, + help_="上传图库图片需要的管理员等级", + default_value=6, +) + + nonebot.load_plugins("plugins/image_management") diff --git a/plugins/image_management/delete_img/__init__.py b/plugins/image_management/delete_img/__init__.py index e3e85ff94..e7d9b70c9 100644 --- a/plugins/image_management/delete_img/__init__.py +++ b/plugins/image_management/delete_img/__init__.py @@ -5,9 +5,8 @@ from nonebot.rule import to_me from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from configs.config import DELETE_IMG_LEVEL -from configs.config import IMAGE_DIR_LIST from utils.utils import is_number, cn2py, get_message_text +from configs.config import Config from pathlib import Path import os @@ -25,7 +24,7 @@ __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { - "admin_level": DELETE_IMG_LEVEL + "admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL") } @@ -37,7 +36,7 @@ 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 IMAGE_DIR_LIST: + 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": @@ -53,7 +52,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): args = raw_arg.split(" ") if args[0] in ["帮助"]: await delete_img.finish(__plugin_usage__) - if len(args) >= 2 and args[0] in IMAGE_DIR_LIST and is_number(args[1]): + 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] diff --git a/plugins/image_management/move_img/__init__.py b/plugins/image_management/move_img/__init__.py index f541e8837..90a177f2f 100644 --- a/plugins/image_management/move_img/__init__.py +++ b/plugins/image_management/move_img/__init__.py @@ -4,7 +4,7 @@ from nonebot.rule import to_me from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from configs.config import IMAGE_DIR_LIST, MOVE_IMG_LEVEL +from configs.config import Config from utils.utils import is_number, cn2py from configs.path_config import IMAGE_PATH from pathlib import Path @@ -24,7 +24,7 @@ __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { - "admin_level": MOVE_IMG_LEVEL + "admin_level": Config.get_config("image_management", "MOVE_IMAGE_LEVEL") } @@ -36,7 +36,9 @@ 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 IMAGE_DIR_LIST: + 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": @@ -54,8 +56,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await move_img.finish(__plugin_usage__) if ( len(args) >= 3 - and args[0] in IMAGE_DIR_LIST - and args[1] in IMAGE_DIR_LIST + 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] @@ -78,9 +80,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): 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" - ) + 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} 移动成功" ) diff --git a/plugins/send_img/__init__.py b/plugins/image_management/send_img/__init__.py similarity index 73% rename from plugins/send_img/__init__.py rename to plugins/image_management/send_img/__init__.py index 548b29845..57f9e1f9e 100644 --- a/plugins/send_img/__init__.py +++ b/plugins/image_management/send_img/__init__.py @@ -1,135 +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 -import os -import random -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 IMAGE_DIR_LIST -from utils.manager import group_manager - -try: - import ujson as json -except ModuleNotFoundError: - import json - -if "色图" in IMAGE_DIR_LIST: - IMAGE_DIR_LIST.remove("色图") - -__zx_plugin_name__ = "发送本地图库图片" -__plugin_usage__ = f""" -usage: - 发送指定图库下的随机或指定id图片 - 指令: - {IMAGE_DIR_LIST} ?[id] - 示例:美图 - 示例: 萝莉 2 -""".strip() -__plugin_des__ = "让看看我的私藏,指[图片]" -__plugin_cmd__ = IMAGE_DIR_LIST -__plugin_type__ = ("来点好康的",) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["发送图片"] + IMAGE_DIR_LIST, -} -__plugin_task__ = {"pa": "丢人爬"} - -_flmt = FreqLimiter(1) - -cmd = set(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 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)) - 1 - if length < 1: - await send_img.finish("该图库中没有图片噢") - logger.warning(f"图库 {path} 为空,调用取消!") - return - index = img_id if img_id else str(random.randint(0, length)) - if not is_number(index): - await send_img.finish("id错误!") - if int(index) > length 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 - ) - await send_img.finish(f"id:{index}" + result) - 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")) - - -num_key = { - "一": 1, - "二": 2, - "两": 2, - "双": 2, - "三": 3, - "四": 4, - "五": 5, - "六": 6, - "七": 7, - "八": 8, - "九": 9, -} +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_img/__init__.py index 082b01849..cc2f59083 100644 --- a/plugins/image_management/upload_img/__init__.py +++ b/plugins/image_management/upload_img/__init__.py @@ -3,7 +3,7 @@ from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent -from configs.config import IMAGE_DIR_LIST, UPLOAD_IMG_LEVEL +from configs.config import Config from utils.utils import get_message_imgs, get_message_text from .data_source import upload_image_to_local @@ -23,7 +23,7 @@ __plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"] __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" -__plugin_settings__ = {"admin_level": UPLOAD_IMG_LEVEL} +__plugin_settings__ = {"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")} upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True) @@ -35,7 +35,7 @@ @show_gallery.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): x = '公开图库列表:\n' - for i, e in enumerate(IMAGE_DIR_LIST): + 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]) @@ -46,7 +46,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if msg in ["取消", "算了"]: await upload_img.finish("已取消操作..", at_sender=True) if state["_current_key"] in ["path"]: - if msg not in IMAGE_DIR_LIST: + 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"]: @@ -60,7 +60,7 @@ 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 IMAGE_DIR_LIST: + if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"): state["path"] = raw_arg if img_list: state["imgs"] = img_list @@ -88,7 +88,7 @@ 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 IMAGE_DIR_LIST: + 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: @@ -104,7 +104,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @continuous_upload_img.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): path = get_message_imgs(event.json()) - if path in IMAGE_DIR_LIST: + if path in Config.get_config("image_management", "IMAGE_DIR_LIST"): state["path"] = path await continuous_upload_img.send("图来!!") state["tmp"] = [] diff --git a/plugins/init_config/__init__.py b/plugins/init_config/__init__.py deleted file mode 100644 index 926c6f5ce..000000000 --- a/plugins/init_config/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -from .data_source import ( - init_plugins_settings, - init_plugins_cd_limit, - init_plugins_block_limit, - init_group_manager, -) -from nonebot.adapters.cqhttp import Bot -from configs.path_config import DATA_PATH -from services.log import logger -from nonebot import Driver -import nonebot - - -__zx_plugin_name__ = "初始化插件数据 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -driver: Driver = nonebot.get_driver() - - -@driver.on_startup -def _(): - """ - 初始化数据 - """ - init_plugins_settings(DATA_PATH) - init_plugins_cd_limit(DATA_PATH) - init_plugins_block_limit(DATA_PATH) - logger.info("初始化数据完成...") - - -@driver.on_bot_connect -async def _(bot: Bot): - await init_group_manager() - diff --git a/plugins/init_config/data_source.py b/plugins/init_config/data_source.py deleted file mode 100644 index b804d557b..000000000 --- a/plugins/init_config/data_source.py +++ /dev/null @@ -1,289 +0,0 @@ -from pathlib import Path -from ruamel.yaml import YAML, round_trip_load, round_trip_dump -from utils.manager import ( - plugins2settings_manager, - plugins2cd_manager, - plugins2block_manager, - group_manager, - admin_manager -) -from services.db_context import db -from asyncpg.exceptions import DuplicateColumnError -from services.log import logger -from utils.utils import get_matchers -import nonebot - -try: - import ujson as json -except ModuleNotFoundError: - import json -try: - from models.group_remind import GroupRemind -except ModuleNotFoundError: - pass - -yaml = YAML(typ="safe") - - -def init_plugins_settings(data_path: str): - """ - 初始化插件设置,从插件中获取 __zx_plugin_name__,__plugin_cmd__,__plugin_settings__ - """ - plugins2config_file = Path(data_path) / "configs" / "plugins2settings.yaml" - plugins2config_file.parent.mkdir(exist_ok=True, parents=True) - _matchers = get_matchers() - _data = {} - if plugins2config_file.exists(): - with open(plugins2config_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) - _module = _plugin.module - try: - plugin_name = _module.__getattribute__("__zx_plugin_name__") - if "[admin]" in plugin_name.lower(): - try: - level = (_module.__getattribute__("__plugin_settings__"))['admin_level'] - except (AttributeError, KeyError): - level = 5 - admin_manager.add_admin_command(matcher.module, 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(plugins2config_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf) - _data = round_trip_load(open(plugins2config_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(plugins2config_file, "w", encoding="utf8") as wf: - round_trip_dump(_data, wf) - logger.info(f"已成功加载 {len(plugins2settings_manager.get_data())} 个非限制插件.") - - -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) - _module = _plugin.module - try: - plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__") - plugins2cd_manager.add_cd_limit( - matcher.module, data_dict=plugin_cd_limit - ) - except AttributeError: - pass - if plugins2cd_file.exists(): - with open(plugins2cd_file, "r", encoding="utf8") as f: - _data = yaml.load(f) - _data = _data if _data else {} - if _data.get("PluginCdLimit"): - for plugin in _data["PluginCdLimit"].keys(): - plugins2cd_manager.add_cd_limit( - plugin, data_dict=_data["PluginCdLimit"][plugin] - ) - _tmp_data = {"PluginCdLimit": plugins2cd_manager.get_data()} - with open(plugins2cd_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf) - _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) - 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) - _module = _plugin.module - try: - plugin_block_limit = _module.__getattribute__("__plugin_block_limit__") - plugins2block_manager.add_block_limit( - matcher.module, data_dict=plugin_block_limit - ) - except AttributeError: - pass - if plugins2block_file.exists(): - with open(plugins2block_file, "r", encoding="utf8") as f: - _data = yaml.load(f) - _data = _data if _data else {} - if _data.get("PluginBlockLimit"): - for plugin in _data["PluginBlockLimit"].keys(): - plugins2block_manager.add_block_limit( - plugin, data_dict=_data["PluginBlockLimit"][plugin] - ) - _tmp_data = {"PluginBlockLimit": plugins2block_manager.get_data()} - with open(plugins2block_file, "w", encoding="utf8") as wf: - yaml.dump(_tmp_data, wf) - _data = round_trip_load(open(plugins2block_file, encoding="utf8")) - _data["PluginBlockLimit"].yaml_set_start_comment( - """# 用户调用阻塞 -# 即 当用户调用此功能还未结束时 -# 用发送消息阻止用户重复调用此命令直到该命令结束 -# 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(plugins2block_file, "w", encoding="utf8") as wf: - round_trip_dump(_data, wf) - plugins2block_manager.reload_block_limit() - - -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/plugins/mute.py b/plugins/mute.py index 9035386d4..0cd87fc77 100644 --- a/plugins/mute.py +++ b/plugins/mute.py @@ -9,15 +9,9 @@ from configs.path_config import DATA_PATH, IMAGE_PATH from utils.image_utils import get_img_hash from services.log import logger -from configs.config import MUTE_LEVEL +from configs.config import NICKNAME, Config import aiohttp import aiofiles -from configs.config import ( - MUTE_DEFAULT_COUNT, - MUTE_DEFAULT_TIME, - MUTE_DEFAULT_DURATION, - NICKNAME, -) try: import ujson as json @@ -40,7 +34,29 @@ __plugin_cmd__ = ["设置刷屏检测时间 [秒]", "设置刷屏检测次数 [次数]", "设置刷屏禁言时长 [分钟]", "刷屏检测设置"] __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" -__plugin_settings__ = {"admin_level": MUTE_LEVEL} +__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_DEFAULT_DURATION": { + "value": 10, + "help": "刷屏检测默禁言时长(分钟)", + "default_value": 10 + }, +} mute = on_message(priority=1, block=False) @@ -98,9 +114,9 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg += img_hash if not mute_data.get(group_id): mute_data[group_id] = { - "count": MUTE_DEFAULT_COUNT, - "time": MUTE_DEFAULT_TIME, - "duration": MUTE_DEFAULT_DURATION, + "count": Config.get_config("mute", "MUTE_DEFAULT_COUNT"), + "time": Config.get_config("mute", "MUTE_DEFAULT_TIME"), + "duration": Config.get_config("mute", "MUTE_DEFAULT_DURATION"), } if not mute_dict.get(event.user_id): mute_dict[event.user_id] = {"time": time.time(), "count": 1, "msg": msg} diff --git a/plugins/nonebot_plugin_picsearcher/__init__.py b/plugins/nonebot_plugin_picsearcher/__init__.py index 48659189d..9810adac2 100644 --- a/plugins/nonebot_plugin_picsearcher/__init__.py +++ b/plugins/nonebot_plugin_picsearcher/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import traceback from typing import Dict from aiohttp.client_exceptions import ClientError @@ -8,7 +7,7 @@ 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 MAX_FIND_IMG_COUNT +from configs.config import Config from nonebot.rule import to_me from .ex import get_des as get_des_ex @@ -37,6 +36,9 @@ "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): @@ -109,11 +111,13 @@ async def get_setu(bot: Bot, event: MessageEvent, state: T_State): async for msg in get_des(url, mod, event.user_id): if msg: await bot.send(event=event, message=msg) - if idx == MAX_FIND_IMG_COUNT: + if idx == Config.get_config( + "nonebot_plugin_picsearcher", "MAX_FIND_IMAGE_COUNT" + ): break idx += 1 if id == 1: - await bot.send(event=event, message='没找着.') + 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}" @@ -163,7 +167,9 @@ async def handle_previous(bot: Bot, event: GroupMessageEvent, state: T_State): idx = 1 async for msg in get_des(url, "nao", event.user_id): await bot.send(event=event, message=msg) - if idx == MAX_FIND_IMG_COUNT: + if idx == Config.get_config( + "nonebot_plugin_picsearcher", "MAX_FIND_IMAGE_COUNT" + ): break idx += 1 except IndexError: diff --git a/plugins/one_friend/__init__.py b/plugins/one_friend/__init__.py index 6e175b874..f1526bef5 100644 --- a/plugins/one_friend/__init__.py +++ b/plugins/one_friend/__init__.py @@ -1,14 +1,12 @@ -import aiohttp from io import BytesIO from random import choice from nonebot import on_regex from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from utils.utils import get_message_text, get_message_at +from utils.utils import get_message_text, get_message_at, get_user_avatar from utils.message_builder import image import re from utils.image_utils import CreateImg -from asyncio.exceptions import TimeoutError __zx_plugin_name__ = "我有一个朋友" __plugin_usage__ = """ @@ -35,18 +33,6 @@ ) -async def get_pic(qq): - url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100" - async with aiohttp.ClientSession() as session: - for _ in range(3): - try: - async with session.get(url, timeout=5) as response: - return await response.read() - except TimeoutError: - pass - return None - - @one_friend.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = get_message_text(event.json()) @@ -72,11 +58,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if not msg: msg = "都不知道问什么" msg = msg.replace("他", "我").replace("她", "我").replace("它", "我") - x = await get_pic(qq) + x = await get_user_avatar(qq) if x: - ava = CreateImg(100, 100, background=BytesIO(await get_pic(qq))) + ava = CreateImg(200, 100, background=BytesIO(x)) else: - ava = CreateImg(100, 100, color=(0, 0, 0)) + ava = CreateImg(200, 100, color=(0, 0, 0)) ava.circle() text = CreateImg(300, 30, font_size=30) text.text((0, 0), user_name) diff --git a/plugins/open_cases/__init__.py b/plugins/open_cases/__init__.py index b8ca34072..155881d41 100644 --- a/plugins/open_cases/__init__.py +++ b/plugins/open_cases/__init__.py @@ -8,6 +8,7 @@ from nonebot.permission import SUPERUSER import random from nonebot.plugin import MatcherGroup +from configs.path_config import IMAGE_PATH import re from .open_cases_c import ( open_case, @@ -68,6 +69,20 @@ } __plugin_task__ = {"open_case_reset_remind": "每日开箱重置提醒"} __plugin_cd_limit__ = {"rst": "着什么急啊,慢慢来!"} +__plugin_resources__ = {f"cases": IMAGE_PATH} +__plugin_configs__ = { + "INITIAL_OPEN_CASE_COUNT": {"value": 20, "help": "初始每日开箱次数", "default_value": 20}, + "EACH_IMPRESSION_ADD_COUNT": { + "value": 3, + "help": "每 * 点好感度额外增加开箱次数", + "default_value": 3, + }, + "COOKIE": { + "value": None, + "help": "BUFF的cookie", + }, + "BUFF_PROXY": {"value": None, "help": "使用代理访问BUFF"}, +} cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"] diff --git a/plugins/open_cases/models/__init__.py b/plugins/open_cases/models/__init__.py new file mode 100644 index 000000000..a944b2fd7 --- /dev/null +++ b/plugins/open_cases/models/__init__.py @@ -0,0 +1,2 @@ +from .open_cases_user import * +from .buff_prices import * diff --git a/models/buff_price.py b/plugins/open_cases/models/buff_prices.py similarity index 95% rename from models/buff_price.py rename to plugins/open_cases/models/buff_prices.py index 852f30e7c..1c988ee15 100644 --- a/models/buff_price.py +++ b/plugins/open_cases/models/buff_prices.py @@ -8,6 +8,7 @@ class BuffPrice(db.Model): __tablename__ = 'buff_prices' + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer(), primary_key=True) case_id = db.Column(db.Integer(), nullable=False) diff --git a/models/open_cases_user.py b/plugins/open_cases/models/open_cases_user.py similarity index 98% rename from models/open_cases_user.py rename to plugins/open_cases/models/open_cases_user.py index 85c8c60e2..99662bb2a 100644 --- a/models/open_cases_user.py +++ b/plugins/open_cases/models/open_cases_user.py @@ -5,6 +5,7 @@ class OpenCasesUser(db.Model): __tablename__ = 'open_cases_users' + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) diff --git a/plugins/open_cases/open_cases_c.py b/plugins/open_cases/open_cases_c.py index 20de162f5..b8ff2fe6a 100644 --- a/plugins/open_cases/open_cases_c.py +++ b/plugins/open_cases/open_cases_c.py @@ -2,39 +2,40 @@ from .config import * from services.log import logger from services.db_context import db -from models.open_cases_user import OpenCasesUser +from .models.open_cases_user import OpenCasesUser from models.sign_group_user import SignGroupUser from utils.message_builder import image import pypinyin import random from .utils import get_price -from models.buff_price import BuffPrice +from .models.buff_prices import BuffPrice from PIL import Image from utils.image_utils import alpha2white_pil, CreateImg from configs.path_config import IMAGE_PATH import asyncio from utils.utils import cn2py -from configs.config import INITIAL_OPEN_CASE_COUNT - - -MAX_COUNT = INITIAL_OPEN_CASE_COUNT +from configs.config import Config async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动") -> str: - if case_name not in ["狂牙大行动", "突围大行动", "命悬一线", '裂空', '光谱']: + if case_name not in ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"]: return "武器箱未收录" knifes_flag = False # lan zi fen hong jin price uplist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0] case = "" for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): - case += ''.join(i) + case += "".join(i) impression = (await SignGroupUser.ensure(user_qq, group)).impression rand = random.random() async with db.transaction(): user = await OpenCasesUser.ensure(user_qq, group, for_update=True) # 一天次数上限 - if user.today_open_total == int(MAX_COUNT + int(impression) / 3): + if user.today_open_total == int( + Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT") + + int(impression) + / Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT") + ): return _handle_is_MAX_COUNT() skin, mosun = get_color_quality(rand, case_name) # 调侃 @@ -43,51 +44,49 @@ async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动" uplist[0] = 1 else: uplist[1] = 1 - ridicule_result = random.choice(['这样看着才舒服', - '是自己人,大伙把刀收好', - '非常舒适~']) + ridicule_result = random.choice(["这样看着才舒服", "是自己人,大伙把刀收好", "非常舒适~"]) if skin[:2] == "受限": if skin.find("StatTrak") == -1: uplist[2] = 1 else: uplist[3] = 1 - ridicule_result = random.choice(['还行吧,勉强接受一下下', - '居然不是蓝色,太假了', - '运气-1-1-1-1-1...']) + ridicule_result = random.choice( + ["还行吧,勉强接受一下下", "居然不是蓝色,太假了", "运气-1-1-1-1-1..."] + ) if skin[:2] == "保密": if skin.find("StatTrak") == -1: uplist[4] = 1 else: uplist[5] = 1 - ridicule_result = random.choice(['开始不适....', - '你妈妈买菜必涨价!涨三倍!', - '你最近不适合出门,真的']) + ridicule_result = random.choice( + ["开始不适....", "你妈妈买菜必涨价!涨三倍!", "你最近不适合出门,真的"] + ) if skin[:2] == "隐秘": if skin.find("StatTrak") == -1: uplist[6] = 1 else: uplist[7] = 1 - ridicule_result = random.choice(['已经非常不适', - '好兄弟你开的什么箱子啊,一般箱子不是只有蓝色的吗', - '开始拿阳寿开箱子了?']) + ridicule_result = random.choice( + ["已经非常不适", "好兄弟你开的什么箱子啊,一般箱子不是只有蓝色的吗", "开始拿阳寿开箱子了?"] + ) if skin[:2] == "罕见": knifes_flag = True if skin.find("StatTrak") == -1: uplist[8] = 1 else: uplist[9] = 1 - ridicule_result = random.choice(['你的好运我收到了,你可以去喂鲨鱼了', - '最近该吃啥就迟点啥吧,哎,好好的一个人怎么就....哎', - '众所周知,欧皇寿命极短.']) + ridicule_result = random.choice( + ["你的好运我收到了,你可以去喂鲨鱼了", "最近该吃啥就迟点啥吧,哎,好好的一个人怎么就....哎", "众所周知,欧皇寿命极短."] + ) if skin.find("(") != -1: cskin = skin.split("(") skin = cskin[0].strip() + "(" + cskin[1].strip() skin = skin.split("|")[0].strip() + " | " + skin.split("|")[1].strip() # 价格 - if skin.find('无涂装') == -1: + if skin.find("无涂装") == -1: dbprice = await BuffPrice.ensure(skin[9:]) else: - dbprice = await BuffPrice.ensure(skin[9: skin.rfind('(')].strip()) + dbprice = await BuffPrice.ensure(skin[9 : skin.rfind("(")].strip()) if dbprice.skin_price != 0: price_result = dbprice.skin_price print("数据库查询到价格: ", dbprice.skin_price) @@ -114,38 +113,55 @@ async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动" # cskin_word = sp[1][:sp[1].find("(") - 1].strip() if knifes_flag: await user.update( - knifes_name=user.knifes_name + f"{case}||{skin.split(':')[1].strip()} 磨损:{str(mosun)[:11]}, 价格:{uplist[10]}," + knifes_name=user.knifes_name + + f"{case}||{skin.split(':')[1].strip()} 磨损:{str(mosun)[:11]}, 价格:{uplist[10]}," ).apply() - cskin_word = skin.split(':')[1].replace('|', '-').replace('(StatTrak™)', '') - cskin_word = cskin_word[: cskin_word.rfind('(')].strip() - skin_name = cn2py(cskin_word.replace('|', '-').replace('(StatTrak™)', '').strip()) - img = image(f'{skin_name}.png', "cases/" + case) + cskin_word = skin.split(":")[1].replace("|", "-").replace("(StatTrak™)", "") + cskin_word = cskin_word[: cskin_word.rfind("(")].strip() + skin_name = cn2py( + cskin_word.replace("|", "-").replace("(StatTrak™)", "").strip() + ) + img = image(f"{skin_name}.png", "cases/" + case) # if knifes_flag: # await user.update( # knifes_name=user.knifes_name + f"{skin} 磨损:{mosun}, 价格:{uplist[10]}" # ).apply() if await update_user_total(user, uplist): - logger.info(f"qq:{user_qq} 群:{group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}, 数据更新成功") + logger.info( + f"qq:{user_qq} 群:{group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}, 数据更新成功" + ) else: - logger.warning(f"qq:{user_qq} 群:{group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}, 数据更新失败") + logger.warning( + f"qq:{user_qq} 群:{group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}, 数据更新失败" + ) user = await OpenCasesUser.ensure(user_qq, group, for_update=True) - over_count = int(MAX_COUNT + int(impression) / 3) - user.today_open_total - return f"开启{case_name}武器箱.\n剩余开箱次数:{over_count}.\n" \ - + img + "\n" + \ - f"皮肤:{skin}\n" \ - f"磨损:{mosun:.9f}\n" \ - f"价格:{price_result}\n" \ - f"{ridicule_result}" + over_count = int( + Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT") + + int(impression) + / Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT") + ) - user.today_open_total + return ( + f"开启{case_name}武器箱.\n剩余开箱次数:{over_count}.\n" + img + "\n" + f"皮肤:{skin}\n" + f"磨损:{mosun:.9f}\n" + f"价格:{price_result}\n" + f"{ridicule_result}" + ) async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int = 10): user = await OpenCasesUser.ensure(user_qq, group, for_update=True) impression = (await SignGroupUser.ensure(user_qq, group)).impression - if user.today_open_total == int(MAX_COUNT + int(impression) / 3): + max_count = int( + Config.get_config("open_cases", "INITIAL_OPEN_CASE_COUNT") + + int(impression) / Config.get_config("open_cases", "EACH_IMPRESSION_ADD_COUNT") + ) + if user.today_open_total == max_count: return _handle_is_MAX_COUNT() - if int(MAX_COUNT + int(impression) / 3) - user.today_open_total < num: - return f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)" \ - f"\n剩余开箱次数:{int(MAX_COUNT + int(impression) / 3) - user.today_open_total}" + if max_count - user.today_open_total < num: + return ( + f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)" + f"\n剩余开箱次数:{max_count - user.today_open_total}" + ) await user.update( total_count=user.total_count + num, spend_money=user.spend_money + 17 * num, @@ -163,7 +179,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int = # lan zi fen hong jin price uplist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0] img_list = [] - name_list = ['蓝', '蓝(暗金)', '紫', '紫(暗金)', '粉', '粉(暗金)', '红', '红(暗金)', '金', '金(暗金)'] + name_list = ["蓝", "蓝(暗金)", "紫", "紫(暗金)", "粉", "粉(暗金)", "红", "红(暗金)", "金", "金(暗金)"] async with db.transaction(): for _ in range(num): knifes_flag = False @@ -200,49 +216,71 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int = skin = cskin[0].strip() + "(" + cskin[1].strip() skin = skin.split("|")[0].strip() + " | " + skin.split("|")[1].strip() # 价格 - if skin.find('无涂装') == -1: + if skin.find("无涂装") == -1: dbprice = await BuffPrice.ensure(skin[9:]) else: - dbprice = await BuffPrice.ensure(skin[9: skin.rfind('(')].strip()) + dbprice = await BuffPrice.ensure(skin[9 : skin.rfind("(")].strip()) if dbprice.skin_price != 0: price_result = dbprice.skin_price uplist[10] += price_result else: - price_result = '未查询到' + price_result = "未查询到" if knifes_flag: await user.update( - knifes_name=user.knifes_name + f"{case}||{skin.split(':')[1].strip()} 磨损:{str(mosun)[:11]}, 价格:{dbprice.skin_price}," + knifes_name=user.knifes_name + + f"{case}||{skin.split(':')[1].strip()} 磨损:{str(mosun)[:11]}, 价格:{dbprice.skin_price}," ).apply() - cskin_word = skin.split(':')[1].replace('|', '-').replace('(StatTrak™)', '') - cskin_word = cskin_word[: cskin_word.rfind('(')].strip() + cskin_word = skin.split(":")[1].replace("|", "-").replace("(StatTrak™)", "") + cskin_word = cskin_word[: cskin_word.rfind("(")].strip() skin_name = "" - for i in pypinyin.pinyin(cskin_word.replace('|', '-').replace('(StatTrak™)', '').strip(), - style=pypinyin.NORMAL): - skin_name += ''.join(i) + for i in pypinyin.pinyin( + cskin_word.replace("|", "-").replace("(StatTrak™)", "").strip(), + style=pypinyin.NORMAL, + ): + skin_name += "".join(i) # img = image(skin_name, "cases/" + case, "png") wImg = CreateImg(200, 270, 200, 200) - wImg.paste(alpha2white_pil(Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((200, 200), Image.ANTIALIAS)), (0, 0)) + wImg.paste( + alpha2white_pil( + Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize( + (200, 200), Image.ANTIALIAS + ) + ), + (0, 0), + ) wImg.text((5, 200), skin) - wImg.text((5, 220), f'磨损:{str(mosun)[:9]}') - wImg.text((5, 240), f'价格:{price_result}') + wImg.text((5, 220), f"磨损:{str(mosun)[:9]}") + wImg.text((5, 240), f"价格:{price_result}") img_list.append(wImg) - logger.info(f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}") + logger.info( + f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}" + ) if await update_user_total(user, uplist, 0): - logger.info(f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 数据更新成功") + logger.info( + f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 数据更新成功" + ) else: - logger.warning(f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 价格:{uplist[10]}, 数据更新失败") + logger.warning( + f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 价格:{uplist[10]}, 数据更新失败" + ) # markImg = CreateImg(1000, h, 200, 270) # for img in img_list: # markImg.paste(img) - markImg = await asyncio.get_event_loop().run_in_executor(None, paste_markImg, h, img_list) - over_count = int(MAX_COUNT + int(impression) / 3) - user.today_open_total - result = '' + markImg = await asyncio.get_event_loop().run_in_executor( + None, paste_markImg, h, img_list + ) + over_count = max_count - user.today_open_total + result = "" for i in range(len(name_list)): if uplist[i]: - result += f'[{name_list[i]}:{uplist[i]}] ' - return f"开启{case_name}武器箱\n剩余开箱次数:{over_count}\n" \ - + image(b64=markImg.pic2bs4()) + \ - '\n' + result[:-1] + f'\n总获取金额:{uplist[-1]:.2f}\n总花费:{17 * num}' + result += f"[{name_list[i]}:{uplist[i]}] " + return ( + f"开启{case_name}武器箱\n剩余开箱次数:{over_count}\n" + + image(b64=markImg.pic2bs4()) + + "\n" + + result[:-1] + + f"\n总获取金额:{uplist[-1]:.2f}\n总花费:{17 * num}" + ) def paste_markImg(h: int, img_list: list): @@ -273,7 +311,7 @@ async def update_user_total(user: OpenCasesUser, up_list: list, num: int = 1) -> spend_money=user.spend_money + 17 * num, make_money=user.make_money + up_list[10], today_open_total=user.today_open_total + num, - open_cases_time_last=datetime.now() + open_cases_time_last=datetime.now(), ).apply() return True except: @@ -283,21 +321,23 @@ async def update_user_total(user: OpenCasesUser, up_list: list, num: int = 1) -> async def total_open_statistics(user_qq: int, group: int) -> str: async with db.transaction(): user = await OpenCasesUser.ensure(user_qq, group, for_update=True) - return f"开箱总数:{user.total_count}\n" \ - f"今日开箱:{user.today_open_total}\n" \ - f"蓝色军规:{user.blue_count}\n" \ - f"蓝色暗金:{user.blue_st_count}\n" \ - f"紫色受限:{user.purple_count}\n" \ - f"紫色暗金:{user.purple_st_count}\n" \ - f"粉色保密:{user.pink_count}\n" \ - f"粉色暗金:{user.pink_st_count}\n" \ - f"红色隐秘:{user.red_count}\n" \ - f"红色暗金:{user.red_st_count}\n" \ - f"金色罕见:{user.knife_count}\n" \ - f"金色暗金:{user.knife_st_count}\n" \ - f"花费金额:{user.spend_money}\n" \ - f"获取金额:{user.make_money:.2f}\n" \ - f"最后开箱日期:{(user.open_cases_time_last + timedelta(hours=8)).date()}" + return ( + f"开箱总数:{user.total_count}\n" + f"今日开箱:{user.today_open_total}\n" + f"蓝色军规:{user.blue_count}\n" + f"蓝色暗金:{user.blue_st_count}\n" + f"紫色受限:{user.purple_count}\n" + f"紫色暗金:{user.purple_st_count}\n" + f"粉色保密:{user.pink_count}\n" + f"粉色暗金:{user.pink_st_count}\n" + f"红色隐秘:{user.red_count}\n" + f"红色暗金:{user.red_st_count}\n" + f"金色罕见:{user.knife_count}\n" + f"金色暗金:{user.knife_st_count}\n" + f"花费金额:{user.spend_money}\n" + f"获取金额:{user.make_money:.2f}\n" + f"最后开箱日期:{(user.open_cases_time_last + timedelta(hours=8)).date()}" + ) async def group_statistics(group: int): @@ -318,20 +358,22 @@ async def group_statistics(group: int): uplist[10] += user.make_money uplist[11] += user.total_count uplist[12] += user.today_open_total - return f"群开箱总数:{uplist[11]}\n" \ - f"群今日开箱:{uplist[12]}\n" \ - f"蓝色军规:{uplist[0]}\n" \ - f"蓝色暗金:{uplist[1]}\n" \ - f"紫色受限:{uplist[2]}\n" \ - f"紫色暗金:{uplist[3]}\n" \ - f"粉色保密:{uplist[4]}\n" \ - f"粉色暗金:{uplist[5]}\n" \ - f"红色隐秘:{uplist[6]}\n" \ - f"红色暗金:{uplist[7]}\n" \ - f"金色罕见:{uplist[8]}\n" \ - f"金色暗金:{uplist[9]}\n" \ - f"花费金额:{uplist[11] * 17}\n" \ - f"获取金额:{uplist[10]:.2f}" + return ( + f"群开箱总数:{uplist[11]}\n" + f"群今日开箱:{uplist[12]}\n" + f"蓝色军规:{uplist[0]}\n" + f"蓝色暗金:{uplist[1]}\n" + f"紫色受限:{uplist[2]}\n" + f"紫色暗金:{uplist[3]}\n" + f"粉色保密:{uplist[4]}\n" + f"粉色暗金:{uplist[5]}\n" + f"红色隐秘:{uplist[6]}\n" + f"红色暗金:{uplist[7]}\n" + f"金色罕见:{uplist[8]}\n" + f"金色暗金:{uplist[9]}\n" + f"花费金额:{uplist[11] * 17}\n" + f"获取金额:{uplist[10]:.2f}" + ) async def my_knifes_name(user_id: int, group: int): @@ -348,7 +390,9 @@ async def my_knifes_name(user_id: int, group: int): else: h = 600 * int(length / 5) + 600 w = 540 * 5 - A = await asyncio.get_event_loop().run_in_executor(None, _pst_my_knife, w, h, knifes_list) + A = await asyncio.get_event_loop().run_in_executor( + None, _pst_my_knife, w, h, knifes_list + ) return image(b64=A.pic2bs4()) else: return "您木有开出金色级别的皮肤喔" @@ -357,30 +401,36 @@ async def my_knifes_name(user_id: int, group: int): def _pst_my_knife(w, h, knifes_list): A = CreateImg(w, h, 540, 600) for knife in knifes_list: - case = knife.split('||')[0] - knife = knife.split('||')[1] - name = knife[:knife.find('(')].strip() - itype = knife[knife.find('(')+1: knife.find(')')].strip() - mosun = knife[knife.find('磨损:')+3: knife.rfind('价格:')].strip() - if mosun[-1] == ',' or mosun[-1] == ',': + case = knife.split("||")[0] + knife = knife.split("||")[1] + name = knife[: knife.find("(")].strip() + itype = knife[knife.find("(") + 1 : knife.find(")")].strip() + mosun = knife[knife.find("磨损:") + 3 : knife.rfind("价格:")].strip() + if mosun[-1] == "," or mosun[-1] == ",": mosun = mosun[:-1] - price = knife[knife.find('价格:')+3:] + price = knife[knife.find("价格:") + 3 :] skin_name = "" - for i in pypinyin.pinyin(name.replace('|', '-').replace('(StatTrak™)', '').strip(), - style=pypinyin.NORMAL): - skin_name += ''.join(i) + for i in pypinyin.pinyin( + name.replace("|", "-").replace("(StatTrak™)", "").strip(), + style=pypinyin.NORMAL, + ): + skin_name += "".join(i) knife_img = CreateImg(470, 600, 470, 470, font_size=20) - knife_img.paste(alpha2white_pil( - Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((470, 470), Image.ANTIALIAS)), (0, 0)) - knife_img.text((5, 500), f'\t{name}({itype})') - knife_img.text((5, 530), f'\t磨损:{mosun}') - knife_img.text((5, 560), f'\t价格:{price}') + knife_img.paste( + alpha2white_pil( + Image.open(IMAGE_PATH + f"cases/{case}/{skin_name}.png").resize( + (470, 470), Image.ANTIALIAS + ) + ), + (0, 0), + ) + knife_img.text((5, 500), f"\t{name}({itype})") + knife_img.text((5, 530), f"\t磨损:{mosun}") + knife_img.text((5, 560), f"\t价格:{price}") A.paste(knife_img) return A - - # G3SG1(StatTrak™) | 血腥迷彩 (战痕累累) # G3SG1(StatTrak™) | 血腥迷彩 (战痕累累) # G3SG1(StatTrak™) | 血腥迷彩 (战痕累累) diff --git a/plugins/open_cases/utils.py b/plugins/open_cases/utils.py index be7db1d65..5c534a622 100644 --- a/plugins/open_cases/utils.py +++ b/plugins/open_cases/utils.py @@ -1,19 +1,18 @@ -from models.buff_price import BuffPrice +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 models.open_cases_user import OpenCasesUser +from .models.open_cases_user import OpenCasesUser import os from services.log import logger from utils.utils import get_bot -from utils.utils import get_cookie_text from asyncio.exceptions import TimeoutError import pypinyin from nonebot.adapters.cqhttp.exception import ActionFailed -from configs.config import BUFF_PROXY +from configs.config import Config from utils.manager import group_manager url = "https://buff.163.com/api/market/goods" @@ -21,20 +20,20 @@ async def util_get_buff_price(case_name: str = "狂牙大行动") -> str: - cookie = {'session': get_cookie_text('buff')} + cookie = {"session": Config.get_config("open_cases", "COOKIE")} failed_list = [] case = "" for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): - case += ''.join(i) + case += "".join(i) if case_name == "狂牙大行动": case_id = 1 elif case_name == "突围大行动": case_id = 2 elif case_name == "命悬一线": case_id = 3 - elif case_name == '裂空': + elif case_name == "裂空": case_id = 4 - elif case_name == '光谱': + elif case_name == "光谱": case_id = 5 else: return "未查询到武器箱" @@ -44,47 +43,63 @@ 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: + 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]: - print("----------------------------------") for skin in total_list: - print(skin) - if skin in ["蝴蝶刀 | 无涂装", '求生匕首 | 无涂装', '流浪者匕首 | 无涂装', '系绳匕首 | 无涂装', '骷髅匕首 | 无涂装']: - skin = skin.split('|')[0].strip() + if skin in [ + "蝴蝶刀 | 无涂装", + "求生匕首 | 无涂装", + "流浪者匕首 | 无涂装", + "系绳匕首 | 无涂装", + "骷髅匕首 | 无涂装", + ]: + skin = skin.split("|")[0].strip() async with db.transaction(): name_list = [] price_list = [] - parameter = { - "game": "csgo", - "page_num": "1", - "search": skin - } + parameter = {"game": "csgo", "page_num": "1", "search": skin} try: - async with session.get(url, proxy=BUFF_PROXY, params=parameter, timeout=20) as response: + 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 ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: + 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 + "search": skin, } - async with session.get(url, params=parameter, timeout=20) as res: - data = (await response.json())["data"]["items"] + 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}(★)']: + 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 = data[j][ + "sell_reference_price" + ] + name_list.append( + name.split("(")[0].strip() + + " | 无涂装" + ) price_list.append(price) - print(name_list[-1]) - print(price_list[-1]) flag = True break if flag: @@ -99,38 +114,56 @@ async def util_get_buff_price(case_name: str = "狂牙大行动") -> str: price_list.append(price) except Exception as e: failed_list.append(skin) - print(f"{skin}更新失败") + logger.warning(f"{skin}更新失败") else: failed_list.append(skin) - print(f"{skin}更新失败") + logger.warning(f"{skin}更新失败") except Exception: failed_list.append(skin) - print(f"{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:] + 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() + name = ( + name.split("|")[0].strip() + + " | " + + name.split("|")[1].strip() + ) elif name.find("消音") != -1: - name = name.split("|")[0][:-5].strip() + " | " + name.split("|")[1].strip() + 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() + name = ( + name.split("|")[0].strip() + + " | " + + name.split("|")[1].strip() + ) elif name.find(" 18 ") != -1: - name = name.split("|")[0][:-6].strip() + " | " + name.split("|")[1].strip() + 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(): + 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() - print(f"{name_list[i]}---------->成功更新") + logger.info(f"{name_list[i]}---------->成功更新") result = None if failed_list: result = "" @@ -140,11 +173,11 @@ async def util_get_buff_price(case_name: str = "狂牙大行动") -> str: async def util_get_buff_img(case_name: str = "狂牙大行动") -> str: - cookie = {'session': get_cookie_text('buff')} + cookie = {"session": Config.get_config("open_cases", "COOKIE")} error_list = [] case = "" for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): - case += ''.join(i) + case += "".join(i) path = "cases/" + case + "/" if not os.path.exists(IMAGE_PATH + path): os.mkdir(IMAGE_PATH + path) @@ -154,63 +187,75 @@ 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: + 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() - print("开始更新----->", skin) - print(skin) - skin_name = '' + 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=BUFF_PROXY, params=parameter, timeout=20) as response: + 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 ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: + if ( + skin.find("|") == -1 + ): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: for i in range(1, total_page + 1): - async with session.get(url, params=parameter, timeout=20) as res: + 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: - print("------->开始写入 ", skin) - async with session.get(img_url, timeout=7) as res: + 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: - print("------->开始写入 ", skin) + 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()) - # async with session.get(url, params=parameter, timeout=7) as response: - # if response.status == 200: - # img_url = (await response.json())["data"]['items'][0]['goods_info']['icon_url'] - # skin_name = '' - # for i in pypinyin.pinyin(skin.split("|")[1].strip(), style=pypinyin.NORMAL): - # skin_name += ''.join(i) - # async with aiofiles.open(IMAGE_PATH + path + skin_name + ".png", 'wb') as f: - # print("------->开始写入 ", skin) - # async with session.get(img_url, timeout=7) as res: - # await f.write(await res.read()) - # except Exception: - # print("图片更新失败 ---->", skin) - # error_list.append(skin) result = None if error_list: result = "" @@ -220,16 +265,14 @@ async def util_get_buff_img(case_name: str = "狂牙大行动") -> str: async def get_price(dname): - cookie = {'session': get_cookie_text('buff')} + 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": dname} try: - async with aiohttp.ClientSession(cookies=cookie, headers=get_user_agent()) as session: + 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: @@ -262,22 +305,21 @@ async def update_count_daily(): for user in users: await user.update( today_open_total=0, - ).apply() + ).apply() bot = get_bot() gl = await bot.get_group_list() - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] for g in gl: - if await group_manager.check_group_task_status(g, 'open_case_reset_remind'): + if await group_manager.check_group_task_status(g, "open_case_reset_remind"): try: await bot.send_group_msg(group_id=g, message="今日开箱次数重置成功") except ActionFailed: - logger.warning(f'{g} 群被禁言,无法发送 开箱重置提醒') + logger.warning(f"{g} 群被禁言,无法发送 开箱重置提醒") logger.info("今日开箱次数重置成功") except Exception as e: - logger.error(f'开箱重置错误 e:{e}') - + logger.error(f"开箱重置错误 e:{e}") # 蝴蝶刀(★) | 噩梦之夜 (久经沙场) -if __name__ == '__main__': +if __name__ == "__main__": print(util_get_buff_img("xxxx/")) diff --git a/plugins/pid_search.py b/plugins/pid_search.py index 85e694fb2..80e6701f6 100644 --- a/plugins/pid_search.py +++ b/plugins/pid_search.py @@ -104,7 +104,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): ) ) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f"(USER {event.user_id}, " + f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 查询图片 PID:{pid}" ) if isinstance(event, GroupMessageEvent): diff --git a/plugins/pix_gallery/__init__.py b/plugins/pix_gallery/__init__.py index 7d7f556cc..9f8073469 100644 --- a/plugins/pix_gallery/__init__.py +++ b/plugins/pix_gallery/__init__.py @@ -1,6 +1,6 @@ -from configs.config import HIBIAPI +from configs.config import Config from services.log import logger -from models.omega_pixiv_illusts import OmegaPixivIllusts +from .model.omega_pixiv_illusts import OmegaPixivIllusts from pathlib import Path from nonebot import Driver from typing import List @@ -14,10 +14,60 @@ __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() -illust_url = f"{HIBIAPI}/api/pixiv/illust" @driver.on_startup diff --git a/plugins/pix_gallery/data_source.py b/plugins/pix_gallery/data_source.py index 17941f453..34c0d00f2 100644 --- a/plugins/pix_gallery/data_source.py +++ b/plugins/pix_gallery/data_source.py @@ -4,17 +4,17 @@ ClientConnectorError, ) from asyncpg.exceptions import UniqueViolationError -from models.omega_pixiv_illusts import OmegaPixivIllusts +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 models.pixiv import Pixiv +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 HIBIAPI_BOOKMARKS, HIBIAPI, PIX_IMAGE_SIZE +from configs.config import Config from configs.path_config import TEMP_PATH import platform import aiohttp @@ -31,36 +31,45 @@ policy = asyncio.WindowsSelectorEventLoopPolicy() asyncio.set_event_loop_policy(policy) -search_url = f"{HIBIAPI}/api/pixiv/search" -member_illust_url = f"{HIBIAPI}/api/pixiv/member_illust" -illust_url = f"{HIBIAPI}/api/pixiv/illust" - 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]): +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 = member_illust_url + url = f"{HIBIAPI}/api/pixiv/member_illust" params = {"id": keyword[4:], "page": page} if page == 30: break elif keyword.startswith("pid:"): - url = illust_url + url = f"{HIBIAPI}/api/pixiv/illust" params = {"id": keyword[4:]} else: - url = search_url + url = f"{HIBIAPI}/api/pixiv/search" params = {"word": keyword, "page": page} tasks.append( asyncio.ensure_future( @@ -86,7 +95,18 @@ async def search_image( 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 @@ -104,7 +124,7 @@ async def search_image( or (not data.get("illusts") and not data.get("illust")) ): return 0, 0 - if url != illust_url: + if url != f"{HIBIAPI}/api/pixiv/illust": logger.info(f'{keyword}: 获取数据成功...数据总量:{len(data["illusts"])}') data = data["illusts"] else: @@ -133,9 +153,13 @@ async def search_image( img_urls.append(urls["image_urls"]["original"]) if ( ( - bookmarks >= HIBIAPI_BOOKMARKS - or (url == member_illust_url and bookmarks >= 1500) - or (url == illust_url) + 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) @@ -177,13 +201,8 @@ async def search_image( tmp_pid.append(data["pid"]) pic_count += 1 logger.info(f'存储图片PID:{data["pid"]} IMG_P:{img_p}') - # await download_image(img_url, session) except UniqueViolationError: logger.warning(f'{data["pid"]} | {img_url} 已存在...') - # 下载图片 - # if not os.path.exists(f'{image_path}/ - # {img_url[img_url.rfind("/") + 1:]}'): - # await download_image(img_url, session) except (ServerDisconnectedError, ClientConnectorError, ClientOSError): logger.warning("搜索图片服务被关闭,再次调用....") await search_image(url, keyword, params, semaphore, session, page, black) @@ -192,22 +211,26 @@ async def search_image( # 下载图片 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()) - # async with semaphore: - # await asyncio.get_event_loop().run_in_executor(None, - # upload_image, img_url[img_url.rfind("/")+1:]) except ServerDisconnectedError: logger.warning(f"下载图片服务被关闭,第 {_count} 次调用....") await download_image(img_url, session, _count + 1) except ClientOSError: logger.warning(f"远程连接被关闭,第 {_count} 次调用....") - await download_image( - img_url.replace("i.pximg.net", "i.pixiv.cat"), session, _count + 1 - ) + 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) @@ -216,34 +239,47 @@ async def download_image(img_url: str, session: ClientSession, _count: int = 1): async def get_image(img_url: str, user_id: int) -> str: + """ + 下载图片 + :param img_url: + :param user_id: + :return: 图片名称 + """ async with aiohttp.ClientSession(headers=headers) as session: - if 'https://www.pixiv.net/artworks' in img_url: - pid = img_url.rsplit('/', maxsplit=1)[-1] + 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( - illust_url, + 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'] + 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"] + 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, PIX_IMAGE_SIZE) + img_url = change_picture_links( + img_url, Config.get_config("pix", "PIX_IMAGE_SIZE") + ) 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 @@ -257,12 +293,15 @@ async def get_image(img_url: str, user_id: int) -> str: pass -# 检测UID或PID是否有效 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 = illust_url + url = f"{HIBIAPI}/api/pixiv/illust" else: return False params = {"id": int(id_[4:])} @@ -274,13 +313,24 @@ async def uid_pid_exists(id_: str) -> bool: return True -async def get_keyword_num(keyword: str) -> "int , int": +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()) + 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}" @@ -290,6 +340,12 @@ async def remove_image(pid: int, img_p: str) -> bool: 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 @@ -373,10 +429,15 @@ def gen_keyword_pic( return A.pic2bs4() -def _check_black(img_urls: List[str], black: List[str]): +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: - # img_url = img_url[img_url.rfind('/')+1: img_url.rfind('.')] 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 new file mode 100644 index 000000000..e3ad2b4ba --- /dev/null +++ b/plugins/pix_gallery/model/__init__.py @@ -0,0 +1,3 @@ +from .pixiv_keyword_user import * +from .omega_pixiv_illusts import * +from .pixiv import * diff --git a/models/omega_pixiv_illusts.py b/plugins/pix_gallery/model/omega_pixiv_illusts.py similarity index 98% rename from models/omega_pixiv_illusts.py rename to plugins/pix_gallery/model/omega_pixiv_illusts.py index 52a086f24..cd2138536 100644 --- a/models/omega_pixiv_illusts.py +++ b/plugins/pix_gallery/model/omega_pixiv_illusts.py @@ -5,6 +5,8 @@ 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) diff --git a/models/pixiv.py b/plugins/pix_gallery/model/pixiv.py similarity index 98% rename from models/pixiv.py rename to plugins/pix_gallery/model/pixiv.py index 6210f2a21..15275d3e4 100644 --- a/models/pixiv.py +++ b/plugins/pix_gallery/model/pixiv.py @@ -4,6 +4,7 @@ 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) @@ -35,7 +36,6 @@ async def add_image_data( uid: int, author: str, tags: str, - nws ): """ 说明: @@ -133,7 +133,7 @@ async def query_images( elif uid: query = query.where(cls.uid == uid) elif pid: - query = query.where(cls.uid == pid) + query = query.where(cls.pid == pid) query = query.order_by(db.func.random()).limit(num) return await query.gino.all() diff --git a/models/pixiv_keyword_user.py b/plugins/pix_gallery/model/pixiv_keyword_user.py similarity index 98% rename from models/pixiv_keyword_user.py rename to plugins/pix_gallery/model/pixiv_keyword_user.py index 7c5b864c4..f8774e6d3 100644 --- a/models/pixiv_keyword_user.py +++ b/plugins/pix_gallery/model/pixiv_keyword_user.py @@ -4,6 +4,7 @@ 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) diff --git a/plugins/pix_gallery/pix.py b/plugins/pix_gallery/pix.py index 755ecdb8b..10c352c19 100644 --- a/plugins/pix_gallery/pix.py +++ b/plugins/pix_gallery/pix.py @@ -1,19 +1,18 @@ from nonebot.adapters.cqhttp.message import Message from utils.utils import get_message_text, is_number -from configs.config import PIX_OMEGA_PIXIV_RATIO, WITHDRAW_PIX_TIME -from models.omega_pixiv_illusts import OmegaPixivIllusts +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, - PrivateMessageEvent, ) -from utils.manager import withdraw_message_manager from nonebot.typing import T_State from .data_source import get_image -from models.pixiv import Pixiv +from .model.pixiv import Pixiv from nonebot import on_command import random @@ -55,54 +54,66 @@ pix = on_command("pix", aliases={"PIX", "Pix"}, priority=5, block=True) -PIX_RATIO = PIX_OMEGA_PIXIV_RATIO[0] / ( - PIX_OMEGA_PIXIV_RATIO[0] + PIX_OMEGA_PIXIV_RATIO[1] -) -OMEGA_RATIO = 1 - PIX_RATIO +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): - all_image = await Pixiv.query_images(uid=int(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(":", "") + pid = keyword.replace("pid", "").replace(":", "").replace(":", "") if not is_number(pid): await pix.finish("PID必须是数字...", at_sender=True) - isr18 = 0 if isinstance(event, GroupMessageEvent) else 2 - nsfw_tag = 0 if isinstance(event, GroupMessageEvent) else None - all_image = await Pixiv.query_images(pid=int(pid), r18=isr18) + 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: - 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 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) @@ -124,14 +135,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if isinstance(img, OmegaPixivIllusts): img_url = img.url author = img.uname - print(img.nsfw_tag) elif isinstance(img, Pixiv): img_url = img.img_url author = img.author pid = img.pid title = img.title uid = img.uid - # tags = img.tags _img = await get_image(img_url, event.user_id) if _img: msg_id = await pix.send( @@ -152,14 +161,5 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 查看PIX图库PID: {pid},下载图片出错" ) - withdraw_message(event, msg_id["message_id"]) - + withdraw_message_manager.withdraw_message(event, msg_id, Config.get_config("pix", "WITHDRAW_PIX_MESSAGE")) -def withdraw_message(event: MessageEvent, id_: int): - if WITHDRAW_PIX_TIME[0]: - if ( - (WITHDRAW_PIX_TIME[1] == 0 and isinstance(event, PrivateMessageEvent)) - or (WITHDRAW_PIX_TIME[1] == 1 and isinstance(event, GroupMessageEvent)) - or WITHDRAW_PIX_TIME[1] == 2 - ): - withdraw_message_manager.append((id_, WITHDRAW_PIX_TIME[0])) diff --git a/plugins/pix_gallery/pix_add_keyword.py b/plugins/pix_gallery/pix_add_keyword.py index fd8c16fc2..6160be150 100644 --- a/plugins/pix_gallery/pix_add_keyword.py +++ b/plugins/pix_gallery/pix_add_keyword.py @@ -4,8 +4,8 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from .data_source import uid_pid_exists -from models.pixiv_keyword_user import PixivKeywordUser -from models.pixiv import Pixiv +from .model.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv import Pixiv from nonebot.permission import SUPERUSER __zx_plugin_name__ = "PIX关键词/UID/PID添加管理 [Superuser]" diff --git a/plugins/pix_gallery/pix_pass_del_keyword.py b/plugins/pix_gallery/pix_pass_del_keyword.py index eed460aa6..9bbbba1da 100644 --- a/plugins/pix_gallery/pix_pass_del_keyword.py +++ b/plugins/pix_gallery/pix_pass_del_keyword.py @@ -7,8 +7,8 @@ from nonebot.permission import SUPERUSER from nonebot.typing import T_State from .data_source import remove_image -from models.pixiv_keyword_user import PixivKeywordUser -from models.pixiv import Pixiv +from .model.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv import Pixiv __zx_plugin_name__ = "PIX关键词/UID/PID删除管理 [Superuser]" diff --git a/plugins/pix_gallery/pix_show_info.py b/plugins/pix_gallery/pix_show_info.py index 9463fd2bb..5ed7eb9f9 100644 --- a/plugins/pix_gallery/pix_show_info.py +++ b/plugins/pix_gallery/pix_show_info.py @@ -4,7 +4,7 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent from nonebot.typing import T_State from .data_source import gen_keyword_pic, get_keyword_num -from models.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv_keyword_user import PixivKeywordUser import asyncio diff --git a/plugins/pix_gallery/pix_update.py b/plugins/pix_gallery/pix_update.py index 1dcc50065..a9c01d466 100644 --- a/plugins/pix_gallery/pix_update.py +++ b/plugins/pix_gallery/pix_update.py @@ -3,8 +3,8 @@ from nonebot.permission import SUPERUSER from nonebot.typing import T_State from .data_source import start_update_image_url -from models.pixiv_keyword_user import PixivKeywordUser -from models.pixiv import Pixiv +from .model.pixiv_keyword_user import PixivKeywordUser +from .model.pixiv import Pixiv from nonebot.adapters.cqhttp import Bot, MessageEvent import time diff --git a/plugins/pixiv/__init__.py b/plugins/pixiv_rank_search/__init__.py similarity index 88% rename from plugins/pixiv/__init__.py rename to plugins/pixiv_rank_search/__init__.py index af901a2d6..3fed86045 100644 --- a/plugins/pixiv/__init__.py +++ b/plugins/pixiv_rank_search/__init__.py @@ -1,204 +1,199 @@ -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 configs.config import NICKNAME -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": ["pixiv", "p站排行", "搜图", "p站搜图", "P站搜图"], -} -__plugin_block_limit__ = { - "rst": "P站排行榜或搜图正在搜索,请不要重复触发命令..." -} - - -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) - ) - data = { - "type": "node", - "data": { - "name": f"这里是{NICKNAME}酱", - "uin": f"{bot.self_id}", - "content": _message, - }, - } - mes_list.append(data) - idx += 1 - 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 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站排行榜或搜图正在搜索,请不要重复触发命令..."} +Config.add_plugin_config( + "hibiapi", + "HIBIAPI", + "https://api.obfs.dev", + help_="如果没有自建或其他hibiapi请不要修改", + default_value="https://api.obfs.dev", +) + +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) diff --git a/plugins/pixiv/data_source.py b/plugins/pixiv_rank_search/data_source.py similarity index 83% rename from plugins/pixiv/data_source.py rename to plugins/pixiv_rank_search/data_source.py index 3909e5447..2f76098d4 100644 --- a/plugins/pixiv/data_source.py +++ b/plugins/pixiv_rank_search/data_source.py @@ -1,131 +1,161 @@ -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 HIBIAPI -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()) - - -rank_url = f"{HIBIAPI}/api/pixiv/rank" -search_url = f"{HIBIAPI}/api/pixiv/search" - - -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": - params = {"mode": mode, "page": page} - if date: - params["date"] = date - return await parser_data(rank_url, num, params, 'rank') - - -async def search_pixiv_urls( - keyword: str, num: int, page: int, r18: int -) -> "list, list": - params = {"word": keyword, 'page': page} - 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": - 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=5 - ) 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: - result = "" - index = 0 - for url in urls: - url = 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=3 - ) 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.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=5 + ) 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: + url = 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=3 + ) 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 + diff --git a/plugins/reimu/__init__.py b/plugins/reimu/__init__.py deleted file mode 100644 index 73b24a49c..000000000 --- a/plugins/reimu/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -from nonebot import on_command -from nonebot.adapters.cqhttp.permission import PRIVATE -from .data_source import from_reimu_get_info -from services.log import logger -from nonebot.adapters.cqhttp import Bot, PrivateMessageEvent -from nonebot.typing import T_State -from utils.utils import is_number, get_message_text, UserBlockLimiter, scheduler -from models.count_user import UserCount -from configs.config import COUNT_PER_DAY_REIMU, NICKNAME - -__zx_plugin_name__ = "上车" -__plugin_usage__ = """ -usage: - * 请各位使用后不要转发 * - * 大部分解压密码是⑨ * - / 并不推荐小色批使用此功能[主要是不够色,目的不够明确] / - 指令: - 上车 ?[page] [目的地] - 示例:上车 萝莉 - 示例:上车 5 萝莉: 该目的地第5页停车场 - ps: 请尽量提供具体的目的地名称 -""".strip() -__plugin_des__ = "都坐稳了,老司机焊死了车门![仅限私聊]" -__plugin_cmd__ = ["上车 ?[page] [目的地]"] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["上车"], -} - - -_ulmt = UserBlockLimiter() - -reimu = on_command("上车", permission=PRIVATE, block=True, priority=1) - - -@reimu.args_parser -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - if get_message_text(event.json()) in ["取消", "算了"]: - await reimu.finish("已取消操作..", at_sender=True) - if not get_message_text(event.json()): - await reimu.reject("没时间等了!快说你要去哪里?", at_sender=True) - state["keyword"] = get_message_text(event.json()) - state["page"] = 1 - - -@reimu.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - if str(event.get_message()) in ["帮助"]: - await reimu.finish(__plugin_usage__) - if await UserCount.check_count(event.user_id, "reimu", COUNT_PER_DAY_REIMU): - await reimu.finish("今天已经没车了,请明天再来...", at_sender=True) - if _ulmt.check(event.user_id): - await reimu.finish("您还没下车呢,请稍等...", at_sender=True) - _ulmt.set_true(event.user_id) - msg = get_message_text(event.json()) - args = msg.split(" ") - if msg in ["!", "!", "?", "?", ",", ",", ".", "。"]: - await reimu.finish(__plugin_usage__) - if msg: - if len(args) > 1 and is_number(args[0]): - state["keyword"] = args[1] - state["page"] = args[0] - else: - state["keyword"] = msg - state["page"] = 1 - - -@reimu.got("keyword", "你的目的地是哪?") -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - try: - keyword = state["keyword"] - page = state["page"] - await UserCount.add_count(event.user_id, "reimu") - await reimu.send( - f"已经帮你关好车门了,请等待发车(不加{NICKNAME}好友的话是欣赏不到旅途的风景的)", at_sender=True - ) - reimu_report = await from_reimu_get_info(keyword, page) - if reimu_report: - await reimu.send(reimu_report) - else: - logger.error("Not found reimuInfo") - await reimu.send("没找着") - _ulmt.set_false(event.user_id) - except: - _ulmt.set_false(event.user_id) - - -@scheduler.scheduled_job( - "cron", - hour=0, - minute=1, -) -async def _(): - await UserCount.reset_count() diff --git a/plugins/reimu/data_source.py b/plugins/reimu/data_source.py deleted file mode 100644 index 926eb967a..000000000 --- a/plugins/reimu/data_source.py +++ /dev/null @@ -1,112 +0,0 @@ -from lxml import etree -import time -import aiohttp -from services.log import logger -from configs.config import MAXINFO_REIMU -from utils.user_agent import get_user_agent -from utils.utils import get_local_proxy -from asyncio.exceptions import TimeoutError - - -async def from_reimu_get_info(key_word: str, page: int) -> str: - if "miku" in key_word.lower(): - logger.warning("Taboo words are being triggered") - return '' - repass = "" - url = f"https://blog.reimu.net/search/{key_word}/page/{page}" - url_s = "https://blog.reimu.net/" - # try: - # if key_word == "最近的存档": - # logger.debug("Now starting get the {}".format(url_s)) - # repass = await get_repass(url_s) - # else: - # logger.debug("Now starting get the {}".format(url)) - # repass = await get_repass(url) - # except TimeoutError as e: - # logger.error("Timeout! {}".format(e)) - # - # return repass - - -# async def get_repass(url: str) -> str: -# repass = "" -# info = "[Note]注意大部分资源解压密码为⑨\n" -# fund = None -# print(url) -# async with aiohttp.ClientSession(headers=get_user_agent()) as session: -# async with session.get(url, proxy=get_local_proxy(), timeout=15) as response: -# html = etree.HTML(await response.text()) -# -# fund_l = html.xpath('//h1[@class="page-title"]/text()') -# if fund_l: -# fund = fund_l[0] -# if fund == "未找到": -# return "老司机也找不到路了……" -# else: -# pass -# -# headers = html.xpath("//article/header/h2/a/text()") -# urls = html.xpath("//article/header/h2/a/@href") -# logger.debug("Now get {} post from search page".format(len(headers))) -# -# headers_d = [] -# urls_d = [] -# for i, header in enumerate(headers): -# if check_need_list(header): -# headers_d.append(headers[i]) -# urls_d.append(urls[i]) -# else: -# logger.debug( -# "This title {} does not meet the requirements".format(header) -# ) -# -# header_len = len(headers_d) -# logger.debug("Get {} post after processing".format(header_len)) -# if header_len > MAXINFO_REIMU: -# headers_d = headers_d[:MAXINFO_REIMU] -# urls_d = urls_d[:MAXINFO_REIMU] -# -# for h_s, url_s in zip(headers_d, urls_d): -# if h_s != "审核结果存档": -# time.sleep(1.5) -# putline = await get_son_html_info(h_s, url_s) -# if putline: -# if repass: -# repass = "\n\n- - - - - - - - \n".join([repass, putline]) -# else: -# repass = putline -# else: -# logger.info("审核归档页面已跳过") -# -# if repass: -# repass = info + repass -# return repass -# -# -# async def get_son_html_info(h_s, url_s) -> str: -# repass = "" -# logger.debug("Now starting get the {}".format(url_s)) -# async with aiohttp.ClientSession(headers=get_user_agent()) as session: -# async with session.get(url_s, proxy=get_local_proxy(), timeout=15) as response: -# html = etree.HTML(await response.text()) -# pres = html.xpath('//div[@class="entry-content"]/pre/text()') -# a_texts = html.xpath('//div[@class="entry-content"]/pre//a/text()') -# a_hrefs = html.xpath('//div[@class="entry-content"]/pre//a/@href') -# -# if pres and a_texts and a_hrefs: -# while "" in pres: -# pres.remove("") -# -# repass = "【资源名称】 {}\n\n{}".format(h_s, pres[0].strip()) -# for i, (a_t_s, a_h_s) in enumerate(zip(a_texts, a_hrefs)): -# a = "\n {} {} {} ".format(a_t_s, a_h_s, pres[i + 1].strip()) -# repass += a -# else: -# logger.warning("Not get putline from {}".format(url_s)) -# -# return repass - - - - - diff --git a/plugins/russian/__init__.py b/plugins/russian/__init__.py index 791a7372a..afdccbff2 100644 --- a/plugins/russian/__init__.py +++ b/plugins/russian/__init__.py @@ -5,13 +5,13 @@ from nonebot.typing import T_State from utils.utils import get_message_text, is_number, get_message_at from models.group_member_info import GroupInfoUser -from utils.message_builder import at -from models.russian_user import RussianUser +from utils.message_builder import at, image +from .model import RussianUser from models.bag_user import BagUser from services.log import logger import time from .data_source import rank -from configs.config import MAX_RUSSIAN_BET_GOLD, NICKNAME +from configs.config import NICKNAME, Config __zx_plugin_name__ = "俄罗斯轮盘" __plugin_usage__ = """ @@ -47,6 +47,13 @@ "limit_superuser": False, "cmd": ["俄罗斯轮盘", "装弹"], } +__plugin_configs__ = { + "MAX_RUSSIAN_BET_GOLD": { + "value": 1000, + "help": "俄罗斯轮盘最大赌注金额", + "default_value": 1000, + } +} rs_player = {} @@ -228,7 +235,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): and rs_player[event.group_id][2] and time.time() - rs_player[event.group_id]["time"] > 30 ): - await shot.send("决斗已过时,强行结算...") + await russian.send("决斗已过时,强行结算...") await end_game(bot, event) if ( not rs_player[event.group_id][2] @@ -250,11 +257,15 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = msg[0].strip() if is_number(msg) and not (int(msg) < 1 or int(msg) > 6): state["bullet_num"] = int(msg) - if is_number(money) and 0 < int(money) <= MAX_RUSSIAN_BET_GOLD: + if is_number(money) and 0 < int(money) <= Config.get_config( + "russian", "MAX_RUSSIAN_BET_GOLD" + ): state["money"] = int(money) else: state["money"] = 200 - await russian.send(f"赌注金额超过限制({MAX_RUSSIAN_BET_GOLD}),已改为200(默认)") + await russian.send( + f"赌注金额超过限制({Config.get_config('russian', 'MAX_RUSSIAN_BET_GOLD')}),已改为200(默认)" + ) state["at"] = get_message_at(event.json()) @@ -267,8 +278,11 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): user_money = await BagUser.get_gold(event.user_id, event.group_id) if bullet_num < 0 or bullet_num > 6: await russian.reject("子弹数量必须大于0小于7!速速重新装弹!") - if money > MAX_RUSSIAN_BET_GOLD: - await russian.finish(f"太多了!单次金额不能超过{MAX_RUSSIAN_BET_GOLD}!", at_sender=True) + if money > Config.get_config("russian", "MAX_RUSSIAN_BET_GOLD"): + await russian.finish( + f"太多了!单次金额不能超过{Config.get_config('russian', 'MAX_RUSSIAN_BET_GOLD')}!", + at_sender=True, + ) if money > user_money: await russian.finish("你没有足够的钱支撑起这场挑战", at_sender=True) @@ -433,6 +447,7 @@ async def end_game(bot: Bot, event: GroupMessageEvent): for x in rs_player[event.group_id]["bullet"]: bullet_str += "__ " if x == 0 else "| " logger.info(f"俄罗斯轮盘:胜者:{win_name} - 败者:{lose_name} - 金币:{money}") + rs_player[event.group_id] = {} await bot.send( event, message=f"结算:\n" @@ -449,7 +464,6 @@ async def end_game(bot: Bot, event: GroupMessageEvent): f"哼哼,{NICKNAME}从中收取了 {float(rand)}%({fee}金币) 作为手续费!\n" f"子弹排列:{bullet_str[:-1]}", ) - rs_player[event.group_id] = {} @record.handle() @@ -471,18 +485,26 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): @russian_rank.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + num = get_message_text(event.json()) + if is_number(num) and 51 > int(num) > 10: + num = int(num) + else: + num = 10 + rank_image = None if state["_prefix"]["raw_command"] in ["胜场排行", "胜利排行"]: - await russian_rank.finish(await rank(event.group_id, "win_rank")) + rank_image = await rank(event.group_id, "win_rank", num) if state["_prefix"]["raw_command"] in ["败场排行", "失败排行"]: - await russian_rank.finish(await rank(event.group_id, "lose_rank")) + rank_image = await rank(event.group_id, "lose_rank", num) if state["_prefix"]["raw_command"] == "欧洲人排行": - await russian_rank.finish(await rank(event.group_id, "make_money")) + rank_image = await rank(event.group_id, "make_money", num) if state["_prefix"]["raw_command"] == "慈善家排行": - await russian_rank.finish(await rank(event.group_id, "spend_money")) + rank_image = await rank(event.group_id, "spend_money", num) if state["_prefix"]["raw_command"] == "最高连胜排行": - await russian_rank.finish(await rank(event.group_id, "max_winning_streak")) + rank_image = await rank(event.group_id, "max_winning_streak", num) if state["_prefix"]["raw_command"] == "最高连败排行": - await russian_rank.finish(await rank(event.group_id, "max_losing_streak")) + rank_image = await rank(event.group_id, "max_losing_streak", num) + if rank_image: + await russian_rank.send(image(b64=rank_image.pic2bs4())) # 随机子弹排列 diff --git a/plugins/russian/data_source.py b/plugins/russian/data_source.py index 9c793e6d5..35d126236 100644 --- a/plugins/russian/data_source.py +++ b/plugins/russian/data_source.py @@ -1,32 +1,34 @@ -from models.russian_user import RussianUser +from .model import RussianUser +from typing import Optional from utils.data_utils import init_rank +from utils.image_utils import CreateMat -async def rank(group_id: int, itype) -> str: +async def rank(group_id: int, itype: str, num: int) -> Optional[CreateMat]: all_users = await RussianUser.get_all_user(group_id) all_user_id = [user.user_qq for user in all_users] if itype == 'win_rank': - rank_name = '\t胜场排行榜\n' + rank_name = '胜场排行榜' all_user_data = [user.win_count for user in all_users] elif itype == 'lose_rank': - rank_name = '\t败场排行榜\n' + rank_name = '败场排行榜' all_user_data = [user.fail_count for user in all_users] elif itype == 'make_money': - rank_name = '\t赢取金币排行榜\n' + rank_name = '赢取金币排行榜' all_user_data = [user.make_money for user in all_users] elif itype == 'spend_money': - rank_name = '\t输掉金币排行榜\n' + rank_name = '输掉金币排行榜' all_user_data = [user.lose_money for user in all_users] elif itype == 'max_winning_streak': - rank_name = '\t最高连胜排行榜\n' + rank_name = '最高连胜排行榜' all_user_data = [user.max_winning_streak for user in all_users] else: - rank_name = '\t最高连败排行榜\n' + rank_name = '最高连败排行榜' all_user_data = [user.max_losing_streak for user in all_users] - rst = '' + rst = None if all_users: - rst = await init_rank(all_user_id, all_user_data, group_id) - return rank_name + rst + rst = await init_rank(rank_name, all_user_id, all_user_data, group_id, num) + return rst diff --git a/models/russian_user.py b/plugins/russian/model.py similarity index 98% rename from models/russian_user.py rename to plugins/russian/model.py index 26e5a2074..e7fec04a4 100644 --- a/models/russian_user.py +++ b/plugins/russian/model.py @@ -4,6 +4,7 @@ class RussianUser(db.Model): __tablename__ = "russian_users" + __table_args__ = {'extend_existing': True} id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) diff --git a/plugins/search_anime/__init__.py b/plugins/search_anime/__init__.py index 3bc06b469..74817b989 100644 --- a/plugins/search_anime/__init__.py +++ b/plugins/search_anime/__init__.py @@ -3,15 +3,15 @@ from services.log import logger from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State -from configs.config import MAXINFO_GROUP_ANIME, MAXINFO_PRIVATE_ANIME +from configs.config import Config from utils.utils import get_message_text +from utils.message_builder import custom_forward_msg __zx_plugin_name__ = "搜番" __plugin_usage__ = f""" usage: 搜索动漫资源 - 普通的搜番群内使用此功能只返还 {MAXINFO_GROUP_ANIME} 个结果,私聊返还 {MAXINFO_PRIVATE_ANIME} 个结果(绝不能打扰老色批们看色图!) 指令: 搜番 [番剧名称或者关键词] 示例:搜番 刀剑神域 @@ -27,8 +27,9 @@ "limit_superuser": False, "cmd": ["搜番"], } -__plugin_block_limit__ = { - "rst": "搜索还未完成,不要重复触发!" +__plugin_block_limit__ = {"rst": "搜索还未完成,不要重复触发!"} +__plugin_configs__ = { + "SEARCH_ANIME_MAX_INFO": {"value": 20, "help": "搜索动漫返回的最大数量", "default_value": 20} } search_anime = on_command("搜番", aliases={"搜动漫"}, priority=5, block=True) @@ -54,12 +55,14 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await search_anime.send(f"开始搜番 {key_word}", at_sender=True) anime_report = await from_anime_get_info( key_word, - MAXINFO_GROUP_ANIME - if isinstance(event, GroupMessageEvent) - else MAXINFO_PRIVATE_ANIME, + Config.get_config("search_anime", "SEARCH_ANIME_MAX_INFO"), ) if anime_report: - await search_anime.send(anime_report) + if isinstance(event, GroupMessageEvent): + mes_list = custom_forward_msg(anime_report, bot.self_id) + await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list) + else: + await search_anime.send("\n\n".join(anime_report)) logger.info( f"USER {event.user_id} GROUP" f" {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} 搜索番剧 {key_word} 成功" diff --git a/plugins/search_anime/data_source.py b/plugins/search_anime/data_source.py index 182e4f444..577ccf3db 100644 --- a/plugins/search_anime/data_source.py +++ b/plugins/search_anime/data_source.py @@ -2,12 +2,13 @@ import feedparser from urllib import parse from services.log import logger +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) -> str: +async def from_anime_get_info(key_word: str, max_: int) -> List[str]: s_time = time.time() repass = "" url = "https://share.dmhy.org/topics/rss/rss.xml?keyword=" + parse.quote(key_word) @@ -15,15 +16,16 @@ async def from_anime_get_info(key_word: str, max_: int) -> str: repass = await get_repass(url, max_) except Exception as e: logger.error("Timeout! {}".format(e)) + repass.insert(0, f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n") + return repass - return f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n" + repass - -async def get_repass(url: str, max_: int) -> str: - putline = [] +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: @@ -44,15 +46,11 @@ async def get_repass(url: str, max_: int) -> str: .replace("\t", "") ) size = item[3].xpath("string(.)")[5:].strip() - putline.append( + put_line.append( "【{}】| {}\n【{}】| {}".format(class_a, title, size, magent) ) except Exception as e: logger.warning(f"搜番超时 e:{e}") - - repass = "\n\n".join(putline) - - return repass + return put_line -# print(asyncio.get_event_loop().run_until_complete(from_anime_get_info('进击的巨人', 1234556))) diff --git a/plugins/search_buff_skin_price/__init__.py b/plugins/search_buff_skin_price/__init__.py index 318a824c7..42d4c9a5c 100644 --- a/plugins/search_buff_skin_price/__init__.py +++ b/plugins/search_buff_skin_price/__init__.py @@ -28,6 +28,19 @@ "limit_superuser": False, "cmd": ["查询皮肤"], } +__plugin_block_limit__ = { + "rst": "您有皮肤正在搜索,请稍等..." +} +__plugin_configs__ = { + "BUFF_PROXY": { + "value": None, + "help": "BUFF代理,有些厂ip可能被屏蔽" + }, + "COOKIE": { + "value": None, + "help": "BUFF的账号cookie" + } +} search_skin = on_command("查询皮肤", aliases={"皮肤查询"}, priority=5, block=True) @@ -45,8 +58,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if str(event.get_message()) in ["帮助"]: await search_skin.finish(__plugin_usage__) raw_arg = get_message_text(event.json()) - if _ulmt.check(event.user_id): - await search_skin.finish("您有皮肤正在搜索,请稍等...", at_sender=True) if raw_arg: args = raw_arg.split(" ") if len(args) >= 2: @@ -58,7 +69,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @search_skin.got("skin", prompt="要查询该武器的什么皮肤呢?") async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): result = "" - _ulmt.set_true(event.user_id) if state["name"] in ["ak", "ak47"]: state["name"] = "ak-47" name = state["name"] + " | " + state["skin"] @@ -67,7 +77,6 @@ async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): except FileNotFoundError: await search_skin.finish(f'请先对{NICKNAME}说"设置cookie"来设置cookie!') if status_code in [996, 997, 998]: - _ulmt.set_false(event.user_id) await search_skin.finish(result) if result: logger.info( @@ -75,7 +84,6 @@ async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询皮肤:" + name ) - _ulmt.set_false(event.user_id) await search_skin.finish(result) else: logger.info( @@ -83,7 +91,6 @@ async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" f" 查询皮肤:{name} 没有查询到" ) - _ulmt.set_false(event.user_id) await search_skin.finish("没有查询到哦,请检查格式吧") diff --git a/plugins/search_buff_skin_price/data_source.py b/plugins/search_buff_skin_price/data_source.py index 6d5032c79..3785484aa 100644 --- a/plugins/search_buff_skin_price/data_source.py +++ b/plugins/search_buff_skin_price/data_source.py @@ -1,18 +1,14 @@ from utils.user_agent import get_user_agent import aiohttp -from utils.utils import get_cookie_text -from configs.path_config import TEXT_PATH from asyncio.exceptions import TimeoutError -from configs.config import BUFF_PROXY -from pathlib import Path -from services.log import logger +from configs.config import Config url = "https://buff.163.com/api/market/goods" async def get_price(dname): - cookie = {"session": get_cookie_text("buff")} + cookie = {"session": Config.get_config("search_buff_skin_price", "COOKIE")} name_list = [] price_list = [] parameter = {"game": "csgo", "page_num": "1", "search": dname} @@ -21,7 +17,7 @@ async def get_price(dname): cookies=cookie, headers=get_user_agent() ) as session: async with session.get( - url, proxy=BUFF_PROXY, params=parameter, timeout=5 + url, proxy=Config.get_config("search_buff_skin_price", "BUFF_PROXY"), params=parameter, timeout=5 ) as response: if response.status == 200: try: @@ -36,29 +32,21 @@ async def get_price(dname): price = data[i]["sell_reference_price"] name_list.append(name) price_list.append(price) - except Exception as e: + except Exception: return "没有查询到...", 998 else: return "访问失败!", response.status - except TimeoutError as e: + except TimeoutError: return "访问超时! 请重试或稍后再试!", 997 result = f"皮肤: {dname}({len(name_list)})\n" - # result = "皮肤: " + dname + "\n" for i in range(len(name_list)): result += name_list[i] + ": " + price_list[i] + "\n" return result[:-1], 999 -def update_buff_cookie(cookie: str): - _cookie = Path(TEXT_PATH + "cookie/buff.txt") - try: - _cookie.parent.mkdir(parents=True, exist_ok=True) - with open(_cookie, "w") as f: - f.write(cookie) - return "更新cookie成功" - except Exception as e: - logger.error(f"更新cookie失败 e:{e}") - return "更新cookie失败" +def update_buff_cookie(cookie: str) -> str: + Config.set_config("search_buff_skin_price", "COOKIE", cookie) + return "更新cookie成功" if __name__ == "__main__": diff --git a/plugins/send_dinggong_voice/__init__.py b/plugins/send_dinggong_voice/__init__.py index f547ff5fe..2b584168f 100644 --- a/plugins/send_dinggong_voice/__init__.py +++ b/plugins/send_dinggong_voice/__init__.py @@ -3,7 +3,6 @@ from configs.path_config import VOICE_PATH import random from services.log import logger -from utils.utils import FreqLimiter from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.rule import to_me @@ -26,8 +25,10 @@ "limit_superuser": False, "cmd": ["骂老子", "骂我"], } - -_flmt = FreqLimiter(3) +__plugin_cd_limit__ = { + "cd": 3, + "rst": "就...就算求我骂你也得慢慢来..." +} dg_voice = on_keyword({"骂"}, rule=to_me(), priority=5, block=True) @@ -35,16 +36,13 @@ @dg_voice.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - if len(str((event.get_message()))) == 1: - return - if not _flmt.check(event.user_id): - await dg_voice.finish("就...就算求我骂你也得慢慢来...", at_sender=True) - _flmt.start_cd(event.user_id) - voice = random.choice(os.listdir(VOICE_PATH + "dinggong/")) - result = record(voice, "dinggong") - await dg_voice.send(result) - await dg_voice.send(voice.split("_")[1]) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送钉宫骂人:" - + result - ) + if len(str((event.get_message()))) > 1: + voice = random.choice(os.listdir(VOICE_PATH + "dinggong/")) + result = record(voice, "dinggong") + await dg_voice.send(result) + await dg_voice.send(voice.split("_")[1]) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送钉宫骂人:" + + result + ) diff --git a/plugins/send_setu_/__init__.py b/plugins/send_setu_/__init__.py new file mode 100644 index 000000000..9418a25bc --- /dev/null +++ b/plugins/send_setu_/__init__.py @@ -0,0 +1,4 @@ +import nonebot + + +nonebot.load_plugins("plugins/send_setu_") diff --git a/models/setu.py b/plugins/send_setu_/model.py similarity index 99% rename from models/setu.py rename to plugins/send_setu_/model.py index 4cd05883c..affe617da 100644 --- a/models/setu.py +++ b/plugins/send_setu_/model.py @@ -4,6 +4,8 @@ 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) diff --git a/plugins/send_setu/__init__.py b/plugins/send_setu_/send_setu/__init__.py similarity index 83% rename from plugins/send_setu/__init__.py rename to plugins/send_setu_/send_setu/__init__.py index 605ac3da4..2d4ce1fc3 100644 --- a/plugins/send_setu/__init__.py +++ b/plugins/send_setu_/send_setu/__init__.py @@ -1,330 +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 ONLY_USE_LOCAL_SETU, WITHDRAW_SETU_TIME, 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': '您冲的太快了,请稍后再冲.', -} - -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(event, msg_id["message_id"]) - 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 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, _ = await get_setu_list(tags=tags, r18=r18) - 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(event, msg_id["message_id"]) - 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(event, msg_id["message_id"]) - 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) - - -# 撤回图片 -def withdraw_message(event: MessageEvent, id_: int): - if WITHDRAW_SETU_TIME[0]: - if ( - (WITHDRAW_SETU_TIME[1] == 0 and isinstance(event, PrivateMessageEvent)) - or (WITHDRAW_SETU_TIME[1] == 1 and isinstance(event, GroupMessageEvent)) - or WITHDRAW_SETU_TIME[1] == 2 - ): - withdraw_message_manager.append((id_, WITHDRAW_SETU_TIME[0])) +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/data_source.py b/plugins/send_setu_/send_setu/data_source.py similarity index 91% rename from plugins/send_setu/data_source.py rename to plugins/send_setu_/send_setu/data_source.py index d59e6194e..4a7998a64 100644 --- a/plugins/send_setu/data_source.py +++ b/plugins/send_setu_/send_setu/data_source.py @@ -1,273 +1,276 @@ -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 INITIAL_SETU_PROBABILITY, NICKNAME, DOWNLOAD_SETU -from models.setu 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=2, 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_: 存储路径 - """ - if "i.pixiv.cat" in url_: - url_ = url_.replace("i.pixiv.cat", "i.pximg.net") - 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=3) 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 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): - 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 + 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=5) 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 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: + url_ = 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 diff --git a/plugins/update_setu/__init__.py b/plugins/send_setu_/update_setu/__init__.py similarity index 88% rename from plugins/update_setu/__init__.py rename to plugins/send_setu_/update_setu/__init__.py index 48d86b75a..aa048f9be 100644 --- a/plugins/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 DOWNLOAD_SETU - - -__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 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 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/update_setu/data_source.py b/plugins/send_setu_/update_setu/data_source.py similarity index 90% rename from plugins/update_setu/data_source.py rename to plugins/send_setu_/update_setu/data_source.py index d50f852a8..dbb508e30 100644 --- a/plugins/update_setu/data_source.py +++ b/plugins/send_setu_/update_setu/data_source.py @@ -1,164 +1,171 @@ -from configs.path_config import IMAGE_PATH, TEXT_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 models.setu import Setu -from aiohttp.client_exceptions import ClientConnectorError -from asyncpg.exceptions import UniqueViolationError -from pathlib import Path -from nonebot import Driver -import nonebot -import aiofiles -import aiohttp -import os -import ujson as json - -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} 条") - - -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 / "r18_rar" if image.is_r18 else _path / "rar" - 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: - for _ in range(3): - try: - async with session.get( - image.img_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 - 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, get_local_proxy +from asyncio.exceptions import TimeoutError +from ..model import Setu +from aiohttp.client_exceptions import ClientConnectorError +from asyncpg.exceptions import UniqueViolationError +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: + for _ in range(3): + try: + async with session.get( + image.img_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), + ) + + diff --git a/plugins/shop/__init__.py b/plugins/shop/__init__.py index e0fbec0a1..43482d7ac 100644 --- a/plugins/shop/__init__.py +++ b/plugins/shop/__init__.py @@ -1,84 +1,14 @@ -from pathlib import Path -from configs.path_config import DATA_PATH import nonebot -try: - import ujson as json -except ModuleNotFoundError: - import json +from configs.config import Config -nonebot.load_plugins("plugins/shop") - -# 修改旧数据 - -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('我的道具') 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('我的道具') 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('我的道具') 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) +Config.add_plugin_config( + "shop", + "IMPORT_DEFAULT_SHOP_GOODS", + True, + help_="导入商店自带的三个商品", + default_value=True +) +nonebot.load_plugins("plugins/shop") diff --git a/plugins/shop/buy.py b/plugins/shop/buy.py index 5968360cd..7518ccc60 100644 --- a/plugins/shop/buy.py +++ b/plugins/shop/buy.py @@ -6,7 +6,7 @@ from models.bag_user import BagUser from services.db_context import db from nonebot.adapters.cqhttp.permission import GROUP -from models.goods_info import GoodsInfo +from .models.goods_info import GoodsInfo __zx_plugin_name__ = "商店 - 购买道具" diff --git a/plugins/shop/gold.py b/plugins/shop/gold.py index e139a23c7..688df78bc 100644 --- a/plugins/shop/gold.py +++ b/plugins/shop/gold.py @@ -4,6 +4,8 @@ from nonebot.adapters.cqhttp.permission import GROUP from utils.data_utils import init_rank from models.bag_user import BagUser +from utils.message_builder import image +from utils.utils import get_message_text, is_number __zx_plugin_name__ = "商店 - 我的金币" __plugin_usage__ = """ @@ -14,7 +16,7 @@ """.strip() __plugin_des__ = "商店 - 我的金币" __plugin_cmd__ = ["我的金币"] -__plugin_type__ = ('商店',) +__plugin_type__ = ("商店",) __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { @@ -37,9 +39,14 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): @gold_rank.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + num = get_message_text(event.json()) + if is_number(num) and 51 > int(num) > 10: + num = int(num) + else: + num = 10 all_users = await BagUser.get_all_users(event.group_id) all_user_id = [user.user_qq for user in all_users] all_user_data = [user.gold for user in all_users] - await gold_rank.finish( - "金币排行:\n" + await init_rank(all_user_id, all_user_data, event.group_id) - ) + rank_image = await init_rank("金币排行", all_user_id, all_user_data, event.group_id, num) + if rank_image: + await gold_rank.finish(image(b64=rank_image.pic2bs4())) diff --git a/plugins/shop/gold_redbag/__init__.py b/plugins/shop/gold_redbag/__init__.py index a7be458ea..2c20c7a32 100644 --- a/plugins/shop/gold_redbag/__init__.py +++ b/plugins/shop/gold_redbag/__init__.py @@ -19,6 +19,7 @@ from utils.utils import get_message_text, is_number, scheduler from utils.message_builder import image from services.log import logger +from configs.path_config import IMAGE_PATH from nonebot.permission import SUPERUSER from nonebot.rule import to_me from datetime import datetime, timedelta @@ -49,6 +50,9 @@ "limit_superuser": False, "cmd": ["金币红包", "塞红包"], } +__plugin_resources__ = { + "prts": IMAGE_PATH +} gold_redbag = on_command( "塞红包", aliases={"金币红包"}, priority=5, block=True, permission=GROUP diff --git a/plugins/shop/gold_redbag/data_source.py b/plugins/shop/gold_redbag/data_source.py index fb14c0947..466402951 100644 --- a/plugins/shop/gold_redbag/data_source.py +++ b/plugins/shop/gold_redbag/data_source.py @@ -3,7 +3,7 @@ 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 ..models.redbag_user import RedbagUser import random import os import aiohttp diff --git a/plugins/shop/models/__init__.py b/plugins/shop/models/__init__.py new file mode 100644 index 000000000..db1ab9efa --- /dev/null +++ b/plugins/shop/models/__init__.py @@ -0,0 +1 @@ +from .goods_info import * diff --git a/models/goods_info.py b/plugins/shop/models/goods_info.py similarity index 100% rename from models/goods_info.py rename to plugins/shop/models/goods_info.py diff --git a/models/redbag_user.py b/plugins/shop/models/redbag_user.py similarity index 100% rename from models/redbag_user.py rename to plugins/shop/models/redbag_user.py diff --git a/plugins/shop/shop_handle/data_source.py b/plugins/shop/shop_handle/data_source.py index b9b5d479f..915839d4e 100644 --- a/plugins/shop/shop_handle/data_source.py +++ b/plugins/shop/shop_handle/data_source.py @@ -1,8 +1,8 @@ -from models.goods_info import GoodsInfo +from ..models.goods_info import GoodsInfo from utils.image_utils import CreateImg from utils.utils import is_number from configs.path_config import IMAGE_PATH -from configs.config import IMPORT_DEFAULT_SHOP_GOODS +from configs.config import Config from nonebot import Driver import nonebot import time @@ -17,7 +17,7 @@ async def init_default_shop_goods(): if os.path.exists(f"{IMAGE_PATH}/shop_help.png"): os.remove(f"{IMAGE_PATH}/shop_help.png") - if IMPORT_DEFAULT_SHOP_GOODS: + if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"): await add_goods(["好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"]) await add_goods(["好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)"]) await add_goods(["好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"]) diff --git a/plugins/sign_in/__init__.py b/plugins/sign_in/__init__.py index d0df66f16..cece39ab5 100644 --- a/plugins/sign_in/__init__.py +++ b/plugins/sign_in/__init__.py @@ -6,7 +6,6 @@ ) from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from utils.manager import plugins2cd_manager from nonebot.adapters.cqhttp.permission import GROUP from utils.message_builder import image from nonebot import on_command @@ -15,6 +14,7 @@ from configs.path_config import DATA_PATH from services.log import logger from .utils import clear_sign_data_pic +from utils.utils import is_number try: import ujson as json @@ -43,9 +43,30 @@ "limit_superuser": False, "cmd": ["签到"], } -plugins2cd_manager.add_cd_limit( - 'sign_in', -) +__plugin_cd_limit__ = {} +__plugin_configs__ = { + "MAX_SIGN_GOLD": { + "value": 200, + "help": "签到好感度加成额外获得的最大金币数", + "default_value": 200 + }, + "SIGN_CARD1_PROB": { + "value": 0.2, + "help": "签到好感度双倍加持卡Ⅰ掉落概率", + "default_value": 0.2 + }, + "SIGN_CARD2_PROB": { + "value": 0.09, + "help": "签到好感度双倍加持卡Ⅱ掉落概率", + "default_value": 0.09 + }, + "SIGN_CARD3_PROB": { + "value": 0.05, + "help": "签到好感度双倍加持卡Ⅲ掉落概率", + "default_value": 0.05 + } +} + _file = Path(f"{DATA_PATH}/not_show_sign_rank_user.json") try: @@ -90,7 +111,14 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): @sign_rank.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await sign_rank.send(await group_impression_rank(event.group_id)) + num = get_message_text(event.json()) + if is_number(num) and 51 > int(num) > 10: + num = int(num) + else: + num = 10 + _image = await group_impression_rank(event.group_id, num) + if _image: + await sign_rank.send(image(b64=_image.pic2bs4())) @total_sign_rank.handle() diff --git a/plugins/sign_in/config.py b/plugins/sign_in/config.py index 1e662bcbf..682329549 100644 --- a/plugins/sign_in/config.py +++ b/plugins/sign_in/config.py @@ -10,16 +10,6 @@ SIGN_BORDER_PATH.mkdir(exist_ok=True, parents=True) SIGN_BACKGROUND_PATH.mkdir(exist_ok=True, parents=True) -SIGN_CARD1_PROB = 0.2 # 好感度双倍加持卡Ⅰ -SIGN_CARD2_PROB = 0.09 # 好感度双倍加持卡Ⅱ -SIGN_CARD3_PROB = 0.05 # 好感度双倍加持卡Ⅲ - -PROB_DATA = { - SIGN_CARD3_PROB: '好感度双倍加持卡Ⅲ', - SIGN_CARD2_PROB: '好感度双倍加持卡Ⅱ', - SIGN_CARD1_PROB: '好感度双倍加持卡Ⅰ' -} - lik2relation = { '0': '路人', diff --git a/plugins/sign_in/group_user_checkin.py b/plugins/sign_in/group_user_checkin.py index a7a10863d..18150ab49 100644 --- a/plugins/sign_in/group_user_checkin.py +++ b/plugins/sign_in/group_user_checkin.py @@ -5,11 +5,13 @@ from configs.config import NICKNAME from nonebot.adapters.cqhttp import MessageSegment from asyncio.exceptions import TimeoutError -from utils.image_utils import CreateImg +from utils.image_utils import CreateImg, CreateMat from services.db_context import db from .utils import get_card, SIGN_TODAY_CARD_PATH +from typing import Optional from services.log import logger from .random_event import random_event +from utils.data_utils import init_rank from io import BytesIO import random import aiohttp @@ -82,31 +84,9 @@ async def group_user_check(nickname: str, user_qq: int, group: int) -> MessageSe return await get_card(user, nickname, None, gold, "", is_card_view=True) -async def group_impression_rank(group: int) -> str: - result = "\t好感度排行榜\t\n" +async def group_impression_rank(group: int, num: int) -> Optional[CreateMat]: user_qq_list, impression_list, _ = await SignGroupUser.get_all_impression(group) - _count = 11 - if user_qq_list and impression_list: - for i in range(1, len(user_qq_list)): - if len(user_qq_list) == 0 or len(impression_list) == 0 or i == _count: - break - impression = max(impression_list) - index = impression_list.index(impression) - user_qq = user_qq_list[index] - try: - user_name = ( - await GroupInfoUser.get_member_info(user_qq, group) - ).user_name - except AttributeError: - logger.info(f"USER {user_qq}, GROUP {group} 不在群内") - _count += 1 - impression_list.remove(impression) - user_qq_list.remove(user_qq) - continue - result += f"{i - _count + 11}. {user_name}: {impression:.2f}\n" - impression_list.remove(impression) - user_qq_list.remove(user_qq) - return result[:-1] + return await init_rank("好感度排行榜", user_qq_list, impression_list, group, num) async def random_gold(user_id, group_id, impression): diff --git a/plugins/sign_in/random_event.py b/plugins/sign_in/random_event.py index f1dda0449..c5ea35a86 100644 --- a/plugins/sign_in/random_event.py +++ b/plugins/sign_in/random_event.py @@ -1,21 +1,30 @@ -from configs.config import MAX_SIGN_GOLD -from typing import Union -from .config import PROB_DATA +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))) - gold = MAX_SIGN_GOLD if gold > MAX_SIGN_GOLD else gold + 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 index 34092e9b7..2bffa5eaa 100644 --- a/plugins/sign_in/utils.py +++ b/plugins/sign_in/utils.py @@ -11,6 +11,7 @@ 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 @@ -22,7 +23,6 @@ import asyncio import random import nonebot -import aiohttp import os @@ -41,13 +41,6 @@ async def init_image(): clear_sign_data_pic() -async def _get_pic(qq): - url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100" - async with aiohttp.ClientSession() as session: - async with session.get(url, timeout=5) as response: - return await response.read() - - async def get_card( user: "SignGroupUser", nickname: str, @@ -80,7 +73,7 @@ async def get_card( "sign/today_card", ) is_card_view = True - ava = BytesIO(await _get_pic(user_id)) + ava = BytesIO(await get_user_avatar(user_id)) uid = await GroupInfoUser.get_group_member_uid( user.user_qq, user.belonging_group ) diff --git a/plugins/statistics/__init__.py b/plugins/statistics/__init__.py index 6b1c8b3c9..590246d9e 100644 --- a/plugins/statistics/__init__.py +++ b/plugins/statistics/__init__.py @@ -2,6 +2,10 @@ 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") @@ -13,3 +17,112 @@ 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 index c0623d44c..6626bc2ce 100644 --- a/plugins/statistics/statistics_handle.py +++ b/plugins/statistics/statistics_handle.py @@ -105,15 +105,19 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): itype = 'total_statistics' tmp_dict = {} data = data[itype] - 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] + 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())) @@ -273,3 +277,4 @@ def update_data(data: dict): 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 index e5bc57d1e..b2f0b2d60 100644 --- a/plugins/statistics/statistics_hook.py +++ b/plugins/statistics/statistics_hook.py @@ -97,7 +97,7 @@ async def _( if ( matcher.type == "message" and matcher.priority not in [1, 9] - and matcher.module not in ["update_info"] + and matcher.module not in ["update_info", "statistics_handle"] ): module = matcher.module day_index = _prefix_count_dict["day_index"] @@ -153,7 +153,7 @@ def check_exists_key(group_id: str, user_id: str, plugin_name: str): 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): @@ -163,23 +163,24 @@ def check_exists_key(group_id: str, user_id: str, plugin_name: str): if not data["day_statistics"][key].get(plugin_name): data["day_statistics"][key][plugin_name] = 0 - 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 + 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 # 天 @@ -213,3 +214,4 @@ async def _(): 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/super_cmd/__init__.py b/plugins/super_cmd/__init__.py deleted file mode 100644 index 781ddcbbf..000000000 --- a/plugins/super_cmd/__init__.py +++ /dev/null @@ -1,289 +0,0 @@ -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, MessageEvent, Message -from nonebot.rule import to_me -from utils.utils import get_message_at, get_message_text, is_number, get_bot, scheduler -from pathlib import Path -from services.log import logger -from models.group_info import GroupInfo -from models.friend_user import FriendUser -from utils.message_builder import at -from configs.path_config import IMAGE_PATH -from utils.manager import plugins2cd_manager, plugins2settings_manager, plugins2block_manager, group_manager -import asyncio -import os - - -__zx_plugin_name__ = "超级用户指令 [Superuser]" -__plugin_usage__ = """ -usage: - 超级用户集成指令 - 指令: - 添加权限 [at] [权限] - 删除权限 [at] - 开启/关闭广播通知 [group] - 查看所有好友/查看所有群组 - 退群 [group] - 更新群信息 - 更新好友信息 - 清理临时数据 - 重载插件配置 -""".strip() -__plugin_des__ = "超级用户集成指令" -__plugin_cmd__ = [ - "添加权限 [at] [权限]", - "删除权限 [at]", - "开启/关闭广播通知 [group]", - "查看所有好友/查看所有群组", - "退群 [group]", - "更新群信息", - "更新好友信息", - "清理临时数据", - "重载插件配置" -] -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -super_cmd = on_command( - "添加管理", - aliases={"删除管理", "添加权限", "删除权限"}, - rule=to_me(), - priority=1, - permission=SUPERUSER, - block=True, -) -oc_gb = on_command( - "开启广播通知", - aliases={"关闭广播通知"}, - rule=to_me(), - permission=SUPERUSER, - priority=1, - block=True, -) -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 -) -del_group = on_command("退群", rule=to_me(), permission=SUPERUSER, priority=1, block=True) -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 -) -clear_data = on_command( - "清理临时数据", rule=to_me(), permission=SUPERUSER, priority=1, block=True -) -reload_plugins_manager = on_command('重载插件配置', rule=to_me(), permission=SUPERUSER, priority=1, block=True) - - -@super_cmd.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - 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 = int(qq[0]) - group_id = event.group_id - flag = 2 - if is_number(args[0]): - level = int(args[0]) - else: - await super_cmd.finish("权限等级必须是数字!", at_sender=True) - if state["_prefix"]["raw_command"][:2] == "添加": - 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}") - - -@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) - # try: - 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) - # except Exception as e: - # await oc_gb.finish(f'关闭 {group} 的广播失败', at_sender=True) - else: - await oc_gb.finish("请输入正确的群号", at_sender=True) - else: - await oc_gb.finish("请输入要关闭广播的群号", at_sender=True) - - -@del_group.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - group = get_message_text(event.json()) - if group: - if is_number(group): - try: - await bot.set_group_leave(group_id=int(group)) - logger.info(f"退出群聊 {group} 成功") - await del_group.finish(f"退出群聊 {group} 成功", at_sender=True) - except Exception as e: - logger.info(f"退出群聊 {group} 失败 e:{e}") - else: - await del_group.finish(f"请输入正确的群号", at_sender=True) - else: - await del_group.finish(f"请输入群号", at_sender=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, - ) - - -@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]}") - - -@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)) - - -@reload_plugins_manager.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - plugins2settings_manager.reload() - plugins2cd_manager.reload() - plugins2block_manager.reload() - try: - (Path(IMAGE_PATH) / "help.png").unlink() - except FileNotFoundError: - pass - await reload_plugins_manager.send(f'重载插件配置完成...\n' - f'已加载 {len(plugins2settings_manager.get_data())} 个非限制插件\n' - f'已加载 {len(plugins2cd_manager.get_data())} 个Cd限制\n' - f'已加载 {len(plugins2block_manager.get_data())} 个Block限制') - - -def _clear_data() -> float: - size = 0 - for dir_name in ["temp", "rar", "r18_rar"]: - dir_name = f"{IMAGE_PATH}/{dir_name}" - if os.path.exists(dir_name): - for file in os.listdir(dir_name): - try: - file_size = os.path.getsize(os.path.join(dir_name, file)) - os.remove(os.path.join(dir_name, file)) - except Exception as e: - logger.error(f"清理临时数据错误...e:{e}") - file_size = 0 - size += file_size - 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/plugins/update_gocqhttp/__init__.py b/plugins/update_gocqhttp/__init__.py index 6a088d3bb..d910e5963 100644 --- a/plugins/update_gocqhttp/__init__.py +++ b/plugins/update_gocqhttp/__init__.py @@ -6,7 +6,7 @@ from services.log import logger from utils.utils import scheduler, get_bot from nonebot.permission import SUPERUSER -from configs.config import UPDATE_GOCQ_GROUP +from configs.config import Config from pathlib import Path @@ -20,7 +20,13 @@ __plugin_cmd__ = ["更新gocq"] __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" - +__plugin_configs__ = { + "UPDATE_GOCQ_GROUP": { + "value": [], + "help": "需要为哪些群更新最新版gocq吗?(上传最新版gocq)示例:[434995955, 239483248]", + "default_value": [], + } +} path = str((Path() / "resources" / "gocqhttp_file").absolute()) + "/" @@ -30,7 +36,7 @@ @lasted_gocqhttp.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): # try: - if event.group_id in UPDATE_GOCQ_GROUP: + if event.group_id in Config.get_config("update_gocqhttp", "UPDATE_GOCQ_GROUP"): await lasted_gocqhttp.send("检测中...") info = await download_gocq_lasted(path) if info == "gocqhttp没有更新!": @@ -51,14 +57,14 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): minute=1, ) async def _(): - if UPDATE_GOCQ_GROUP: + if Config.get_config("update_gocqhttp", "UPDATE_GOCQ_GROUP"): bot = get_bot() try: info = await download_gocq_lasted(path) if info == "gocqhttp没有更新!": logger.info("gocqhttp没有更新!") return - for group in UPDATE_GOCQ_GROUP: + for group in Config.get_config("update_gocqhttp", "UPDATE_GOCQ_GROUP"): for file in os.listdir(path): await upload_gocq_lasted(path, file, group) await bot.send_group_msg( diff --git a/plugins/weather/__init__.py b/plugins/weather/__init__.py index 075d9df6a..e65b1f63d 100644 --- a/plugins/weather/__init__.py +++ b/plugins/weather/__init__.py @@ -1,5 +1,5 @@ from nonebot import on_regex -from .data_source import get_weather_of_city, update_city, get_city_list +from .data_source import get_weather_of_city, get_city_list from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from jieba import posseg from services.log import logger @@ -28,7 +28,7 @@ } -weather = on_regex(r".*?(.*)市?的?天气.*?", priority=5, block=True) +weather = on_regex(r".{0,10}市?的?天气.{0,10}", priority=5, block=True) @weather.handle() diff --git a/plugins/weather/data_source.py b/plugins/weather/data_source.py index 0fd8bf27c..a787cf671 100644 --- a/plugins/weather/data_source.py +++ b/plugins/weather/data_source.py @@ -1,9 +1,7 @@ -from services.log import logger 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 asyncio.exceptions import TimeoutError from typing import List from nonebot import Driver from pathlib import Path @@ -23,7 +21,7 @@ async def get_weather_of_city(city: str) -> str: if code == 999: return "不要查一个省份的天气啊,很累人的!" elif code == 998: - return f"{NICKNAME}只可以查询国内城市的天气喔..." + return f"{NICKNAME}没查到!!试试查火星的天气?" else: async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get( @@ -32,7 +30,7 @@ async def get_weather_of_city(city: str) -> str: data_json = json.loads(await res.text(encoding="utf8")) if "desc" in data_json: if data_json["desc"] == "invilad-citykey": - return f"你为啥不查火星的天气呢?{NICKNAME}只支持国内天气查询!!" + image( + return f"{NICKNAME}没查到!!试试查火星的天气?" + image( "shengqi", "zhenxun" ) elif data_json["desc"] == "OK": @@ -48,34 +46,9 @@ async def get_weather_of_city(city: str) -> str: return "好像出错了?" -# 更新城市 -@driver.on_startup -async def update_city(): - global 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("自动更新城市列表超时.....") - - # 城市是否存在或是否是省份 def _check_exists_city(city: str) -> int: + global data city = city if city[-1] != "市" else city[:-1] for province in data.keys(): for city_ in data[province]: @@ -99,4 +72,5 @@ def get_city_list() -> List[str]: for p in data.keys(): for c in data[p]: city_list.append(c) + city_list.append(p) return city_list diff --git a/plugins/what_anime/__init__.py b/plugins/what_anime/__init__.py index b3f09e849..c4c9006d3 100644 --- a/plugins/what_anime/__init__.py +++ b/plugins/what_anime/__init__.py @@ -15,6 +15,7 @@ """.strip() __plugin_des__ = "以图识番" __plugin_cmd__ = ["识番 [图片]"] +__plugin_type__ = ("一些工具",) __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { diff --git a/plugins/white2black_img.py b/plugins/white2black_img.py index 3519948b3..2d5e8e5e8 100644 --- a/plugins/white2black_img.py +++ b/plugins/white2black_img.py @@ -1,7 +1,7 @@ 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_local_proxy, get_message_text, is_chinese from utils.message_builder import image import aiohttp import aiofiles @@ -134,11 +134,11 @@ async def get_translate(msg: str) -> str: def formalization_msg(msg: str) -> str: rst = "" for i in range(len(msg)): - if is_Chinese(msg[i]): + if is_chinese(msg[i]): rst += msg[i] + " " else: rst += msg[i] - if i + 1 < len(msg) and is_Chinese(msg[i + 1]) and msg[i].isalpha(): + if i + 1 < len(msg) and is_chinese(msg[i + 1]) and msg[i].isalpha(): rst += " " return rst diff --git a/plugins/yiqing/data_source.py b/plugins/yiqing/data_source.py index 38e6eb9ea..f56c638e0 100644 --- a/plugins/yiqing/data_source.py +++ b/plugins/yiqing/data_source.py @@ -82,7 +82,7 @@ def get_city_and_province_list() -> List[str]: data = json.load(f) except FileNotFoundError: data = {} - city_list = [] + city_list = ["中国"] for p in data.keys(): for c in data[p]: city_list.append(c) diff --git a/services/__init__.py b/services/__init__.py index 15a269450..aae2c0934 100644 --- a/services/__init__.py +++ b/services/__init__.py @@ -1,3 +1,2 @@ from .db_context import * from .log import * -from .service_config import * \ No newline at end of file diff --git a/services/db_context.py b/services/db_context.py index 3549ace63..0911a4bba 100644 --- a/services/db_context.py +++ b/services/db_context.py @@ -2,7 +2,6 @@ from gino import Gino from .log import logger from configs.config import bind, sql_name, user, password, address, port, database -from .service_config import DATABASE_URI # 全局数据库连接对象 @@ -10,7 +9,7 @@ async def init(): - i_bind = DATABASE_URI if DATABASE_URI else bind + i_bind = bind if not i_bind: i_bind = f"{sql_name}://{user}:{password}@{address}:{port}/{database}" try: diff --git a/services/service_config.py b/services/service_config.py deleted file mode 100644 index 2d89d872e..000000000 --- a/services/service_config.py +++ /dev/null @@ -1,11 +0,0 @@ -import os - -DATABASE_URI = os.environ.get('DATABASE_URI') - -TL_M_KEY = os.environ.get('TL_KEY') - -SYSTEM_M_PROXY = os.environ.get('SYSTEM_PROXY') - -ALAPI_M_TOKEN = os.environ.get('ALAPI_TOKEN') - - diff --git a/update_info.json b/update_info.json index 1e0dfab01..76ac52a98 100644 --- a/update_info.json +++ b/update_info.json @@ -5,9 +5,8 @@ "models", "services", "utils", - "resources/ttf", "bot.py" ], - "add_file": ["resources/img/background"], + "add_file": ["basic_plugins"], "delete_file": [] } diff --git a/utils/data_utils.py b/utils/data_utils.py index b3c8cfa56..cc4a7bafd 100644 --- a/utils/data_utils.py +++ b/utils/data_utils.py @@ -1,31 +1,68 @@ from models.group_member_info import GroupInfoUser -from typing import List +from utils.image_utils import CreateMat +from configs.path_config import IMAGE_PATH +from typing import List, Union +import asyncio +import os -async def init_rank(all_user_id: List[int], all_user_data: List[int], group_id: int) -> str: +async def init_rank( + title: str, all_user_id: List[int], all_user_data: List[int], group_id: int, total_count: int = 10 +) -> CreateMat: """ 说明: 初始化通用的数据排行榜 参数: + :param title: 排行榜标题 :param all_user_id: 所有用户的qq号 :param all_user_data: 所有用户需要排行的对应数据 :param group_id: 群号,用于从数据库中获取该用户在此群的昵称 + :param total_count: 获取人数总数 """ - rst = '' - for i in range(len(all_user_id) if len(all_user_id) < 10 else 10): + _uname_lst = [] + _num_lst = [] + for i in range(len(all_user_id) if len(all_user_id) < total_count else total_count): _max = max(all_user_data) max_user_id = all_user_id[all_user_data.index(_max)] all_user_id.remove(max_user_id) all_user_data.remove(_max) try: - user_name = (await GroupInfoUser.get_member_info(max_user_id, group_id)).user_name + user_name = ( + await GroupInfoUser.get_member_info(max_user_id, group_id) + ).user_name except AttributeError: - user_name = f'{max_user_id}' - rst += f'{user_name}: {_max}\n' - return rst[:-1] - - - - + user_name = f"{max_user_id}" + _uname_lst.append(user_name) + _num_lst.append(_max) + _uname_lst.reverse() + _num_lst.reverse() + return await asyncio.get_event_loop().run_in_executor( + None, _init_rank_graph, title, _uname_lst, _num_lst + ) +def _init_rank_graph( + title: str, _uname_lst: List[str], _num_lst: List[Union[int, float]] +) -> CreateMat: + """ + 生成排行榜统计图 + :param title: 排行榜标题 + :param _uname_lst: 用户名列表 + :param _num_lst: 数值列表 + """ + image = CreateMat( + y=_num_lst, + y_name="* 可以在命令后添加数字来指定排行人数 至多 50 *", + mat_type="barh", + title=title, + x_index=_uname_lst, + display_num=True, + x_rotate=30, + background=[ + f"{IMAGE_PATH}/background/create_mat/{x}" + for x in os.listdir(f"{IMAGE_PATH}/background/create_mat") + ], + bar_color=["*"], + ) + image.gen_graph() + return image diff --git a/utils/image_utils.py b/utils/image_utils.py index 4075978c8..6db72d32f 100644 --- a/utils/image_utils.py +++ b/utils/image_utils.py @@ -416,6 +416,7 @@ def transparent(self, alpha_ratio: float = 1, n: int = 0): color = self.markImg.getpixel((i, k)) color = color[:-1] + (int(100 * alpha_ratio),) self.markImg.putpixel((i, k), color) + self.draw = ImageDraw.Draw(self.markImg) def pic2bs4(self) -> str: """ @@ -501,26 +502,38 @@ def circle_corner(self, radii: int = 30): :param radii: 半径 """ # 画圆(用于分离4个角) - circle = Image.new('L', (radii * 2, radii * 2), 0) + circle = Image.new("L", (radii * 2, radii * 2), 0) draw = ImageDraw.Draw(circle) draw.ellipse((0, 0, radii * 2, radii * 2), fill=255) self.markImg = self.markImg.convert("RGBA") w, h = self.markImg.size - alpha = Image.new('L', self.markImg.size, 255) + alpha = Image.new("L", self.markImg.size, 255) alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0)) alpha.paste(circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0)) - alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii)) + alpha.paste( + circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii) + ) alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii)) self.markImg.putalpha(alpha) - def rotate(self, angle: int): + def rotate(self, angle: int, expand: bool = False): """ 说明: 旋转图片 + 参数: + :param angle: 角度 + :param expand: 放大图片适应角度 + """ + self.markImg = self.markImg.rotate(angle, expand=expand) + + def transpose(self, angle: int): + """ + 说明: + 旋转图片(包括边框) 参数: :param angle: 角度 """ - self.markImg = self.markImg.rotate(angle) + self.markImg.transpose(angle) def filter(self, filter_: str, aud: int = None): """ @@ -529,21 +542,22 @@ def filter(self, filter_: str, aud: int = None): :param aud: 利率 """ _x = None - if filter_ == 'GaussianBlur': # 高斯模糊 + if filter_ == "GaussianBlur": # 高斯模糊 _x = ImageFilter.GaussianBlur - elif filter_ == 'EDGE_ENHANCE': # 锐化效果 + elif filter_ == "EDGE_ENHANCE": # 锐化效果 _x = ImageFilter.EDGE_ENHANCE - elif filter_ == 'BLUR': # 模糊效果 + elif filter_ == "BLUR": # 模糊效果 _x = ImageFilter.BLUR - elif filter_ == 'CONTOUR': # 铅笔滤镜 + elif filter_ == "CONTOUR": # 铅笔滤镜 _x = ImageFilter.CONTOUR - elif filter_ == 'FIND_EDGES': # 边缘检测 + elif filter_ == "FIND_EDGES": # 边缘检测 _x = ImageFilter.FIND_EDGES if _x: if aud: self.markImg = self.markImg.filter(_x(aud)) else: self.markImg = self.markImg.filter(_x) + self.draw = ImageDraw.Draw(self.markImg) # def getchannel(self, type_): @@ -565,9 +579,11 @@ def __init__( y_name: Optional[str] = None, x_index: List[Union[str, int, float]] = None, y_index: List[Union[str, int, float]] = None, + x_rotate: int = 0, title: Optional[str] = None, size: Tuple[int, int] = (1000, 1000), - font_size: int = 20, + font: str = "msyh.ttf", + font_size: Optional[int] = None, display_num: bool = False, is_grid: bool = False, background: Optional[List[str]] = None, @@ -584,8 +600,10 @@ def __init__( :param y_name: 纵坐标名称 :param x_index: 横坐标值 :param y_index: 纵坐标值 + :param x_rotate: 横坐标旋转角度 :param title: 标题 :param size: 图像大小,建议默认 + :param font: 字体 :param font_size: 字体大小,建议默认 :param display_num: 是否显示数值 :param is_grid: 是否添加栅格 @@ -603,8 +621,9 @@ def __init__( self.y_name = y_name self.x_index = x_index self.y_index = y_index + self.x_rotate = x_rotate self.title = title - self.font_size = font_size + self.font = font self.display_num = display_num self.is_grid = is_grid self.background = background @@ -616,6 +635,10 @@ def __init__( self.line_length = 760 self._deviation = 0.905 self._color = {} + if not font_size: + self.font_size = int(25 * (1 - len(x_index) / 100)) + else: + self.font_size = font_size if self.bar_color == ["*"]: self.bar_color = [ "#FF0000", @@ -629,14 +652,11 @@ def __init__( if not x_index: raise ValueError("缺少 x_index [横坐标值]...") self._x_interval = int((self.line_length - 70) / len(x_index)) - self._bar_width = int( - (self.line_length - (len(x_index) * (10 if len(x_index) >= 18 else 25))) - / len(x_index) - ) + self._bar_width = int(30 * (1 - (len(x_index) + 10) / 100)) # 没有 y_index 时自动生成 if not y_index: _y_index = [] - _max_value = max(y) + _max_value = int(max(y)) _max_value = ceil( _max_value / eval("1" + "0" * (len(str(_max_value)) - 1)) ) * eval("1" + "0" * (len(str(_max_value)) - 1)) @@ -793,9 +813,9 @@ def _gen_line_graph( self.markImg.text( ( current_w - w, - current_h - int(y[i] * self._p * self._deviation) - 25, + current_h - int(y[i] * self._p * self._deviation) - 25 - 5, ), - str(y[i]), + f"{y[i]:.2f}" if isinstance(y[i], float) else f"{y[i]}", ) self.markImg.paste( _black_point, @@ -840,15 +860,17 @@ def _gen_bar_graph( current_w = self.padding_w + _interval current_h = self.padding_h + self.line_length for i in range(len(y)): + # 画出显示数字 if display_num: + # 横柱状图 if is_barh: font_h = self.markImg.getsize(str(y[i]))[1] self.markImg.text( ( - self.padding_w + int(y[i] * self._p * self._deviation) + 2, - current_h - int(font_h / 2), + self.padding_w + int(y[i] * self._p * self._deviation) + 2 + 5, + current_h - int(font_h / 2) - 1, ), - str(y[i]), + f"{y[i]:.2f}" if isinstance(y[i], float) else f"{y[i]}", ) else: w = int(self.markImg.getsize(str(y[i]))[0] / 2) @@ -857,7 +879,7 @@ def _gen_bar_graph( current_w - w, current_h - int(y[i] * self._p * self._deviation) - 25, ), - str(y[i]), + f"{y[i]:.2f}" if isinstance(y[i], float) else f"{y[i]}", ) if i != len(y): bar_color = random.choice(self.bar_color) @@ -915,7 +937,7 @@ def _init_graph( 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, 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) @@ -928,6 +950,7 @@ def _init_graph( color=(255, 255, 255, 0), font_size=35, font_color=self._color.get("title"), + font=self.font ) A.paste(title, (0, 25), True, "by_width") A.line( @@ -957,8 +980,9 @@ def _init_graph( y_index = tmp _interval = self._y_interval current_w = padding_w + _interval - _text_font = CreateImg(0, 0, font_size=self.font_size) + _text_font = CreateImg(0, 0, font_size=self.font_size, font=self.font) _grid = self.line_length if is_grid else 10 + x_rotate_height = 0 for _x in x_index: _p = CreateImg(1, _grid, color="#a9a9a9") A.paste(_p, (current_w, padding_h + line_length - _grid)) @@ -969,15 +993,18 @@ def _init_graph( plain_text=f"{_x}", font_size=self.font_size, color=(255, 255, 255, 0), + font=self.font ) + text.rotate(self.x_rotate, True) A.paste(text, (current_w - w, padding_h + line_length + 10), alpha=True) current_w += _interval + x_rotate_height = text.h _interval = self._x_interval if self.mat_type == "barh" else self._y_interval current_h = padding_h + line_length - _interval - _text_font = CreateImg(0, 0, font_size=self.font_size) + _text_font = CreateImg(0, 0, font_size=self.font_size, font=self.font) for _y in y_index: _p = CreateImg(_grid, 1, color="#a9a9a9") - A.paste(_p, (padding_w, current_h)) + A.paste(_p, (padding_w + 2, current_h)) w, h = _text_font.getsize(f"{_y}") h = int(h / 2) text = CreateImg( @@ -986,6 +1013,7 @@ def _init_graph( plain_text=f"{_y}", font_size=self.font_size, color=(255, 255, 255, 0), + font=self.font ) idx = 0 while text.size[0] > self.padding_w - 10 and idx < 3: @@ -993,8 +1021,9 @@ def _init_graph( 0, 0, plain_text=f"{_y}", - font_size=int(self.font_size * 0.9), + font_size=int(self.font_size * 0.75), color=(255, 255, 255, 0), + font=self.font ) w, _ = text.getsize(f"{_y}") idx += 1 @@ -1004,10 +1033,10 @@ def _init_graph( A.text((int(padding_w / 2), int(padding_w / 2)), x_name) if y_name: A.text( - (int(padding_w + line_length + 50), int(padding_h + line_length + 50)), + (int(padding_w + line_length + 50 - A.getsize(y_name)[0]), + int(padding_h + line_length + 50 + x_rotate_height)), y_name, ) - # A.show() return A diff --git a/utils/manager/__init__.py b/utils/manager/__init__.py index ebf4f88fe..ce83637bb 100644 --- a/utils/manager/__init__.py +++ b/utils/manager/__init__.py @@ -1,11 +1,17 @@ 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 @@ -19,6 +25,11 @@ # 撤回消息管理 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" @@ -34,6 +45,27 @@ 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 index c050ad164..c0a8380ef 100644 --- a/utils/manager/admin_manager.py +++ b/utils/manager/admin_manager.py @@ -1,8 +1,5 @@ -from typing import Optional, Dict from .data_class import StaticData -from utils.utils import FreqLimiter -from services.log import logger -from pathlib import Path +from typing import List, Optional class AdminManager(StaticData): @@ -13,15 +10,27 @@ class AdminManager(StaticData): def __init__(self): super().__init__(None) - def add_admin_command(self, plugin: str, level: int): + def add_admin_plugin_settings(self, plugin: str, cmd: List[str], level: int): """ 添加一个管理员命令 + :param plugin: 模块 + :param cmd: 别名 + :param level: 等级 + """ + self._data[plugin] = { + "level": level, + "cmd": cmd, + } + + def set_admin_level(self, plugin: str, level: int): + """ + 设置管理员命令等级 :param plugin: 模块名 :param level: 权限等级 """ - self._data[plugin] = level + self._data[plugin]["level"] = level - def remove_admin_command(self, plugin: str): + def remove_admin_plugin_settings(self, plugin: str): """ 删除一个管理员命令 :param plugin: 模块名 @@ -36,7 +45,7 @@ def check(self, plugin: str, level: int) -> bool: :param level: 权限等级 """ if plugin in self._data.keys(): - return level >= self._data[plugin] + return level >= self._data[plugin]["level"] return True def get_plugin_level(self, plugin: str) -> int: @@ -45,7 +54,15 @@ def get_plugin_level(self, plugin: str) -> int: :param plugin: 模块名 """ if plugin in self._data.keys(): - return self._data[plugin] + return self._data[plugin]["level"] return 0 - + def get_plugin_module(self, cmd: str) -> Optional[str]: + """ + 根据 cmd 获取功能 module + :param cmd: 命令 + """ + for key in self._data.keys(): + if self._data[key].get("cmd") and cmd in self._data[key]["cmd"]: + return key + return None diff --git a/utils/manager/configs_manager.py b/utils/manager/configs_manager.py new file mode 100644 index 000000000..9736f21cb --- /dev/null +++ b/utils/manager/configs_manager.py @@ -0,0 +1,65 @@ +# from typing import Optional, Any +# from .data_class import StaticData +# from pathlib import Path +# from ruamel.yaml import YAML +# +# yaml = YAML(typ="safe") +# +# +# class ConfigsManager(StaticData): +# """ +# 插件配置 与 资源 管理器 +# """ +# +# def __init__(self, file: Path): +# self.file = file +# super().__init__(file) +# self._resource_data = {} +# +# def add_plugin_config( +# self, +# module: str, +# key: str, +# value: str, +# help_: Optional[str] = None, +# default_value: Optional[str] = None, +# ): +# """ +# 为插件添加一个配置 +# :param module: 模块 +# :param key: 键 +# :param value: 值 +# :param help_: 配置注解 +# :param default_value: 默认值 +# """ +# if self._data.get(module) is None: +# self._data[module] = {} +# self._data[module][key] = { +# "value": value, +# "help": help_, +# "default_value": default_value, +# } +# +# def remove_plugin_config(self, module: str): +# """ +# 为插件删除一个配置 +# :param module: 模块名 +# """ +# if module in self._data.keys(): +# del self._data[module] +# +# def get_config(self, module: str, key: str) -> Optional[Any]: +# """ +# 获取指定配置值 +# :param module: 模块名 +# :param key: 配置名称 +# """ +# if module in self._data.keys(): +# if self._data[module].get(key): +# if self._data[module][key]["value"] is None: +# return self._data[module][key]["default_value"] +# return self._data[module][key]["value"] +# return None +# +# +# diff --git a/utils/manager/data_class.py b/utils/manager/data_class.py index 4c55a234b..6507be8f8 100644 --- a/utils/manager/data_class.py +++ b/utils/manager/data_class.py @@ -12,15 +12,25 @@ class StaticData: """ def __init__(self, file: Optional[Path]): - self._data = {} + self._data: dict = {} if file: file.parent.mkdir(exist_ok=True, parents=True) self.file = file if file.exists(): - self._data: dict = json.load(open(file, "r", encoding="utf8")) + 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) @@ -31,20 +41,24 @@ def keys(self): def delete(self, key): if self._data.get(key) is not None: del self._data[key] + self.save() - def get_data(self): + def get_data(self) -> dict: return self._data def save(self, path: Union[str, Path] = None): path = path if path else self.file - with open(path, "w", encoding="utf8") as f: - json.dump(self._data, f, ensure_ascii=False, indent=4) + 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'): + if self.file.name.endswith("json"): self._data: dict = json.load(open(self.file, "r", encoding="utf8")) - elif self.file.name.endswith('yaml'): + elif self.file.name.endswith("yaml"): self._data: dict = yaml.load(open(self.file, "r", encoding="utf8")) def is_exists(self): @@ -61,4 +75,3 @@ def __setitem__(self, key, value): def __getitem__(self, key): return self._data[key] - diff --git a/utils/manager/group_manager.py b/utils/manager/group_manager.py index f973b452a..a0db6af02 100644 --- a/utils/manager/group_manager.py +++ b/utils/manager/group_manager.py @@ -1,4 +1,3 @@ -from configs.config import DEFAULT_GROUP_LEVEL from typing import Optional, List, Union, Dict from pathlib import Path from .data_class import StaticData @@ -15,33 +14,30 @@ def __init__(self, file: Path): super().__init__(file) if not self._data: self._data = { - "super": {"close_plugins": {}, "white_group_list": []}, + "super": {"white_group_list": []}, "group_manager": {}, } self._task = {} - def block_plugin( - self, plugin_cmd: str, group_id: Optional[int] = None, block_type: str = "all" - ): + def block_plugin(self, module: str, group_id: int): """ 说明: 锁定插件 参数: - :param plugin_cmd: 功能模块名 + :param module: 功能模块名 :param group_id: 群组,None时为超级用户禁用 - :param block_type: 限制类型 """ - self._set_plugin_status(plugin_cmd, "block", group_id, block_type) + self._set_plugin_status(module, "block", group_id) - def unblock_plugin(self, plugin_cmd: str, group_id: Optional[int] = None): + def unblock_plugin(self, module: str, group_id: int): """ 说明: 解锁插件 参数: - :param plugin_cmd: 功能模块名 + :param module: 功能模块名 :param group_id: 群组 """ - self._set_plugin_status(plugin_cmd, "unblock", group_id) + self._set_plugin_status(module, "unblock", group_id) def set_group_level(self, group_id: int, level: int): """ @@ -57,49 +53,21 @@ def set_group_level(self, group_id: int, level: int): self._data["group_manager"][group_id]["level"] = level self.save() - def get_plugin_status( - self, plugin_cmd: str, group_id: Optional[int] = None, block_type: str = "all" - ) -> bool: + def get_plugin_status(self, module: str, group_id: int) -> bool: """ 说明: 获取插件状态 参数: - :param plugin_cmd: 功能模块名 + :param module: 功能模块名 :param group_id: 群组 - :param block_type: 限制类型 """ group_id = str(group_id) if group_id else group_id - if group_id: - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - return True - if plugin_cmd in self._data["group_manager"][group_id]["close_plugins"]: - return False - return True - else: - if plugin_cmd in self._data["super"]["close_plugins"]: - if ( - self._data["super"]["close_plugins"][plugin_cmd] == "all" - and block_type == "all" - ): - return False - else: - return ( - not self._data["super"]["close_plugins"][plugin_cmd] - == block_type - ) + if not self._data["group_manager"].get(group_id): + self._init_group(group_id) return True - - def get_plugin_block_type(self, plugin_cmd: str) -> str: - """ - 说明: - 获取功能限制类型 - 参数: - :param plugin_cmd: 模块名称 - """ - if plugin_cmd in self._data["super"]["close_plugins"]: - return self._data["super"]["close_plugins"][plugin_cmd] - return "" + if module in self._data["group_manager"][group_id]["close_plugins"]: + return False + return True def get_group_level(self, group_id: int) -> int: """ @@ -149,6 +117,17 @@ 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): """ 开启群被动技能 @@ -181,6 +160,9 @@ async def check_group_task_status(self, group_id: int, task: str) -> bool: 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: @@ -188,7 +170,7 @@ async def group_task_status(self, group_id: int) -> str: 查看群被全部动技能状态 :param group_id: 群号 """ - x = '[群被动技能]:\n' + 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) @@ -222,8 +204,8 @@ 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) - _module = _plugin.module try: + _module = _plugin.module plugin_task = _module.__getattribute__("__plugin_task__") for key in plugin_task.keys(): self._task[key] = plugin_task[key] @@ -240,18 +222,20 @@ async def init_group_task(self, group_id: Optional[Union[int, str]] = None): 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'] = {} + 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) + 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 self._data["group_manager"][group_id]["group_task_status"]: + 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 @@ -260,48 +244,27 @@ async def init_group_task(self, group_id: Optional[Union[int, str]] = None): def _set_plugin_status( self, - plugin_cmd: str, + module: str, status: str, - group_id: Optional[str], - block_type: str = "all", + group_id: int, ): """ 说明: 设置功能开关状态 参数: - :param plugin_cmd: 功能模块名 + :param module: 功能模块名 :param status: 功能状态 :param group_id: 群组 - :param block_type: 限制类型 """ group_id = str(group_id) if group_id else group_id - if plugin_cmd: - if group_id: - if not self._data["group_manager"].get(group_id): - self._init_group(group_id) - if status == "block": - if ( - plugin_cmd - not in self._data["group_manager"][group_id]["close_plugins"] - ): - self._data["group_manager"][group_id]["close_plugins"].append( - plugin_cmd - ) - else: - if plugin_cmd in self._data["group_manager"][group_id]["close_plugins"]: - self._data["group_manager"][group_id]["close_plugins"].remove( - plugin_cmd - ) - else: - if status == "block": - if ( - plugin_cmd not in self._data["super"]["close_plugins"] - or block_type != self._data["super"]["close_plugins"][plugin_cmd] - ): - self._data["super"]["close_plugins"][plugin_cmd] = block_type - else: - if plugin_cmd in self._data["super"]["close_plugins"]: - del self._data["super"]["close_plugins"][plugin_cmd] + 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): @@ -311,9 +274,22 @@ 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, + "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 new file mode 100644 index 000000000..88f5260e1 --- /dev/null +++ b/utils/manager/none_plugin_count_manager.py @@ -0,0 +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 + + + + + diff --git a/utils/manager/plugins2block_manager.py b/utils/manager/plugins2block_manager.py index f97cbf1a7..a30ff3021 100644 --- a/utils/manager/plugins2block_manager.py +++ b/utils/manager/plugins2block_manager.py @@ -12,10 +12,18 @@ class Plugins2blockManager(StaticData): """ 插件命令阻塞 管理器 """ + def __init__(self, file: Path): self.file = file super().__init__(None) self._block_limiter: Dict[str, UserBlockLimiter] = {} + if file.exists(): + with open(file, "r", encoding="utf8") as f: + self._data = yaml.load(f) + if "PluginBlockLimit" in self._data.keys(): + self._data = ( + self._data["PluginBlockLimit"] if self._data["PluginBlockLimit"] else {} + ) def add_block_limit( self, @@ -36,13 +44,13 @@ def add_block_limit( :param data_dict: 封装好的字典数据 """ if data_dict: - status = data_dict.get('status') - check_type = data_dict.get('check_type') - limit_type = data_dict.get('limit_type') - rst = data_dict.get('rst') + status = data_dict.get("status") + check_type = data_dict.get("check_type") + limit_type = data_dict.get("limit_type") + rst = data_dict.get("rst") status = status if status is not None else True - check_type = check_type if check_type else 'all' - limit_type = limit_type if limit_type else 'user' + check_type = check_type if check_type else "all" + limit_type = limit_type if limit_type else "user" if check_type not in ["all", "group", "private"]: raise ValueError( f"{plugin} 添加block限制错误,‘check_type‘ 必须为 'private'/'group'/'all'" @@ -56,14 +64,6 @@ def add_block_limit( "rst": rst, } - def remove_block_limit(self, plugin: str): - """ - 删除一个插件 block 限制 - :param plugin: 插件模块名称 - """ - if self._data.get(plugin): - del self._data[plugin] - def get_plugin_block_data(self, plugin: str) -> Optional[dict]: """ 获取插件block数据 @@ -78,10 +78,7 @@ def check_plugin_block_status(self, plugin: str) -> bool: 检测插件是否有 block :param plugin: 模块名 """ - return ( - plugin in self._data.keys() - and self._data[plugin]["status"] - ) + return plugin in self._data.keys() and self._data[plugin]["status"] def check(self, id_: int, plugin: str) -> bool: """ @@ -119,7 +116,7 @@ def reload_block_limit(self): for plugin in self._data: if self.check_plugin_block_status(plugin): self._block_limiter[plugin] = UserBlockLimiter() - logger.info(f'已成功加载 {len(self._block_limiter)} 个Block限制.') + logger.info(f"已成功加载 {len(self._block_limiter)} 个Block限制.") def reload(self): """ @@ -128,5 +125,5 @@ def reload(self): if self.file.exists(): with open(self.file, "r", encoding="utf8") as f: self._data: dict = yaml.load(f) - self._data = self._data['PluginBlockLimit'] + self._data = self._data["PluginBlockLimit"] self.reload_block_limit() diff --git a/utils/manager/plugins2cd_manager.py b/utils/manager/plugins2cd_manager.py index 8f5577570..2ea245c45 100644 --- a/utils/manager/plugins2cd_manager.py +++ b/utils/manager/plugins2cd_manager.py @@ -17,6 +17,13 @@ def __init__(self, file: Path): self.file = file super().__init__(None) self._freq_limiter: Dict[str, FreqLimiter] = {} + if file.exists(): + with open(file, "r", encoding="utf8") as f: + self._data = yaml.load(f) + if "PluginCdLimit" in self._data.keys(): + self._data = ( + self._data["PluginCdLimit"] if self._data["PluginCdLimit"] else {} + ) def add_cd_limit( self, @@ -40,15 +47,15 @@ def add_cd_limit( :param data_dict: 封装好的字典数据 """ if data_dict: - cd = data_dict.get('cd') - status = data_dict.get('status') - check_type = data_dict.get('check_type') - limit_type = data_dict.get('limit_type') - rst = data_dict.get('rst') + cd = data_dict.get("cd") + status = data_dict.get("status") + check_type = data_dict.get("check_type") + limit_type = data_dict.get("limit_type") + rst = data_dict.get("rst") cd = cd if cd is not None else 5 status = status if status is not None else True - check_type = check_type if check_type else 'all' - limit_type = limit_type if limit_type else 'user' + check_type = check_type if check_type else "all" + limit_type = limit_type if limit_type else "user" if check_type not in ["all", "group", "private"]: raise ValueError( f"{plugin} 添加cd限制错误,‘check_type‘ 必须为 'private'/'group'/'all'" @@ -63,14 +70,6 @@ def add_cd_limit( "rst": rst, } - def remove_cd_limit(self, plugin: str): - """ - 删除一个插件 cd 限制 - :param plugin: 插件模块名称 - """ - if self._data.get(plugin): - del self._data[plugin] - def get_plugin_cd_data(self, plugin: str) -> Optional[dict]: """ 获取插件cd数据 @@ -128,8 +127,10 @@ def reload_cd_limit(self): """ for plugin in self._data: if self.check_plugin_cd_status(plugin): - self._freq_limiter[plugin] = FreqLimiter(self.get_plugin_cd_data(plugin)['cd']) - logger.info(f'已成功加载 {len(self._freq_limiter)} 个Cd限制.') + self._freq_limiter[plugin] = FreqLimiter( + self.get_plugin_cd_data(plugin)["cd"] + ) + logger.info(f"已成功加载 {len(self._freq_limiter)} 个Cd限制.") def reload(self): """ @@ -138,7 +139,5 @@ def reload(self): if self.file.exists(): with open(self.file, "r", encoding="utf8") as f: self._data: dict = yaml.load(f) - self._data = self._data['PluginCdLimit'] + self._data = self._data["PluginCdLimit"] self.reload_cd_limit() - - diff --git a/utils/manager/plugins2count_manager.py b/utils/manager/plugins2count_manager.py new file mode 100644 index 000000000..724c39ace --- /dev/null +++ b/utils/manager/plugins2count_manager.py @@ -0,0 +1,134 @@ +from typing import Optional, Dict +from .data_class import StaticData +from utils.utils import DailyNumberLimiter +from services.log import logger +from pathlib import Path +from ruamel.yaml import YAML + +yaml = YAML(typ="safe") + + +class Plugins2countManager(StaticData): + """ + 插件命令 次数 管理器 + """ + + def __init__(self, file: Path): + self.file = file + super().__init__(None) + self._daily_limiter: Dict[str, DailyNumberLimiter] = {} + if file.exists(): + with open(file, "r", encoding="utf8") as f: + self._data = yaml.load(f) + if "PluginCountLimit" in self._data.keys(): + self._data = ( + self._data["PluginCountLimit"] if self._data["PluginCountLimit"] else {} + ) + + def add_count_limit( + self, + plugin: str, + *, + max_count: int = 5, + status: Optional[bool] = True, + limit_type: Optional[str] = "user", + rst: Optional[str] = None, + data_dict: Optional[dict] = None, + ): + """ + 添加插件调用 次数 限制 + :param plugin: 插件模块名称 + :param max_count: 最大次数限制 + :param status: 默认开关状态 + :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id + :param rst: 回复的话,为空则不回复 + :param data_dict: 封装好的字典数据 + """ + if data_dict: + max_count = data_dict.get("max_count") + status = data_dict.get("status") + limit_type = data_dict.get("limit_type") + rst = data_dict.get("rst") + status = status if status is not None else True + limit_type = limit_type if limit_type else "user" + max_count = max_count if max_count is not None else 5 + if limit_type not in ["user", "group"]: + raise ValueError(f"{plugin} 添加count限制错误,‘limit_type‘ 必须为 'user'/'group'") + self._data[plugin] = { + "max_count": max_count, + "status": status, + "limit_type": limit_type, + "rst": rst, + } + + def get_plugin_count_data(self, plugin: str) -> Optional[dict]: + """ + 获取插件次数数据 + :param plugin: 模块名 + """ + if self.check_plugin_count_status(plugin): + return self._data[plugin] + return None + + def get_plugin_data(self, plugin: str) -> Optional[dict]: + """ + 获取单个模块限制数据 + :param plugin: 模块名 + """ + if self._data.get(plugin) is not None: + return self._data.get(plugin) + return None + + def check_plugin_count_status(self, plugin: str) -> bool: + """ + 检测插件是否有 次数 限制 + :param plugin: 模块名 + """ + return ( + plugin in self._data.keys() + and self._data[plugin]["status"] + and self._data[plugin]["max_count"] > 0 + ) + + def check(self, plugin: str, id_: int) -> bool: + """ + 检查 count + :param plugin: 模块名 + :param id_: 限制 id + """ + if self._daily_limiter.get(plugin): + return self._daily_limiter[plugin].check(id_) + return True + + def increase(self, plugin: str, id_: int, num: int = 1): + """ + 增加次数 + :param plugin: 模块名 + :param id_: cd 限制类型 + :param num: 增加次数 + :return: + """ + if self._daily_limiter.get(plugin): + self._daily_limiter[plugin].increase(id_, num) + + def reload_count_limit(self): + """ + 加载 cd 限制器 + :return: + """ + for plugin in self._data: + if self.check_plugin_count_status(plugin): + self._daily_limiter[plugin] = DailyNumberLimiter( + self.get_plugin_count_data(plugin)["max_count"] + ) + logger.info(f"已成功加载 {len(self._daily_limiter)} 个Count限制.") + + def reload(self): + """ + 重载本地数据 + """ + if self.file.exists(): + with open(self.file, "r", encoding="utf8") as f: + self._data: dict = yaml.load(f) + self._data = self._data["PluginCountLimit"] + self.reload_count_limit() diff --git a/utils/manager/plugins2settings_manager.py b/utils/manager/plugins2settings_manager.py index d4183b40b..d5f49fe6d 100644 --- a/utils/manager/plugins2settings_manager.py +++ b/utils/manager/plugins2settings_manager.py @@ -55,17 +55,11 @@ def add_plugin_settings( if limit_superuser is not None else False, "cmd": cmd, - "plugin_type": list(plugin_type if plugin_type is not None else ("normal",)) + "plugin_type": list( + plugin_type if plugin_type is not None else ("normal",) + ), } - def remove_plugin_settings(self, plugin: str): - """ - 删除一个插件设置 - :param plugin: 插件模块名称 - """ - if self._data.get(plugin): - del self._data[plugin] - def get_plugin_data(self, module: str) -> dict: """ 通过模块名获取数据 @@ -99,4 +93,4 @@ def reload(self): if self.file.exists(): with open(self.file, "r", encoding="utf8") as f: self._data: dict = yaml.load(f) - self._data = self._data['PluginSettings'] + self._data = self._data["PluginSettings"] diff --git a/utils/manager/plugins_manager.py b/utils/manager/plugins_manager.py new file mode 100644 index 000000000..2b0cf8540 --- /dev/null +++ b/utils/manager/plugins_manager.py @@ -0,0 +1,154 @@ +from typing import Optional +from pathlib import Path +from .data_class import StaticData +from . import group_manager + + +class PluginsManager(StaticData): + """ + 插件 管理器 + """ + + def __init__(self, file: Path): + super().__init__(file) + if not self._data: + self._data = {} + + def add_plugin_data( + self, + module: str, + plugin_name: str, + *, + status: Optional[bool] = True, + error: Optional[bool] = False, + block_type: Optional[str] = None, + author: Optional[str] = None, + version: Optional[int] = None, + ): + """ + 添加插件数据 + :param module: 模块名称 + :param plugin_name: 插件名称 + :param status: 插件开关状态 + :param error: 加载状态 + :param block_type: 限制类型 + :param author: 作者 + :param version: 版本 + """ + self._data[module] = { + "plugin_name": plugin_name, + "status": status, + "error": error, + "block_type": block_type, + "author": author, + "version": version, + } + + def block_plugin( + self, module: str, group_id: Optional[int] = None, block_type: str = "all" + ): + """ + 说明: + 锁定插件 + 参数: + :param module: 功能模块名 + :param group_id: 群组,None时为超级用户禁用 + :param block_type: 限制类型 + """ + self._set_plugin_status(module, "block", group_id, block_type) + + def unblock_plugin(self, module: str, group_id: Optional[int] = None): + """ + 说明: + 解锁插件 + 参数: + :param module: 功能模块名 + :param group_id: 群组 + """ + self._set_plugin_status(module, "unblock", group_id) + + def get_plugin_status( + self, module: str, block_type: str = "all" + ) -> bool: + """ + 说明: + 获取插件状态 + 参数: + :param module: 功能模块名 + :param block_type: 限制类型 + """ + if module in self._data.keys(): + if self._data[module]["block_type"] == "all" and block_type == "all": + return False + else: + return not self._data[module]["block_type"] == block_type + return True + + def get_plugin_block_type(self, module: str) -> str: + """ + 说明: + 获取功能限制类型 + 参数: + :param module: 模块名称 + """ + if module in self._data.keys(): + return self._data[module]["block_type"] + return "" + + def get_plugin_error_status(self, module: str) -> bool: + """ + 插件是否成功加载 + :param module: 模块名称 + """ + if module not in self._data.keys(): + self.init_plugin(module) + return self._data[module]["error"] + + def _set_plugin_status( + self, + module: str, + status: str, + group_id: Optional[str], + block_type: str = "all", + ): + """ + 说明: + 设置功能开关状态 + 参数: + :param module: 功能模块名 + :param status: 功能状态 + :param group_id: 群组 + :param block_type: 限制类型 + """ + group_id = str(group_id) if group_id else group_id + if module: + if group_id: + if status == "block": + group_manager.block_plugin(f"{module}:super", int(group_id)) + else: + group_manager.unblock_plugin(f"{module}:super", int(group_id)) + else: + if module not in self._data.keys(): + self.init_plugin(module) + if status == "block": + self._data[module]["status"] = False + self._data[module]["block_type"] = block_type + else: + if module in self._data.keys(): + self._data[module]["status"] = True + self.save() + + def init_plugin(self, module: str): + """ + 初始化插件数据 + :param module: 模块名称 + """ + if module not in self._data.keys(): + self._data[module] = { + "plugin_name": module, + "status": True, + "error": False, + "block_type": None, + "author": None, + "version": None, + } diff --git a/utils/manager/requests_manager.py b/utils/manager/requests_manager.py new file mode 100644 index 000000000..842926dd2 --- /dev/null +++ b/utils/manager/requests_manager.py @@ -0,0 +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 diff --git a/utils/manager/resources_manager.py b/utils/manager/resources_manager.py new file mode 100644 index 000000000..c517003b1 --- /dev/null +++ b/utils/manager/resources_manager.py @@ -0,0 +1,109 @@ +from typing import Union, List, Optional +from .data_class import StaticData +from pathlib import Path +from ruamel.yaml import YAML +from services.log import logger +import shutil + +yaml = YAML(typ="safe") + + +class ResourcesManager(StaticData): + """ + 插件配置 与 资源 管理器 + """ + + def __init__(self, file: Path): + self.file = file + super().__init__(file) + self._temp_dir = [] + self._abspath = Path() + + def add_resource( + self, module: str, source_file: Union[str, Path], move_file: Union[str, Path] + ): + """ + 添加一个资源移动路劲 + :param module: 模块名 + :param source_file: 源文件路径 + :param move_file: 移动路径 + """ + if isinstance(source_file, Path): + source_file = str(source_file.absolute()) + if isinstance(move_file, Path): + move_file = move_file.absolute() + if module not in self._data.keys(): + self._data[module] = {source_file: move_file} + else: + self._data[module][source_file] = move_file + + def remove_resource(self, module: str, source_file: Optional[Union[str, Path]] = None): + """ + 删除一个资源路径 + :param module: 模块 + :param source_file: 源文件路径 + """ + if not source_file: + if module in self._data.keys(): + for x in self._data[module].keys(): + move_file = Path(self._data[module][x]) + if move_file.exists(): + shutil.rmtree(move_file.absolute(), ignore_errors=True) + logger.info(f"已清除插件 {module} 资源路径:{self._data[module][x]}") + del self._data[module][x] + else: + if isinstance(source_file, Path): + source_file = str(source_file.absolute()) + if source_file: + if module in self._data.keys() and source_file in self._data[module].keys(): + move_file = Path(self._data[module][source_file]) + if move_file.exists(): + shutil.rmtree(move_file.absolute(), ignore_errors=True) + del self._data[module][source_file] + self.save() + + def start_move(self): + """ + 开始移动路径 + """ + for module in self._data.keys(): + for source_path in self._data[module].keys(): + move_path = Path(self._data[module][source_path]) + source_path = Path(source_path) + file_name = source_path.name + move_path = move_path / file_name + move_path.mkdir(exist_ok=True, parents=True) + try: + if source_path.exists(): + if move_path.exists(): + shutil.rmtree(str(move_path.absolute()), ignore_errors=True) + shutil.move(str(source_path.absolute()), str(move_path.absolute())) + logger.info( + f"移动资源文件路径 {source_path.absolute()} >>> {move_path.absolute()}" + ) + elif not move_path.exists(): + logger.warning( + f"移动资源路径文件{source_path.absolute()} >>>" + f" {move_path.absolute()} 失败,源文件不存在.." + ) + except Exception as e: + logger.error( + f"移动资源路径文件{source_path.absolute()} >>>" + f" {move_path.absolute()}失败,{type(e)}:{e}" + ) + self.save() + + def add_temp_dir(self, path: Union[str, Path]): + """ + 添加临时清理文件夹 + :param path: 路径 + """ + if isinstance(path, str): + path = Path(path) + self._temp_dir.append(path) + + def get_temp_data_dir(self) -> List[Path]: + """ + 获取临时文件文件夹 + """ + return self._temp_dir diff --git a/utils/manager/withdraw_message_manager.py b/utils/manager/withdraw_message_manager.py index fa7482cc3..57eb34456 100644 --- a/utils/manager/withdraw_message_manager.py +++ b/utils/manager/withdraw_message_manager.py @@ -1,8 +1,8 @@ from typing import Tuple, Union, Dict +from nonebot.adapters.cqhttp import MessageEvent, PrivateMessageEvent, GroupMessageEvent class WithdrawMessageManager: - def __init__(self): self.data = [] @@ -14,7 +14,7 @@ def append(self, message_data: Tuple[Union[int, Dict[str, int]], int]): :param message_data: 撤回消息id和时间 """ if isinstance(message_data[0], dict): - message_data = (message_data[0]['message_id'], message_data[1]) + message_data = (message_data[0]["message_id"], message_data[1]) self.data.append(message_data) def remove(self, message_data: Tuple[int, int]): @@ -26,3 +26,19 @@ def remove(self, message_data: Tuple[int, int]): """ self.data.remove(message_data) + def withdraw_message( + self, event: MessageEvent, id_: Union[int, Dict[str, int]], conditions: Tuple[int, int] + ): + """ + 便捷判断消息撤回 + :param event: event + :param id_: 消息id 或 send 返回的字典 + :param conditions: 判断条件 + """ + if conditions[0]: + if ( + (conditions[1] == 0 and isinstance(event, PrivateMessageEvent)) + or (conditions[1] == 1 and isinstance(event, GroupMessageEvent)) + or conditions[1] == 2 + ): + self.append((id_, conditions[0])) diff --git a/utils/message_builder.py b/utils/message_builder.py index c66230bf3..cb7779a06 100644 --- a/utils/message_builder.py +++ b/utils/message_builder.py @@ -1,13 +1,17 @@ from configs.path_config import IMAGE_PATH, VOICE_PATH from nonebot.adapters.cqhttp.message import MessageSegment +from configs.config import NICKNAME from services.log import logger -from typing import Union +from typing import Union, List from pathlib import Path import os def image( - img_name: Union[str, Path] = None, path: str = None, abspath: str = None, b64: str = None + img_name: Union[str, Path] = None, + path: str = None, + abspath: str = None, + b64: str = None, ) -> Union[MessageSegment, str]: """ 说明: @@ -153,3 +157,27 @@ def poke(qq: int) -> MessageSegment: :param qq: qq号 """ return MessageSegment("poke", {"qq": qq}) + + +def custom_forward_msg( + msg_list: List[str], uin: Union[int, str], name: str = f"这里是{NICKNAME}" +) -> List[dict]: + """ + 生成自定义合并消息 + :param msg_list: 消息列表 + :param uin: 发送者 QQ + :param name: 自定义名称 + """ + uin = int(uin) + mes_list = [] + for _message in msg_list: + data = { + "type": "node", + "data": { + "name": name, + "uin": f"{uin}", + "content": _message, + }, + } + mes_list.append(data) + return mes_list diff --git a/utils/utils.py b/utils/utils.py index 6d6d32cea..c5c469ec3 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -1,15 +1,14 @@ -from datetime import datetime, timedelta +from datetime import datetime from collections import defaultdict from nonebot import require -from configs.path_config import TEXT_PATH from configs.config import SYSTEM_PROXY -from typing import List, Union, Optional, Type +from typing import List, Union, Optional, Type, Any from nonebot.adapters.cqhttp import Bot from nonebot.matcher import matchers, Matcher +import httpx import nonebot import pytz import pypinyin -import aiohttp import time try: @@ -30,10 +29,10 @@ def __init__(self, max_count: int): self.count = defaultdict(int) self.max_count = max_count - def add(self, key: Union[str, int, float]): + def add(self, key: Any): self.count[key] += 1 - def check(self, key: Union[str, int, float]) -> bool: + def check(self, key: Any) -> bool: if self.count[key] >= self.max_count: self.count[key] = 0 return True @@ -49,14 +48,14 @@ def __init__(self): self.flag_data = defaultdict(bool) self.time = time.time() - def set_true(self, key: Union[str, int, float]): + def set_true(self, key: Any): self.time = time.time() self.flag_data[key] = True - def set_false(self, key: Union[str, int, float]): + def set_false(self, key: Any): self.flag_data[key] = False - def check(self, key: Union[str, int, float]) -> bool: + def check(self, key: Any) -> bool: if time.time() - self.time > 30: self.set_false(key) return False @@ -72,15 +71,15 @@ def __init__(self, default_cd_seconds: int): self.next_time = defaultdict(float) self.default_cd = default_cd_seconds - def check(self, key: Union[str, int, float]) -> bool: + def check(self, key: Any) -> bool: return time.time() >= self.next_time[key] - def start_cd(self, key: Union[str, int, float], cd_time: int = 0): + def start_cd(self, key: Any, cd_time: int = 0): self.next_time[key] = time.time() + ( cd_time if cd_time > 0 else self.default_cd ) - def left_time(self, key: Union[str, int, float]) -> float: + def left_time(self, key: Any) -> float: return self.next_time[key] - time.time() @@ -131,8 +130,7 @@ def __init__(self, max_num): self.max = max_num def check(self, key) -> bool: - now = datetime.now(self.tz) - day = (now - timedelta(hours=5)).day + day = datetime.now(self.tz).day if day != self.today: self.today = day self.count.clear() @@ -282,18 +280,6 @@ def get_message_json(data: str) -> List[dict]: return [] -# 获取文本加密后的cookie -def get_cookie_text(cookie_name: str) -> str: - """ - 说明: - 获取 txt/cookie 目录下指定 cookie 的内容 - 参数: - :param cookie_name: cookie文件名称 - """ - with open(TEXT_PATH + "cookie/" + cookie_name + ".txt", "r") as f: - return f.read() - - def get_local_proxy(): """ 说明: @@ -302,7 +288,7 @@ def get_local_proxy(): return SYSTEM_PROXY if SYSTEM_PROXY else None -def is_Chinese(word: str) -> bool: +def is_chinese(word: str) -> bool: """ 说明: 判断字符串是否为纯中文 @@ -315,7 +301,7 @@ def is_Chinese(word: str) -> bool: return True -async def user_avatar(qq: int) -> bytes: +async def get_user_avatar(qq: int) -> bytes: """ 说明: 快捷获取用户头像 @@ -323,12 +309,15 @@ async def user_avatar(qq: int) -> bytes: :param qq: qq号 """ url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=160" - async with aiohttp.ClientSession() as session: - async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: - return await response.read() + async with httpx.AsyncClient() as client: + for _ in range(3): + try: + return (await client.get(url)).content + except TimeoutError: + pass -async def group_avatar(group_id: int) -> bytes: +async def get_group_avatar(group_id: int) -> bytes: """ 说明: 快捷获取用群头像 @@ -336,9 +325,12 @@ async def group_avatar(group_id: int) -> bytes: :param group_id: 群号 """ url = f"http://p.qlogo.cn/gh/{group_id}/{group_id}/640/" - async with aiohttp.ClientSession() as session: - async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: - return await response.read() + async with httpx.AsyncClient() as client: + for _ in range(3): + try: + return (await client.get(url)).content + except TimeoutError: + pass def cn2py(word: str) -> str: @@ -368,3 +360,4 @@ def change_picture_links(url: str, mode: str): img_type = img_sp[1] url = url.replace("original", "master") + f"_master1200.{img_type}" return url +