From 1996f0d96a4a3402dd8fd79c5571e615cf481f92 Mon Sep 17 00:00:00 2001 From: hibiki <775757368@qq.com> Date: Tue, 17 Aug 2021 23:17:08 +0800 Subject: [PATCH] update code --- .env.dev | 2 +- configs/config.py | 15 +- configs/path_config.py | 38 +-- plugins/aconfig/__init__.py | 20 +- plugins/admin_bot_manage/data_source.py | 251 +++++++++++------- .../group_notification_state.py | 2 +- plugins/admin_bot_manage/switch_rule.py | 62 ++++- plugins/admin_help/__init__.py | 13 +- plugins/ai/data_source.py | 6 +- plugins/alapi/comments_163.py | 6 +- plugins/alapi/cover.py | 6 +- plugins/alapi/data_source.py | 39 +++ plugins/alapi/poetry.py | 6 +- plugins/alapi/util.py | 17 -- plugins/alapi/wbtop.py | 62 +++++ plugins/auto_invite/__init__.py | 6 +- plugins/ban/__init__.py | 64 ++++- plugins/check_zhenxun_update/__init__.py | 7 +- plugins/check_zhenxun_update/data_source.py | 120 +++------ plugins/delete_img/__init__.py | 25 +- plugins/epic/__init__.py | 4 +- plugins/fudu.py | 34 ++- .../genshin/query_resource_points/__init__.py | 3 +- .../query_resource_points/query_resource.py | 2 +- plugins/group_handle/__init__.py | 2 +- plugins/group_level/__init__.py | 116 -------- plugins/group_manager/__init__.py | 102 +++++++ plugins/help/__init__.py | 3 - plugins/help/config.py | 11 +- plugins/help/data_source.py | 37 ++- plugins/hook.py | 185 ++++++++++--- plugins/jitang.py | 3 +- plugins/move_img/__init__.py | 18 +- plugins/mute.py | 4 +- plugins/nickname.py | 79 +++--- plugins/nonebot_plugin_manager/__init__.py | 122 --------- plugins/nonebot_plugin_manager/data.py | 121 --------- plugins/nonebot_plugin_manager/handle.py | 138 ---------- plugins/nonebot_plugin_manager/parser.py | 60 ----- plugins/one_friend/__init__.py | 12 +- plugins/open_cases/__init__.py | 3 +- plugins/parse_bilibili_json.py | 137 +++++++--- plugins/pixiv/__init__.py | 3 +- plugins/reimu/__init__.py | 6 +- plugins/remind/__init__.py | 3 +- plugins/roll.py | 3 +- plugins/russian/__init__.py | 6 +- plugins/search_anime/data_source.py | 1 + plugins/search_buff_skin_price/__init__.py | 3 +- plugins/send_img/__init__.py | 1 - plugins/send_setu/__init__.py | 15 +- plugins/send_setu/data_source.py | 16 +- plugins/shop/gold_redbag/__init__.py | 7 +- plugins/sign_in/group_user_checkin.py | 6 +- plugins/statistics_hook.py | 2 +- plugins/super_help/__init__.py | 48 ++-- plugins/update_info.py | 2 +- plugins/update_pic.py | 25 +- plugins/update_setu/data_source.py | 20 +- plugins/upload_img/__init__.py | 126 ++++----- plugins/upload_img/data_source.py | 51 ++++ plugins/weather/__init__.py | 7 +- plugins/weather/data_source.py | 5 +- plugins/yiqing/data_source.py | 3 +- resources/img/other/webtop.png | Bin 0 -> 170709 bytes update_info.json | 40 ++- {plugins/alapi => utils}/__init__.py | 0 utils/browser.py | 16 +- utils/image_utils.py | 11 +- utils/static_data/__init__.py | 11 + utils/static_data/data_class.py | 51 ++++ utils/static_data/data_source.py | 27 ++ utils/static_data/group_manager.py | 186 +++++++++++++ 73 files changed, 1501 insertions(+), 1163 deletions(-) create mode 100644 plugins/alapi/data_source.py delete mode 100644 plugins/alapi/util.py create mode 100644 plugins/alapi/wbtop.py delete mode 100644 plugins/group_level/__init__.py create mode 100644 plugins/group_manager/__init__.py delete mode 100644 plugins/nonebot_plugin_manager/__init__.py delete mode 100644 plugins/nonebot_plugin_manager/data.py delete mode 100644 plugins/nonebot_plugin_manager/handle.py delete mode 100644 plugins/nonebot_plugin_manager/parser.py create mode 100644 plugins/upload_img/data_source.py create mode 100644 resources/img/other/webtop.png rename {plugins/alapi => utils}/__init__.py (100%) create mode 100644 utils/static_data/__init__.py create mode 100644 utils/static_data/data_class.py create mode 100644 utils/static_data/data_source.py create mode 100644 utils/static_data/group_manager.py diff --git a/.env.dev b/.env.dev index 4a2eacf2..00d51e29 100644 --- a/.env.dev +++ b/.env.dev @@ -10,7 +10,7 @@ NICKNAME=["真寻", "小真寻", "绪山真寻", "小寻子"] SESSION_EXPIRE_TIMEOUT=30 -DEBUG=true +DEBUG=False # 服务器和端口 HOST = 127.0.0.1 PORT = 8080 diff --git a/configs/config.py b/configs/config.py index 9d982528..19ff4aeb 100644 --- a/configs/config.py +++ b/configs/config.py @@ -1,4 +1,3 @@ -from .utils.util import get_config_data from typing import List, Optional, Tuple from services.service_config import TL_M_KEY, SYSTEM_M_PROXY, ALAPI_M_TOKEN @@ -11,6 +10,8 @@ except ModuleNotFoundError: # 是否使用配置文件 USE_CONFIG_FILE: bool = False +# 回复消息名称 +NICKNAME: str = '小真寻' # API KEY(必要) RSSHUBAPP: str = "https://rsshub.app" # rsshub @@ -35,7 +36,7 @@ SYSTEM_PROXY: Optional[str] = None # 全局代理 BUFF_PROXY: Optional[str] = None # Buff代理 # 公开图库列表 -IMAGE_DIR_LIST: List[str] = ["色图", "美图", "萝莉", "壁纸"] +IMAGE_DIR_LIST: List[str] = ["美图", "萝莉", "壁纸"] # 对被ban用户发送的消息 BAN_RESULT: str = "才不会给你发消息." @@ -77,6 +78,8 @@ 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 = 8 # 恶意命令触发检测最大触发次数 @@ -190,6 +193,14 @@ plugins2info_dict = { "level": 5, "cmd": ["pix", "PIX", "pIX", "Pix", "PIx"], }, + "wbtop": { + "level": 5, + "cmd": ['微博热搜', "微博", "wbtop"] + }, + "update_info": { + "level": 5, + "cmd": ['更新信息', '更新日志'] + } } if TL_M_KEY: diff --git a/configs/path_config.py b/configs/path_config.py index b4472c01..32c6c474 100644 --- a/configs/path_config.py +++ b/configs/path_config.py @@ -1,6 +1,6 @@ from .utils.util import get_config_data from pathlib import Path -from configs.config import USE_CONFIG_FILE +# from configs.config import USE_CONFIG_FILE # 图片路径 IMAGE_PATH = Path("resources/img/") @@ -20,24 +20,24 @@ TEMP_PATH = Path("resources/img/temp/") def init_path(): global IMAGE_PATH, VOICE_PATH, TXT_PATH, LOG_PATH, TTF_PATH, DATA_PATH, TEMP_PATH - if USE_CONFIG_FILE: - data = get_config_data() - if data.get('IMAGE_PATH'): - IMAGE_PATH = Path(data['IMAGE_PATH']) - if data.get('VOICE_PATH'): - VOICE_PATH = Path(data['VOICE_PATH']) - if data.get('TXT_PATH'): - TXT_PATH = Path(data['TXT_PATH']) - if data.get('LOG_PATH'): - LOG_PATH = Path(data['LOG_PATH']) - if data.get('TTF_PATH'): - TTF_PATH = Path(data['TTF_PATH']) - if data.get('DATA_PATH'): - DATA_PATH = Path(data['DATA_PATH']) - if data.get('DRAW_PATH'): - DRAW_PATH = Path(data['DRAW_PATH']) - if data.get('TEMP_PATH'): - TEMP_PATH = Path(data['TEMP_PATH']) + # if USE_CONFIG_FILE: + # data = get_config_data() + # if data.get('IMAGE_PATH'): + # IMAGE_PATH = Path(data['IMAGE_PATH']) + # if data.get('VOICE_PATH'): + # VOICE_PATH = Path(data['VOICE_PATH']) + # if data.get('TXT_PATH'): + # TXT_PATH = Path(data['TXT_PATH']) + # if data.get('LOG_PATH'): + # LOG_PATH = Path(data['LOG_PATH']) + # if data.get('TTF_PATH'): + # TTF_PATH = Path(data['TTF_PATH']) + # if data.get('DATA_PATH'): + # DATA_PATH = Path(data['DATA_PATH']) + # if data.get('DRAW_PATH'): + # DRAW_PATH = Path(data['DRAW_PATH']) + # if data.get('TEMP_PATH'): + # TEMP_PATH = Path(data['TEMP_PATH']) IMAGE_PATH.mkdir(parents=True, exist_ok=True) VOICE_PATH.mkdir(parents=True, exist_ok=True) TXT_PATH.mkdir(parents=True, exist_ok=True) diff --git a/plugins/aconfig/__init__.py b/plugins/aconfig/__init__.py index 2ead8e17..df5663f0 100644 --- a/plugins/aconfig/__init__.py +++ b/plugins/aconfig/__init__.py @@ -9,6 +9,7 @@ from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent from nonebot.adapters.cqhttp.permission import GROUP from utils.utils import FreqLimiter +from configs.config import NICKNAME __plugin_name__ = "基本设置 [Hidden]" @@ -38,15 +39,16 @@ self_introduction = on_command( @self_introduction.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - result = ( - "我叫绪山真寻\n" - "你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n" - "年龄的话我还是个**岁初中生(至少现在是)\n" - "身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n" - "我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n" - "最好的朋友是椛!\n" + image("zhenxun") - ) - await self_introduction.finish(result) + if NICKNAME.find('真寻') != -1: + result = ( + "我叫绪山真寻\n" + "你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n" + "年龄的话我还是个**岁初中生(至少现在是)\n" + "身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n" + "我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n" + "最好的朋友是椛!\n" + image("zhenxun") + ) + await self_introduction.finish(result) my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True) diff --git a/plugins/admin_bot_manage/data_source.py b/plugins/admin_bot_manage/data_source.py index 92d54136..5c7d60b2 100644 --- a/plugins/admin_bot_manage/data_source.py +++ b/plugins/admin_bot_manage/data_source.py @@ -7,7 +7,6 @@ import aiohttp from utils.message_builder import image from utils.utils import get_local_proxy, get_bot from pathlib import Path -from nonebot import require from configs.config import plugins2info_dict from models.group_member_info import GroupInfoUser import time @@ -15,145 +14,181 @@ 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.static_data import group_manager +from utils.image_utils import CreateImg +import asyncio + try: import ujson as json except ModuleNotFoundError: import json -export = require("nonebot_plugin_manager") - - command_dict = { - '早晚安': 'zwa', - '进群欢迎': 'hy', - '每日开箱重置提醒': 'kxcz', - 'b站转发解析': 'blpar', - 'epic': 'epic', - '丢人爬': 'pa', - '原神黄历提醒': 'almanac', + "早晚安": "zwa", + "进群欢迎": "hy", + "每日开箱重置提醒": "kxcz", + "b站转发解析": "blpar", + "epic": "epic", + "丢人爬": "pa", + "原神黄历提醒": "almanac", } command_list = list(command_dict.values()) command_info_dt = { - '早晚安': '将会在每晚11:59晚安,在6:01早安哦', - '每日开箱重置提醒': '将会在每日00:01提示开箱重置!', - 'epic': '将会在每日中午12:01发送可白嫖的epic游戏', - '原神黄历提醒': '将会在每日8:00发送当日的原神黄历', + "早晚安": "将会在每晚11:59晚安,在6:01早安哦", + "每日开箱重置提醒": "将会在每日00:01提示开箱重置!", + "epic": "将会在每日中午12:01发送可白嫖的epic游戏", + "原神黄历提醒": "将会在每日8:00发送当日的原神黄历", } async def remind_status(group: int, name: str, flag: bool) -> str: - _name = '' + _name = "" if name in command_dict.values(): _name = list(command_dict.keys())[list(command_dict.values()).index(name)] if flag: - rst = '开启' + rst = "开启" if await GroupRemind.get_status(group, name): - return f'该群已经{rst}过 {_name},请勿重复开启!' + return f"该群已经{rst}过 {_name},请勿重复开启!" else: - rst = '关闭' + rst = "关闭" if not await GroupRemind.get_status(group, name): - return f'该群已经{rst}过 {_name},请勿重复开启!' + return f"该群已经{rst}过 {_name},请勿重复开启!" if await GroupRemind.set_status(group, name, flag): - info = command_info_dt[_name] if command_info_dt.get(_name) else '' + info = command_info_dt[_name] if command_info_dt.get(_name) else "" if info: - info = '\n' + info - return f'成功{rst} {_name}!0v0 {info}' + info = "\n" + info + return f"成功{rst} {_name}!0v0 {info}" else: - return f'{rst} {_name} 失败了...' + return f"{rst} {_name} 失败了..." async def set_group_status(name: str, group_id: int): flag = None - if name[:2] == '开启': + if name[:2] == "开启": flag = True - elif name[:2] == '关闭': + elif name[:2] == "关闭": flag = False cmd = name[2:] - if cmd in ['全部通知', '所有通知']: + if cmd in ["全部通知", "所有通知"]: for command in command_list: await remind_status(group_id, command, flag) - return f'已{name[:2]}所有通知!' + return f"已{name[:2]}所有通知!" return await remind_status(group_id, command_dict[cmd], flag) async def group_current_status(group_id: int): - result = f'(被动技能)\n早晚安通知:{"√" if await GroupRemind.get_status(group_id, "zwa") else "×"}\n' \ - f'进群欢迎:{"√" if await GroupRemind.get_status(group_id, "hy") else "×"}\n' \ - f'每日开箱重置通知:{"√" if await GroupRemind.get_status(group_id, "kxcz") else "×"}\n' \ - f'b站转发解析:{"√" if await GroupRemind.get_status(group_id, "blpar") else "×"}\n' \ - f'丢人爬:{"√" if await GroupRemind.get_status(group_id, "pa") else "×"}\n' \ - f'epic免费游戏:{"√" if await GroupRemind.get_status(group_id, "epic") else "×"}\n' \ - f'原神黄历提醒:{"√" if await GroupRemind.get_status(group_id, "almanac") else "×"}' + result = ( + f'(被动技能)\n早晚安通知:{"√" if await GroupRemind.get_status(group_id, "zwa") else "×"}\n' + f'进群欢迎:{"√" if await GroupRemind.get_status(group_id, "hy") else "×"}\n' + f'每日开箱重置通知:{"√" if await GroupRemind.get_status(group_id, "kxcz") else "×"}\n' + f'b站转发解析:{"√" if await GroupRemind.get_status(group_id, "blpar") else "×"}\n' + f'丢人爬:{"√" if await GroupRemind.get_status(group_id, "pa") else "×"}\n' + f'epic免费游戏:{"√" if await GroupRemind.get_status(group_id, "epic") else "×"}\n' + f'原神黄历提醒:{"√" if await GroupRemind.get_status(group_id, "almanac") else "×"}' + ) return result -custom_welcome_msg_json = Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" +custom_welcome_msg_json = ( + Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" +) async def custom_group_welcome(msg, imgs, user_id, 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') + 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") # print(custom_welcome_msg_json) 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')) + 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}') + 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: + 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} 更换群欢迎消息图片') + 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 + logger.error(f"GROUP {group_id} 替换群消息失败 e:{e}") + return "替换群消息失败.." + return f"替换群欢迎消息成功:\n{result}" + img_result -async def change_group_switch(cmd: str, group_id: int): +def change_group_switch(cmd: str, group_id: int, is_super: bool = False): + group_help_file = Path(DATA_PATH) / "group_help" / f"{group_id}.png" group_id = str(group_id) status = cmd[:2] cmd = cmd[2:] - try: - with open(DATA_PATH + 'manager/plugin_list.json', 'r', encoding='utf8') as f: - plugin_list = json.load(f) - except ValueError: - pass - except FileNotFoundError: - pass for plugin_cmd in plugins2info_dict.keys(): - if cmd in plugins2info_dict[plugin_cmd]['cmd']: - # print(plugin_list[plugin_cmd]) - if status == '开启': - # if group_id in plugin_list[plugin_cmd]: - if group_id not in plugin_list[plugin_cmd] or plugin_list[plugin_cmd][group_id]: - return f'功能 {cmd} 正处于开启状态!不要重复开启.' - export.unblock_plugin(group_id, plugin_cmd) + if cmd in plugins2info_dict[plugin_cmd]["cmd"]: + if is_super: + plugin_cmd = f'{plugin_cmd}:super' + if status == "开启": + if group_manager.get_plugin_status(plugin_cmd, group_id): + return f"功能 {cmd} 正处于开启状态!不要重复开启." + group_manager.unblock_plugin(plugin_cmd, group_id) else: - if group_id in plugin_list[plugin_cmd]: - if not plugin_list[plugin_cmd][group_id]: - return f'功能 {cmd} 正处于关闭状态!不要重复关闭.' - export.block_plugin(group_id, plugin_cmd) - if os.path.exists(DATA_PATH + f'group_help/{group_id}.png'): - os.remove(DATA_PATH + f'group_help/{group_id}.png') + if not group_manager.get_plugin_status(plugin_cmd, group_id): + return f"功能 {cmd} 正处于关闭状态!不要重复关闭." + group_manager.block_plugin(plugin_cmd, group_id) + if group_help_file.exists(): + group_help_file.unlink() + return f"{status} {cmd} 功能!" + return f"没有找到 {cmd} 功能..." - return f'{status} {cmd} 功能!' - return f'没有找到 {cmd} 功能喔' + +def set_plugin_status(cmd: str, block_type: str = "all"): + status = cmd[:2] + cmd = cmd[2:] + for plugin_cmd in plugins2info_dict.keys(): + if cmd in plugins2info_dict[plugin_cmd]["cmd"]: + if status == "开启": + group_manager.unblock_plugin(plugin_cmd) + else: + group_manager.block_plugin(plugin_cmd, block_type=block_type) + break + + +async def get_plugin_status(): + return await asyncio.get_event_loop().run_in_executor(None, _get_plugin_status) + + +def _get_plugin_status(): + rst = '\t功能\n' + flag_str = '状态'.rjust(4) + '\n' + for plugin_cmd in plugins2info_dict: + flag = group_manager.get_plugin_block_type(plugin_cmd) + flag = flag.upper() + ' CLOSE' if flag else 'OPEN' + rst += f'{plugins2info_dict[plugin_cmd]["cmd"][0]}\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) -> bool: @@ -163,35 +198,61 @@ async def update_member_info(group_id: int) -> bool: _exist_member_list = [] # try: for user_info in _group_user_list: - if user_info['card'] == "": - nickname = user_info['nickname'] + if user_info["card"] == "": + nickname = user_info["nickname"] else: - nickname = user_info['card'] + 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_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'])) + 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") + 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'])) + 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)))) + _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): @@ -202,9 +263,7 @@ async def update_member_info(group_id: int) -> bool: 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]) + 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/group_notification_state.py b/plugins/admin_bot_manage/group_notification_state.py index c406b5ea..c6df4b61 100644 --- a/plugins/admin_bot_manage/group_notification_state.py +++ b/plugins/admin_bot_manage/group_notification_state.py @@ -39,7 +39,7 @@ group_status = on_command( "群通知状态", }, permission=GROUP, - priority=1, + priority=5, block=True, ) diff --git a/plugins/admin_bot_manage/switch_rule.py b/plugins/admin_bot_manage/switch_rule.py index 56021317..710b4675 100644 --- a/plugins/admin_bot_manage/switch_rule.py +++ b/plugins/admin_bot_manage/switch_rule.py @@ -1,10 +1,11 @@ from nonebot import on_command from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from .data_source import change_group_switch -from nonebot.adapters.cqhttp.permission import GROUP +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent +from .data_source import change_group_switch, set_plugin_status, get_plugin_status from services.log import logger -from configs.config import plugins2info_dict +from configs.config import plugins2info_dict, NICKNAME +from utils.utils import get_message_text, is_number +from nonebot.permission import SUPERUSER __plugin_name__ = "群功能开关" @@ -25,17 +26,52 @@ cmds = set(cmds) switch_rule = on_command( - "switch_rule", aliases=cmds, permission=GROUP, priority=4, block=True + "switch_rule", aliases=cmds, priority=5, block=True ) +plugins_status = on_command('功能状态', permission=SUPERUSER, priority=5, block=True) + @switch_rule.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await switch_rule.send( - await change_group_switch( - state["_prefix"]["raw_command"].strip(), event.group_id +async def _(bot: Bot, event: MessageEvent, state: T_State): + if isinstance(event, GroupMessageEvent): + await switch_rule.send( + change_group_switch( + state["_prefix"]["raw_command"].strip(), event.group_id + ) ) - ) - logger.info( - f'USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {state["_prefix"]["raw_command"]}' - ) + logger.info( + f'USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {state["_prefix"]["raw_command"]}' + ) + else: + if str(event.user_id) in bot.config.superusers: + block_type = get_message_text(event.json()) + _cmd = state["_prefix"]["raw_command"].strip() + if is_number(block_type): + if not int(block_type) in [g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id))]: + await switch_rule.finish(f'{NICKNAME}未加入群聊:{block_type}') + change_group_switch(_cmd, int(block_type), True) + group_name = (await bot.get_group_info(group_id=int(block_type)))['group_name'] + await switch_rule.send(f'已禁用群聊 {group_name}({block_type}) 的 {_cmd[2:]} 功能') + else: + if block_type not in ['all', 'private', 'group', 'a', 'p', 'g']: + block_type = 'all' + block_type = 'all' if block_type == 'a' else block_type + block_type = 'private' if block_type == 'p' else block_type + block_type = 'group' if block_type == 'g' else block_type + set_plugin_status(_cmd, block_type) + if block_type == 'all': + await switch_rule.send(f'已{_cmd[:2]}功能:{_cmd[2:]}') + elif block_type == 'private': + await switch_rule.send(f'已在私聊中{_cmd[:2]}功能:{_cmd[2:]}') + else: + await switch_rule.send(f'已在群聊中{_cmd[:2]}功能:{_cmd[2:]}') + logger.info( + f'USER {event.user_id} 使用功能管理命令 {state["_prefix"]["raw_command"]} | {block_type}' + ) + + +@plugins_status.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await plugins_status.send(await get_plugin_status()) + diff --git a/plugins/admin_help/__init__.py b/plugins/admin_help/__init__.py index 8fe969a9..628fb1b1 100644 --- a/plugins/admin_help/__init__.py +++ b/plugins/admin_help/__init__.py @@ -5,25 +5,22 @@ from nonebot.adapters.cqhttp import GroupMessageEvent from utils.image_utils import CreateImg from configs.path_config import IMAGE_PATH from utils.message_builder import image +from configs.config import NICKNAME __plugin_name__ = '管理帮助 [Hidden]' -__plugin_usage__ = '''管理帮助(权限等级): +__plugin_usage__ = f'''管理帮助(权限等级): 1.更新群组成员列表(1) 2.功能开关 --> 指令:开启/关闭xx功能(2) 3.查看群被动技能 --> 指令:群通知状态(2) 4.自定义群欢迎 --> 指令:自定义进群欢迎消息(2) - 5.将用户拉入真寻黑名单 --> .ban/.unban(5) + 5.将用户拉入{NICKNAME}黑名单 --> .ban/.unban(5) 6.刷屏禁言相关 --> 指令:刷屏检测设置/设置检测时间 \t\t/设置检测次数/设置禁言时长(5) - 7.群员活跃度相关 --> 指令:群员活跃检测设置 - 设置群员活跃检测时长(天) - 添加群员活跃检测白名单[at]... - 查看群员活跃检测白名单(5) - 8.上传图片(6) + 8.上传图片/连续上传图片(6) 9.移动图片(7) 10.删除图片(7) -对我说 “真寻帮助 指令” 获取对应详细帮助 +对我说 “{NICKNAME}帮助 指令” 获取对应详细帮助 群主与管理员默认 5 级权限 ''' diff --git a/plugins/ai/data_source.py b/plugins/ai/data_source.py index aee2cb9a..f4a03247 100644 --- a/plugins/ai/data_source.py +++ b/plugins/ai/data_source.py @@ -1,4 +1,4 @@ -from configs.config import TL_KEY, ALAPI_TOKEN, ALAPI_AI_CHECK +from configs.config import TL_KEY, ALAPI_TOKEN, ALAPI_AI_CHECK, NICKNAME from configs.path_config import IMAGE_PATH, DATA_PATH from aiohttp.client import ClientSession from services.log import logger @@ -104,7 +104,7 @@ async def xie_ai(text: str, sess: ClientSession): if data["result"] == 0: content = data["content"] if "菲菲" in content: - content = content.replace("菲菲", "真寻") + content = content.replace("菲菲", f"{NICKNAME}") if "公众号" in content: content = "" if "{br}" in content: @@ -150,7 +150,7 @@ def no_result() -> str: random.choice( [ "你在说啥子?", - f"纯洁的小真寻没听懂", + f"纯洁的{NICKNAME}没听懂", "下次再告诉你(下次一定)", "你觉得我听懂了吗?嗯?", "我!不!知!道!", diff --git a/plugins/alapi/comments_163.py b/plugins/alapi/comments_163.py index a4d0a7d6..18bd9e47 100644 --- a/plugins/alapi/comments_163.py +++ b/plugins/alapi/comments_163.py @@ -1,8 +1,8 @@ from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from configs.config import ALAPI_TOKEN -from .util import get_data +from .data_source import get_data from services.log import logger __plugin_name__ = '网易云热评' @@ -27,7 +27,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): song_name = data['title'] await comments_163.send(f'{comment}\n\t——《{song_name}》') logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 发送网易云热评: {comment} \n\t\t————{song_name}") diff --git a/plugins/alapi/cover.py b/plugins/alapi/cover.py index 71914a08..c722c300 100644 --- a/plugins/alapi/cover.py +++ b/plugins/alapi/cover.py @@ -1,10 +1,10 @@ from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message +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 .util import get_data +from .data_source import get_data from services.log import logger __plugin_name__ = 'b封面' @@ -33,7 +33,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): 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 event.message_type != 'private' else 'private'})" + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 获取b站封面: {title} url:{img}") diff --git a/plugins/alapi/data_source.py b/plugins/alapi/data_source.py new file mode 100644 index 00000000..c0f38e24 --- /dev/null +++ b/plugins/alapi/data_source.py @@ -0,0 +1,39 @@ +from nonebot.adapters.cqhttp import MessageSegment +from utils.image_utils import CreateImg +from utils.message_builder import image +from configs.path_config import IMAGE_PATH +import aiohttp + + +async def get_data(url: str, params: dict): + async with aiohttp.ClientSession() as session: + try: + async with session.get( + url, timeout=2, params=params + ) as response: + data = await response.json() + if data["code"] == 200: + if not data["data"]: + return "没有搜索到...", 997 + return data, 200 + else: + return f'发生了错误...code:{data["code"]}', 999 + except TimeoutError: + return "超时了....", 998 + + +def gen_wbtop_pic(data) -> MessageSegment: + bk = CreateImg(700, 32 * 50 + 280, 700, 32, color='#797979') + wbtop_bk = CreateImg(700, 280, background=f'{IMAGE_PATH}/other/webtop.png') + bk.paste(wbtop_bk) + text_bk = CreateImg(700, 32 * 50, 700, 32, color='#797979') + for i, data in enumerate(data): + title = f"{i+1}. {data['hot_word']}" + hot = data['hot_word_num'] + img = CreateImg(700, 30, font_size=20) + w, h = img.getsize(title) + img.text((10, int((30 - h) / 2)), title) + img.text((580, int((30 - h) / 2)), hot) + text_bk.paste(img) + bk.paste(text_bk, (0, 280)) + return image(b64=bk.pic2bs4()) diff --git a/plugins/alapi/poetry.py b/plugins/alapi/poetry.py index f03faa1a..d2c2fa5e 100644 --- a/plugins/alapi/poetry.py +++ b/plugins/alapi/poetry.py @@ -1,9 +1,9 @@ from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, MessageEvent +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 .util import get_data +from .data_source import get_data __plugin_name__ = '古诗' __plugin_usage__ = '用法: 无' @@ -29,7 +29,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): author = data['author'] await poetry.send(f'{content}\n\t——{author}《{title}》') logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 发送古诗: f'{content}\n\t--{author}《{title}》'") diff --git a/plugins/alapi/util.py b/plugins/alapi/util.py deleted file mode 100644 index a4ae61fb..00000000 --- a/plugins/alapi/util.py +++ /dev/null @@ -1,17 +0,0 @@ -import aiohttp -from utils.utils import get_local_proxy - - -async def get_data(url: str, params: dict): - async with aiohttp.ClientSession() as session: - try: - async with session.get(url, proxy=get_local_proxy(), timeout=2, params=params) as response: - data = await response.json() - if data['code'] == 200: - if not data['data']: - return '没有搜索到...', 997 - return data, 200 - else: - return f'发生了错误...code:{data["code"]}', 999 - except TimeoutError: - return '超时了....', 998 \ No newline at end of file diff --git a/plugins/alapi/wbtop.py b/plugins/alapi/wbtop.py new file mode 100644 index 00000000..d3a83691 --- /dev/null +++ b/plugins/alapi/wbtop.py @@ -0,0 +1,62 @@ +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 +from utils.utils import get_message_text, is_number +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +import asyncio + +__plugin_name__ = '微博热搜' +__plugin_usage__ = '用法: 无' + + +wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True) + + +wbtop_url = 'https://v2.alapi.cn/api/new/wbtop' + +wbtop_data = [] + + +@wbtop.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + global wbtop_data + msg = get_message_text(event.json()) + if not wbtop_data or not msg: + params = { + 'token': ALAPI_TOKEN + } + data, code = await get_data(wbtop_url, params) + if code != 200: + await wbtop.finish(data, at_sender=True) + wbtop_data = data['data'] + img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data) + await wbtop.send(img) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查询微博热搜") + if is_number(msg) and 0 < int(msg) <= 50: + url = wbtop_data[int(msg) - 1]['url'] + browser = await get_browser() + page = None + try: + if not browser: + logger.warning('获取 browser 失败,请部署至 linux 环境....') + await wbtop.finish('获取 browser 对象失败...') + page = await browser.new_page() + await page.goto(url, wait_until='networkidle', timeout=10000) + await page.set_viewport_size({"width": 2560, "height": 1080}) + div = await page.query_selector("#pl_feedlist_index") + await div.screenshot(path=f'{IMAGE_PATH}/temp/webtop_{event.user_id}.png', timeout=100000) + await page.close() + await wbtop.send(image(f'webtop_{event.user_id}.png', 'temp')) + except Exception as e: + logger.error(f'微博热搜截图出错... {type(e)}: {e}') + if page: + await page.close() + await wbtop.send('发生了一些错误.....') + diff --git a/plugins/auto_invite/__init__.py b/plugins/auto_invite/__init__.py index 9ec5e381..ee42f678 100644 --- a/plugins/auto_invite/__init__.py +++ b/plugins/auto_invite/__init__.py @@ -2,7 +2,7 @@ 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 +from configs.config import AUTO_ADD_FRIEND, NICKNAME from nonebot.adapters.cqhttp.exception import ActionFailed from utils.utils import scheduler import time @@ -70,7 +70,9 @@ async def _(bot: Bot, event: GroupRequestEvent, state: dict): ) await bot.send_private_msg( user_id=event.user_id, - message="想要邀请我偷偷入群嘛~已经提醒真寻的管理员大人了\n" "请确保已经群主或群管理沟通过!\n" "等待管理员处理吧!", + message=f"想要邀请我偷偷入群嘛~已经提醒{NICKNAME}的管理员大人了\n" + "请确保已经群主或群管理沟通过!\n" + "等待管理员处理吧!", ) diff --git a/plugins/ban/__init__.py b/plugins/ban/__init__.py index b733893a..af4d70c7 100644 --- a/plugins/ban/__init__.py +++ b/plugins/ban/__init__.py @@ -3,16 +3,17 @@ 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 +from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent from nonebot.adapters.cqhttp.permission import GROUP from utils.utils import get_message_at, get_message_text, is_number +from configs.config import NICKNAME from services.log import logger __plugin_name__ = "Ban/unBan" __plugin_usage__ = ( "用法:\n" - "(不是禁言!是针对真寻是否处理封禁用户消息)\n" + f"(不是禁言!是针对{NICKNAME}是否处理封禁用户消息)\n" "封禁/解封用户 [小时] [分钟]\n" "示例:.ban @djdsk\n" "示例:.ban @djdsk 0 30\n" @@ -25,7 +26,6 @@ ban = on_command( ".ban", aliases={".unban", "/ban", "/unban"}, priority=5, - permission=GROUP, block=True, ) @@ -58,7 +58,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): and str(event.user_id) not in bot.config.superusers ): await ban.finish( - f"您的权限等级比对方低或相等, {list(bot.config.nickname)[0]}不能为您使用此功能!", + f"您的权限等级比对方低或相等, {NICKNAME}不能为您使用此功能!", at_sender=True, ) if await BanUser.ban( @@ -67,7 +67,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): logger.info( f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟" ) - result = f"已经将 {user_name} 加入{list(bot.config.nickname)[0]}的黑名单了!" + result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!" if time != -1: result += f"将在 {time/60} 分钟后解封" else: @@ -102,6 +102,58 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): 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) + + diff --git a/plugins/check_zhenxun_update/__init__.py b/plugins/check_zhenxun_update/__init__.py index 434c10e8..d83ceda5 100644 --- a/plugins/check_zhenxun_update/__init__.py +++ b/plugins/check_zhenxun_update/__init__.py @@ -2,7 +2,7 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent from nonebot.typing import T_State from nonebot.permission import SUPERUSER from nonebot import on_command -from .data_source import check_update, get_latest_version +from .data_source import check_update, get_latest_version_data from services.log import logger from utils.utils import scheduler, get_bot from pathlib import Path @@ -65,8 +65,9 @@ async def _(): _version = ( open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip() ) - latest_version, tar_gz_url = await get_latest_version() - if latest_version and tar_gz_url: + data = await get_latest_version_data() + if data: + latest_version = data["name"] if _version != latest_version: bot = get_bot() await bot.send_private_msg( diff --git a/plugins/check_zhenxun_update/data_source.py b/plugins/check_zhenxun_update/data_source.py index 95fe8cad..46a7621c 100644 --- a/plugins/check_zhenxun_update/data_source.py +++ b/plugins/check_zhenxun_update/data_source.py @@ -1,11 +1,11 @@ from aiohttp.client_exceptions import ClientConnectorError from nonebot.adapters.cqhttp import Bot from utils.user_agent import get_user_agent -from utils.utils import get_local_proxy, get_bot +from utils.utils import get_local_proxy +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH from typing import List -from bs4.element import Tag from services.log import logger -from bs4 import BeautifulSoup from pathlib import Path import ujson as json import nonebot @@ -24,8 +24,7 @@ if str(platform.system()).lower() == "windows": driver = nonebot.get_driver() -version_url = "https://github.com/HibiKier/zhenxun_bot/releases" -main_url = "https://github.com/HibiKier/zhenxun_bot" +release_url = "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest" _version_file = Path() / "__version__" zhenxun_latest_tar_gz = Path() / "zhenxun_latest_file.tar.gz" @@ -33,30 +32,24 @@ temp_dir = Path() / "temp" backup_dir = Path() / "backup" -@driver.on_startup -def init(): +@driver.on_bot_connect +async def remind(bot: Bot): if str(platform.system()).lower() != "windows": restart = Path() / "restart.sh" - env_file = Path() / ".env.dev" - if not restart.exists() and env_file.exists(): - with open(env_file, "r", encoding="utf8") as ef: - data = ef.readlines() - port = [x.split("=")[1].strip() for x in data if "port" in x.lower()][0] + if not restart.exists(): with open(restart, "w", encoding="utf8") as f: f.write( - "pid=$(netstat -tunlp | grep " + port + " | awk '{print $7}')\n" + f"pid=$(netstat -tunlp | grep " + + str(bot.config.port) + + " | awk '{print $7}')\n" "pid=${pid%/*}\n" "kill -9 $pid\n" "sleep 3\n" "python3 bot.py" ) os.system("chmod +x ./restart.sh") - logger.info("已自动生成 restart.sh(重启) 文件,请检查是否与本地指令符合...") - - -@driver.on_bot_connect -async def remind(bot: Bot): - is_restart_file = Path() / 'is_restart' + logger.info("已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合...") + is_restart_file = Path() / "is_restart" if is_restart_file.exists(): await bot.send_private_msg( user_id=int(list(bot.config.superusers)[0]), @@ -72,9 +65,11 @@ async def check_update(bot: Bot) -> int: _version = ( open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip() ) - latest_version, tar_gz_url = await get_latest_version() - if latest_version and tar_gz_url: + data = await get_latest_version_data() + if data: + latest_version = data["name"] if _version != latest_version: + tar_gz_url = data["tarball_url"] logger.info(f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}") await bot.send_private_msg( user_id=int(list(bot.config.superusers)[0]), @@ -88,22 +83,22 @@ async def check_update(bot: Bot) -> int: ) logger.info("真寻更新完毕,清理文件完成....") logger.info("开始获取真寻更新日志.....") - update_info = await get_updated_info() - if update_info: - logger.info("获取真寻更新日志成功...开始发送日志...") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"真寻更新完成,版本:{_version} -> {latest_version}\n" - f"更新日志:\n" - f"{update_info}", - ) - else: - logger.warning("获取真寻更新日志失败...") - await bot.send_private_msg( - user_id=int(list(bot.config.superusers)[0]), - message=f"真寻更新完成,版本:{_version} -> {latest_version}\n" - f"获取真寻更新日志失败...", - ) + update_info = data["body"] + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"真寻更新完成,版本:{_version} -> {latest_version}\n" + f"更新日期:{data['created_at']}\n" + f"更新日志:\n" + f"{update_info}", + ) + width = 0 + height = len(update_info.split('\n')) * 24 + for m in update_info.split('\n'): + if len(m) * 20 > width: + width = len(m) * 17 + A = CreateImg(800, height, font_size=20) + A.text((10, 10), update_info) + A.save(f'{IMAGE_PATH}/update_info.png') return 200 else: logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}") @@ -187,29 +182,16 @@ def _file_handle(latest_version: str): # 获取最新版本号 -async def get_latest_version() -> "str, str": +async def get_latest_version_data() -> dict: async with aiohttp.ClientSession(headers=get_user_agent()) as session: for _ in range(3): try: - async with session.get(version_url, proxy=get_local_proxy()) as res: + async with session.get(release_url, proxy=get_local_proxy()) as res: if res.status == 200: - soup = BeautifulSoup(await res.text(), "lxml") - div = soup.find("div", {"class": "release-entry"}) - latest_version = ( - div.find( - "div", {"class": "f1 flex-auto min-width-0 text-normal"} - ) - .find("a") - .text - ) - tar_gz_url = div.find_all( - "a", {"class": "d-flex flex-items-center"} - )[-1].get("href") - tar_gz_url = f"https://github.com{tar_gz_url}" - return latest_version, tar_gz_url + return await res.json() except (TimeoutError, ClientConnectorError): pass - return "", "" + return {} # 下载文件 @@ -236,33 +218,3 @@ def check_old_lines(lines: List[str], line: str) -> str: if len(l) > len(line): return l return line - - -async def get_updated_info() -> str: - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - for _ in range(3): - try: - async with session.get(main_url, proxy=get_local_proxy()) as res: - soup = BeautifulSoup(await res.text(), "lxml") - children_list = list(soup.find("article").children) - children_list = [x for x in children_list if x != "\n"] - for i, children in enumerate(children_list): - a = children.find("a") - if a and isinstance(a, Tag) and a.get("href") == "#更新": - update_info = "" - tmp_children_list = children_list[i:] - tmp_children_list = [ - x for x in tmp_children_list if "ul" in str(x) - ] - for j, chi in enumerate(tmp_children_list): - if "ul" in str(chi): - update_time = children_list[i:][j + 1].text - update_info += f"更新日期:{update_time}\n" - ul = children_list[i:][j + 2] - break - for li in ul.find_all("li"): - update_info += f"\t● {li.text}\n" - return update_info - except (TimeoutError, ClientConnectorError): - pass - return "" diff --git a/plugins/delete_img/__init__.py b/plugins/delete_img/__init__.py index e6891b82..e741bfba 100644 --- a/plugins/delete_img/__init__.py +++ b/plugins/delete_img/__init__.py @@ -7,6 +7,7 @@ from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from configs.config import IMAGE_DIR_LIST from utils.utils import is_number, cn2py +from pathlib import Path import os __plugin_name__ = "删除图片" @@ -51,28 +52,30 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): path = cn2py(state["path"]) img_id = state["id"] - path = IMAGE_PATH + path + # path = IMAGE_PATH + path + path = Path(IMAGE_PATH) / path + temp = Path(IMAGE_PATH) / 'temp' max_id = len(os.listdir(path)) - 1 if int(img_id) > max_id or int(img_id) < 0: await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) try: - if os.path.exists(IMAGE_PATH + TEMP_PATH + "delete.jpg"): - os.remove(IMAGE_PATH + TEMP_PATH + "delete.jpg") + if os.path.exists(temp / "delete.jpg"): + os.remove(temp / "delete.jpg") logger.info("删除图片 delete.jpg 成功") except Exception as e: logger.warning(f"删除图片 delete.jpg 失败 e{e}") try: - os.rename(path + img_id + ".jpg", IMAGE_PATH + TEMP_PATH + "delete.jpg") - logger.info(f"移动 {path}{img_id}.jpg 移动成功") + os.rename(path / f"{img_id}.jpg", temp / "delete.jpg") + logger.info(f"移动 {path}/{img_id}.jpg 移动成功") except Exception as e: - logger.warning(f"{path}{img_id}.jpg --> 移动失败 e:{e}") - if not os.path.exists(path + img_id + ".jpg"): + logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}") + if not os.path.exists(path / f"{img_id}.jpg"): try: if int(img_id) != max_id: - os.rename(path + str(max_id) + ".jpg", path + img_id + ".jpg") - except FileExistsError: - logger.error(f"{path}{max_id}.jpg 替换 {path}{img_id}.jpg 失败 e:{e}") - logger.info(f"{path}{max_id}.jpg 替换 {path}{img_id}.jpg 成功") + os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg") + except FileExistsError as e: + logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}") + logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功") logger.info( f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" f" -> id: {img_id} 删除成功" diff --git a/plugins/epic/__init__.py b/plugins/epic/__init__.py index 872ff952..47e9e261 100644 --- a/plugins/epic/__init__.py +++ b/plugins/epic/__init__.py @@ -1,6 +1,6 @@ from nonebot import on_command from services.log import logger -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from utils.utils import scheduler, get_bot from .data_source import get_epic_game @@ -20,7 +20,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): result, code = await get_epic_game() await epic.send(result) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f" 获取epic免费游戏" ) diff --git a/plugins/fudu.py b/plugins/fudu.py index d2a106c6..a3ac3d5b 100644 --- a/plugins/fudu.py +++ b/plugins/fudu.py @@ -16,22 +16,40 @@ from configs.config import FUDU_PROBABILITY class Fudu: def __init__(self): - self.mlist = defaultdict(list) + self.data = {} def append(self, key, content): - self.mlist[key].append(content) + self._create(key) + self.data[key]['data'].append(content) def clear(self, key): - self.mlist[key] = [] + self._create(key) + self.data[key]['data'] = [] + self.data[key]['is_repeater'] = False def size(self, key) -> int: - return len(self.mlist[key]) + self._create(key) + return len(self.data[key]['data']) def check(self, key, content) -> bool: - return self.mlist[key][0] == content + self._create(key) + return self.data[key]['data'][0] == content def get(self, key): - return self.mlist[key][0] + self._create(key) + return self.data[key]['data'][0] + + def is_repeater(self, key): + self._create(key) + return self.data[key]['is_repeater'] + + def set_repeater(self, key): + self._create(key) + self.data[key]['is_repeater'] = True + + def _create(self, key): + if self.data.get(key) is None: + self.data[key] = {'is_repeater': False, 'data': []} _fudulist = Fudu() @@ -64,7 +82,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): _fudulist.clear(event.group_id) _fudulist.append(event.group_id, add_msg) if _fudulist.size(event.group_id) > 2: - if random.random() < FUDU_PROBABILITY: + if random.random() < FUDU_PROBABILITY and not _fudulist.is_repeater(event.group_id): if random.random() < 0.2: await fudu.finish("打断施法!") if imgs and msg: @@ -77,7 +95,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): rst = "" if rst: await fudu.send(rst) - _fudulist.clear(event.group_id) + _fudulist.set_repeater(event.group_id) async def get_fudu_img_hash(url, group_id): diff --git a/plugins/genshin/query_resource_points/__init__.py b/plugins/genshin/query_resource_points/__init__.py index 26906682..9211954f 100644 --- a/plugins/genshin/query_resource_points/__init__.py +++ b/plugins/genshin/query_resource_points/__init__.py @@ -5,6 +5,7 @@ from utils.utils import get_message_text, scheduler from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message from nonebot.typing import T_State from services.log import logger +from nonebot.permission import SUPERUSER import re try: @@ -21,7 +22,7 @@ __plugin_usage__ = ( qr = on_command("原神资源查询", priority=5, block=True) qr_lst = on_command("原神资源列表", priority=5, block=True) rex_qr = on_regex(".*?(在哪|在哪里|哪有|哪里有).*?", rule=to_me(), priority=5, block=True) -update_info = on_command('更新原神资源信息', priority=1, block=True) +update_info = on_command('更新原神资源信息', permission=SUPERUSER, priority=1, block=True) @qr.handle() diff --git a/plugins/genshin/query_resource_points/query_resource.py b/plugins/genshin/query_resource_points/query_resource.py index d5389186..b15a731a 100644 --- a/plugins/genshin/query_resource_points/query_resource.py +++ b/plugins/genshin/query_resource_points/query_resource.py @@ -46,7 +46,7 @@ MAP_RATIO = 0.5 async def query_resource(resource_name: str) -> str: global CENTER_POINT planning_route: bool = False - if resource_name and resource_name[-2:] == "路径": + if resource_name and resource_name[-2:] in ["路径", "路线"]: resource_name = resource_name[:-2].strip() planning_route = True if not resource_name or resource_name not in resource_name_list: diff --git a/plugins/group_handle/__init__.py b/plugins/group_handle/__init__.py index 2a41f718..64344ab5 100644 --- a/plugins/group_handle/__init__.py +++ b/plugins/group_handle/__init__.py @@ -81,7 +81,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): @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 diff --git a/plugins/group_level/__init__.py b/plugins/group_level/__init__.py deleted file mode 100644 index 6ccc2010..00000000 --- a/plugins/group_level/__init__.py +++ /dev/null @@ -1,116 +0,0 @@ -from nonebot import on_command, on_regex -from utils.utils import get_message_text, is_number, FreqLimiter -from nonebot.rule import to_me -from services.log import logger -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent, GROUP -from nonebot.typing import T_State -from nonebot.matcher import Matcher -from nonebot.permission import SUPERUSER -from pathlib import Path -from configs.config import plugins2info_dict -from nonebot.message import run_preprocessor, IgnoredException - -try: - import ujson as json -except ModuleNotFoundError: - import json - -__plugin_name__ = "群权限" -__plugin_usage__ = "区分权限功能" - -flmt = FreqLimiter(60) - -group_level_data = Path() / "data" / "manager" / "group_level.json" -group_level_data.parent.mkdir(exist_ok=True, parents=True) -group_data = {} -if group_level_data.exists(): - group_data = json.load(open(group_level_data, "r", encoding="utf8")) - - -@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 isinstance(event, GroupMessageEvent): - if not group_data.get(str(event.group_id)): - group_data[str(event.group_id)] = 5 - if not check_level_group(matcher.module, event.group_id): - try: - if flmt.check(event.group_id): - flmt.start_cd(event.group_id) - await bot.send_group_msg( - group_id=event.group_id, message="群权限不足..." - ) - except AttributeError: - pass - raise IgnoredException("群权限不足") - - -add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True) -my_group_level = on_command( - "查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True -) -what_up_group_level = on_regex( - ".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?", - rule=to_me(), - priority=5, - permission=GROUP, - block=True, -) - - -@add_group_level.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if not msg: - await add_group_level.finish("用法:修改群权限 [group] [level]") - msg = msg.split(" ") - if len(msg) < 2: - await add_group_level.finish("参数不完全..[group] [level]") - if is_number(msg[0]) and is_number(msg[1]): - group_id = msg[0] - level = int(msg[1]) - else: - await add_group_level.finish("参数错误...group和level必须是数字..") - if not group_data.get(group_id): - group_data[group_id] = 5 - await add_group_level.send("修改成功...", at_sender=True) - await bot.send_group_msg( - group_id=int(group_id), message=f"管理员修改了此群权限:{group_data[group_id]} -> {level}" - ) - group_data[group_id] = level - with open(group_level_data, "w", encoding="utf8") as f: - json.dump(group_data, f, ensure_ascii=False, indent=4) - logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}") - - -@my_group_level.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if group_data.get(str(event.group_id)): - level = group_data[str(event.group_id)] - tmp = "" - for plugin in plugins2info_dict: - if plugins2info_dict[plugin]["level"] > level: - plugin_name = plugins2info_dict[plugin]["cmd"][0] - if plugin_name == "pixiv": - plugin_name = "搜图 p站排行" - tmp += f"{plugin_name}\n" - if tmp: - tmp = "\n目前无法使用的功能:\n" + tmp - await my_group_level.finish(f"当前群权限:{level}{tmp}") - - -@what_up_group_level.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await what_up_group_level.finish( - f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限" - ) - - -# 检查权限 -def check_level_group(module: str, group_id: int) -> bool: - if plugins2info_dict.get(module): - if plugins2info_dict[module]["level"] > group_data[str(group_id)]: - return False - return True diff --git a/plugins/group_manager/__init__.py b/plugins/group_manager/__init__.py new file mode 100644 index 00000000..1ef81650 --- /dev/null +++ b/plugins/group_manager/__init__.py @@ -0,0 +1,102 @@ +from nonebot import on_command, on_regex +from utils.utils import get_message_text, is_number +from nonebot.rule import to_me +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 plugins2info_dict, NICKNAME +from utils.static_data import group_manager + +try: + import ujson as json +except ModuleNotFoundError: + import json + +__plugin_name__ = "群权限操作" +__plugin_usage__ = "区分权限功能" + + +add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True) +my_group_level = on_command( + "查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True +) +what_up_group_level = on_regex( + ".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?", + rule=to_me(), + priority=5, + permission=GROUP, + block=True, +) + +manager_group_whitelist = on_command( + "添加群白名单", aliases={"删除群白名单"}, priority=1, permission=SUPERUSER, block=True +) + + +@add_group_level.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await add_group_level.finish("用法:修改群权限 [group] [level]") + msg = msg.split(" ") + if len(msg) < 2: + await add_group_level.finish("参数不完全..[group] [level]") + if is_number(msg[0]) and is_number(msg[1]): + group_id = msg[0] + level = int(msg[1]) + else: + await add_group_level.finish("参数错误...group和level必须是数字..") + old_level = group_manager.get_group_level(group_id) + group_manager.set_group_level(group_id, level) + await add_group_level.send("修改成功...", at_sender=True) + await bot.send_group_msg( + group_id=int(group_id), message=f"管理员修改了此群权限:{old_level} -> {level}" + ) + logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}") + + +@my_group_level.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + level = group_manager.get_group_level(str(event.group_id)) + tmp = "" + for plugin in plugins2info_dict: + if plugins2info_dict[plugin]["level"] > level: + plugin_name = plugins2info_dict[plugin]["cmd"][0] + if plugin_name == "pixiv": + plugin_name = "搜图 p站排行" + tmp += f"{plugin_name}\n" + if tmp: + tmp = "\n目前无法使用的功能:\n" + tmp + await my_group_level.finish(f"当前群权限:{level}{tmp}") + + +@what_up_group_level.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await what_up_group_level.finish( + f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限" + ) + + +@manager_group_whitelist.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).split() + all_group = [ + g["group_id"] for g in await bot.get_group_list(self_id=int(bot.self_id)) + ] + group_list = [] + for group in msg: + if is_number(group) and int(group) in all_group: + group_list.append(int(group)) + if group_list: + for group in group_list: + if state["_prefix"]["raw_command"] in ["添加群白名单"]: + group_manager.add_group_white_list(group) + else: + group_manager.delete_group_white_list(group) + group_list = [str(x) for x in group_list] + await manager_group_whitelist.send( + "已成功将 " + "\n".join(group_list) + " " + state["_prefix"]["raw_command"] + ) + else: + await manager_group_whitelist.send(f"添加失败,请检查{NICKNAME}是否已加入这些群聊或重复添加/删除群白单名") diff --git a/plugins/help/__init__.py b/plugins/help/__init__.py index 76c8e8e1..487973f5 100644 --- a/plugins/help/__init__.py +++ b/plugins/help/__init__.py @@ -6,12 +6,9 @@ from configs.path_config import DATA_PATH from utils.message_builder import image import os from .data_source import create_help_img, create_group_help_img, get_plugin_help -from nonebot import require from utils.utils import get_message_text -export = require("nonebot_plugin_manager") - __plugin_name__ = "帮助" diff --git a/plugins/help/config.py b/plugins/help/config.py index aabae177..6b2c9b9c 100644 --- a/plugins/help/config.py +++ b/plugins/help/config.py @@ -1,3 +1,5 @@ +from configs.config import NICKNAME + # 实用 utility_help = { "update_pic": "一些对图片的操作 --> 指令:操作图片/图片/修改图片(包含 10 种图片操作)", @@ -46,12 +48,13 @@ entertainment_help = { "qiu_qiu_translation": "这家伙到底在说什么? --> 指令:丘丘翻译/丘丘一下/丘丘语翻译", "query_resource_points": "地图资源速速查看 --> 指令:原神资源查询xx[路径]/原神资源列表/哪里有xx/xx在哪(xx=资源名称)", "russian": "紧张刺激的俄罗斯轮盘 --> 指令:俄罗斯轮盘帮助", - "gold_redbag": "运气项目又来啦! --> 指令:塞红包/ 抢红包指令: 开/抢/戳一戳真寻/ 退回:退回未抢完的红包", - "poetry": "突然文艺的真寻是否搞错了什么 --> 指令:念诗/来首诗/念首诗", + "gold_redbag": f"运气项目又来啦! --> 指令:塞红包/ 抢红包指令: 开/抢/戳一戳{NICKNAME}/ 退回:退回未抢完的红包", + "poetry": f"突然文艺的{NICKNAME}是否搞错了什么 --> 指令:念诗/来首诗/念首诗", "comments_163": "生了个人,我很抱歉 --> 指令:到点了/12点了/网易云热评/网易云评论", "pix_gallery": "偶尔也想看看美图? --> 指令:PIX [关键词/uid/pid:pid] [num]/查看pix图库 [关键词]/显示pix关键词", 'nbnhhsh': "会说话就多说点! --> 指令:nbnhhsh/能不能好好说话 [文本](空格划分)", - "roll": "让真寻来帮你决定吧! --> 指令:roll/ roll [文本](空格划分)" + "roll": f"让{NICKNAME}来帮你决定吧! --> 指令:roll/ roll [文本](空格划分)", + "wbtop": f"刚买完瓜,在吃瓜现场 --> 指令:微博热搜/微博热搜 [数字]" } # 其他 other_help = [ @@ -60,7 +63,7 @@ other_help = [ "不得看看自己权力多大? --> 指令:我的权限", "有人记得你是什么时候加入我们的 --> 指令:我的信息", "让我看看更新了什么 --> 指令:更新信息", - "真寻给我把话收回去! --> 指令:撤回 [id](默认0)", + f"{NICKNAME}给我把话收回去! --> 指令:撤回 [id](默认0)", "群拥有的权限 --> 指令:查看群权限", "数据统计可视化_1 --> 指令:功能调用统计/日功能调用统计/周功能调用统计/月功能调用统计", "数据统计可视化_2 --> 指令:周功能调用统计 [功能名称]/月功能调用统计 [功能名称]", diff --git a/plugins/help/data_source.py b/plugins/help/data_source.py index 4598bb12..f459fcd5 100644 --- a/plugins/help/data_source.py +++ b/plugins/help/data_source.py @@ -1,18 +1,16 @@ from utils.image_utils import CreateImg from configs.path_config import IMAGE_PATH, DATA_PATH -import ujson as json -import os +from pathlib import Path from .config import * -from nonebot import require from configs.config import ( INITIAL_OPEN_CASE_COUNT, INITIAL_SETU_PROBABILITY, ADMIN_DEFAULT_AUTH, ) -from configs.config import plugins2info_dict +from configs.config import plugins2info_dict, NICKNAME +from utils.static_data import group_manager import nonebot -export = require("nonebot_plugin_manager") width = 1600 e_height = 0 @@ -22,8 +20,9 @@ o_height = 1500 def create_help_img(): - if os.path.exists(IMAGE_PATH + "help.png"): - os.remove(IMAGE_PATH + "help.png") + help_img_file = Path(IMAGE_PATH) / 'help.png' + if help_img_file.exists(): + help_img_file.unlink() h = ( 100 + len(utility_help) * 24 @@ -60,10 +59,10 @@ def create_help_img(): A.paste(o, (0, o_height)) A.text( (10, h * 0.68), - "大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对真寻说 “真寻帮助 指令名” 获取对应详细帮助\n" + f"大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对{NICKNAME}说 “{NICKNAME}帮助 指令名” 获取对应详细帮助\n" "可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)" "\n[群管理员请看 管理员帮助(群主与管理员自带 5 级权限)]\n\n" - "\t「如果真寻回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", + f"\t「如果{NICKNAME}回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", ) A.save(IMAGE_PATH + "help.png") @@ -71,11 +70,6 @@ def create_help_img(): def create_group_help_img(group_id: int): group_id = str(group_id) - try: - with open(DATA_PATH + "manager/plugin_list.json", "r", encoding="utf8") as f: - plugin_list = json.load(f) - except (ValueError, FileNotFoundError): - pass h = ( 100 + len(utility_help) * 24 @@ -89,7 +83,7 @@ def create_group_help_img(group_id: int): rst = "" i = 1 for cmd in entertainment_help.keys(): - flag, dfg = parse_cmd(cmd, group_id, plugin_list) + flag, dfg = parse_cmd(cmd, group_id) if dfg: cmd = rcmd(dfg) rst += f"【{flag}】{i}.{entertainment_help[cmd]}\n" @@ -100,7 +94,7 @@ def create_group_help_img(group_id: int): rst = "" i = 1 for cmd in utility_help.keys(): - flag, dfg = parse_cmd(cmd, group_id, plugin_list) + flag, dfg = parse_cmd(cmd, group_id) rst += f"【{flag}】{i}.{utility_help[cmd]}\n" i += 1 u.text((10, 10), "实用功能:", fill=(255, 255, 255)) @@ -118,7 +112,7 @@ def create_group_help_img(group_id: int): # A.text((width, 10), f'总开关【{"√" if data["总开关"] else "×"}】') A.text( (10, h * 0.68), - "大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对真寻说 “真寻帮助 指令名” 获取对应详细帮助\n" + f"大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对{NICKNAME}说 “{NICKNAME}帮助 指令名” 获取对应详细帮助\n" "可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)" f"\n[群管理员请看 管理员帮助(群主与管理员自带 {ADMIN_DEFAULT_AUTH} 级权限)]", ) @@ -130,12 +124,12 @@ def create_group_help_img(group_id: int): f"\t\t示例:开启签到\n" f"\t\t可以通过管理员开关自动发送消息(早晚安等)\n" f"\t\t^请查看管理员帮助^\n\n" - f"\t「如果真寻回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", + f"\t「如果{NICKNAME}回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", ) A.save(DATA_PATH + f"group_help/{group_id}.png") -def parse_cmd(cmd, group_id, plugin_list): +def parse_cmd(cmd, group_id): flag = "√" dfg = None if cmd.find("draw_card") != -1: @@ -148,9 +142,8 @@ def parse_cmd(cmd, group_id, plugin_list): elif cmd == "pixiv_s": cmd = "pixiv" dfg = "s" - if group_id in plugin_list[cmd]: - if not plugin_list[cmd][group_id]: - flag = "×" + if not group_manager.get_plugin_status(cmd, group_id): + flag = "×" if cmd in ["bt", "reimu", "nickname"]: flag = "- " return flag, dfg diff --git a/plugins/hook.py b/plugins/hook.py index c0c84be1..bede26c8 100644 --- a/plugins/hook.py +++ b/plugins/hook.py @@ -1,5 +1,6 @@ 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, @@ -13,12 +14,16 @@ from configs.config import ( MALICIOUS_BAN_TIME, MALICIOUS_CHECK_TIME, MALICIOUS_BAN_COUNT, + CHECK_NOTICE_INFO_CD, + plugins2info_dict, ) from models.ban_user import BanUser from utils.utils import is_number, static_flmt, BanCheckLimiter from utils.message_builder import at from services.log import logger from models.level_user import LevelUser +from utils.static_data import group_manager +from utils.utils import FreqLimiter try: import ujson as json @@ -45,24 +50,34 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): time = str(int(time / 60)) + " 分" else: time = str(time) + " 分" - if event.message_type == "group": + 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: - await bot.send_group_msg( - group_id=event.group_id, - message=at(event.user_id) + BAN_RESULT + f" 在..在 {time}后才会理你喔", - ) + 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: - await bot.send_private_msg( - user_id=event.user_id, - message=at(event.user_id) + BAN_RESULT + f" 在..在 {time}后才会理你喔", - ) + 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("用户处于黑名单中") @@ -85,47 +100,153 @@ async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State if not static_flmt.check(event.user_id): return static_flmt.start_cd(event.user_id) - await bot.send_group_msg( - group_id=event.group_id, - message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", - ) + try: + await bot.send_group_msg( + group_id=event.group_id, + message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", + ) + except ActionFailed: + pass else: if not static_flmt.check(event.user_id): return static_flmt.start_cd(event.user_id) - await bot.send_private_msg( - user_id=event.user_id, - message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", - ) + 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) + + # 权限检测 @run_preprocessor async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - if not isinstance(event, MessageEvent) or await BanUser.is_ban(event.user_id): + if ( + not isinstance(event, MessageEvent) + or await BanUser.is_ban(event.user_id) + or str(event.user_id) in bot.config.superusers + ): return - if matcher.module in admin_plugins_auth.keys() and matcher.priority not in [1, 9]: - if event.message_type == "group": + module = matcher.module + if module in admin_plugins_auth.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_plugins_auth[matcher.module] + event.user_id, event.group_id, admin_plugins_auth[module] ): - await bot.send_group_msg( - group_id=event.group_id, - message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" - f"{admin_plugins_auth[matcher.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_plugins_auth[module]}", + ) + except ActionFailed: + pass raise IgnoredException("权限不足") else: if not await LevelUser.check_level( - event.user_id, 0, admin_plugins_auth[matcher.module] + event.user_id, 0, admin_plugins_auth[module] ): - await bot.send_private_msg( - user_id=event.user_id, - message=f"你的权限不足喔,该功能需要的权限等级:{admin_plugins_auth[matcher.module]}", - ) + try: + await bot.send_private_msg( + user_id=event.user_id, + message=f"你的权限不足喔,该功能需要的权限等级:{admin_plugins_auth[module]}", + ) + except ActionFailed: + pass raise IgnoredException("权限不足") + if module in plugins2info_dict.keys(): + if isinstance(event, GroupMessageEvent): + # 群权限 + if plugins2info_dict[module]["level"] > group_manager.get_group_level( + str(event.group_id) + ): + try: + if _flmt_g.check(event.user_id): + _flmt_g.start_cd(event.user_id) + await bot.send_group_msg( + group_id=event.group_id, message="群权限不足..." + ) + except ActionFailed: + pass + raise IgnoredException("群权限不足") + # 插件状态 + if not group_manager.get_plugin_status(module, str(event.group_id)): + try: + if _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 + raise IgnoredException("未开启此功能...") + # 管理员禁用 + if not group_manager.get_plugin_status( + f"{module}:super", str(event.group_id) + ): + try: + if _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 + raise IgnoredException("管理员禁用了此群该功能...") + # 群聊禁用 + if not group_manager.get_plugin_status(module, block_type="group"): + try: + if _flmt_c.check(event.group_id): + _flmt_c.start_cd(event.group_id) + await bot.send_group_msg( + group_id=event.group_id, message="该功能在群聊中已被禁用..." + ) + except ActionFailed: + pass + 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" + ) and not group_manager.check_group_is_white(event.group_id): + try: + if _flmt_c.check(event.group_id): + _flmt_c.start_cd(event.group_id) + if isinstance(event, GroupMessageEvent): + 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 + raise IgnoredException("此功能正在维护...") # 为什么AI会自己和自己聊天 @@ -145,5 +266,3 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if matcher.type == "message": if state["_prefix"]["raw_command"] and matcher.module == "ai": raise IgnoredException("有命令就别说话了") - - diff --git a/plugins/jitang.py b/plugins/jitang.py index 1f57fa26..26271453 100644 --- a/plugins/jitang.py +++ b/plugins/jitang.py @@ -5,9 +5,10 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State import aiohttp from asyncio.exceptions import TimeoutError +from configs.config import NICKNAME __plugin_name__ = "鸡汤" -__plugin_usage__ = "用法: 发送’鸡汤‘,真寻亲自为你喝鸡汤" +__plugin_usage__ = f"用法: 发送’鸡汤‘,{NICKNAME}亲自为你喝鸡汤" url = "https://v2.alapi.cn/api/soul" diff --git a/plugins/move_img/__init__.py b/plugins/move_img/__init__.py index 2bc033b6..65fb7b5f 100644 --- a/plugins/move_img/__init__.py +++ b/plugins/move_img/__init__.py @@ -7,6 +7,7 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from configs.config import IMAGE_DIR_LIST from utils.utils import is_number, cn2py from configs.path_config import IMAGE_PATH +from pathlib import Path __plugin_name__ = "移动图片" @@ -59,31 +60,32 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @move_img.got("id", prompt="要移动的图片id是?") async def _(bot: Bot, event: MessageEvent, state: T_State): img_id = state["id"] - source_path = IMAGE_PATH + cn2py(state["source_path"]) - destination_path = IMAGE_PATH + cn2py(state["destination_path"]) + source_path = Path(IMAGE_PATH) / cn2py(state["source_path"]) + destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"]) + destination_path.mkdir(parents=True, exist_ok=True) max_id = len(os.listdir(source_path)) - 1 des_max_id = len(os.listdir(destination_path)) if int(img_id) > max_id or int(img_id) < 0: await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) try: os.rename( - source_path + img_id + ".jpg", destination_path + str(des_max_id) + ".jpg" + 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} 移动成功" + f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功" ) except Exception as e: logger.warning( - f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动失败 e:{e}" + f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}" ) await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True) if max_id > 0: try: - os.rename(source_path + str(max_id) + ".jpg", source_path + img_id + ".jpg") - logger.info(f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 成功") + os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg") + logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功") except Exception as e: logger.warning( - f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 失败 e:{e}" + f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}" ) await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True) logger.info( diff --git a/plugins/mute.py b/plugins/mute.py index 23763ecb..3f124ef0 100644 --- a/plugins/mute.py +++ b/plugins/mute.py @@ -11,7 +11,7 @@ from utils.image_utils import get_img_hash from services.log import logger import aiohttp import aiofiles -from configs.config import MUTE_DEFAULT_COUNT, MUTE_DEFAULT_TIME, MUTE_DEFAULT_DURATION +from configs.config import MUTE_DEFAULT_COUNT, MUTE_DEFAULT_TIME, MUTE_DEFAULT_DURATION, NICKNAME try: import ujson as json @@ -105,7 +105,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): user_id=event.user_id, duration=mute_data[group_id]["duration"], ) - await mute.send("检测到恶意刷屏,真寻要把你关进小黑屋!", at_sender=True) + await mute.send(f"检测到恶意刷屏,{NICKNAME}要把你关进小黑屋!", at_sender=True) mute_dict[event.user_id]["count"] = 0 logger.info( f"USER {event.user_id} GROUP {event.group_id} " diff --git a/plugins/nickname.py b/plugins/nickname.py index 7d2a8aec..97c6ee91 100644 --- a/plugins/nickname.py +++ b/plugins/nickname.py @@ -8,11 +8,12 @@ from models.friend_user import FriendUser import random from models.ban_user import BanUser from services.log import logger +from configs.config import NICKNAME __plugin_name__ = "昵称系统" -__plugin_usage__ = "用法:\n" "以后叫我 [名称]\n" "真寻我是谁" +__plugin_usage__ = f"用法:\n以后叫我 [名称]\n{NICKNAME}我是谁" nickname = on_command( "nickname", @@ -46,13 +47,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): await nickname.send( random.choice( [ - "好啦好啦,我知道啦,{},以后就这么叫你吧", - "嗯嗯,小真寻记住你的昵称了哦,{}", - "好突然,突然要叫你昵称什么的...{}..", - "小真寻会好好记住的{}的,放心吧", - "好..好.,那窝以后就叫你{}了.", + f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", + f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", + f"好突然,突然要叫你昵称什么的...{msg}..", + f"{NICKNAME}会好好记住的{msg}的,放心吧", + f"好..好.,那窝以后就叫你{msg}了.", ] - ).format(msg) + ) ) logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}") else: @@ -71,13 +72,13 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): await nickname.send( random.choice( [ - "好啦好啦,我知道啦,{},以后就这么叫你吧", - "嗯嗯,小真寻记住你的昵称了哦,{}", - "好突然,突然要叫你昵称什么的...{}..", - "小真寻会好好记住的{}的,放心吧", - "好..好.,那窝以后就叫你{}了.", + f"好啦好啦,我知道啦,{msg},以后就这么叫你吧", + f"嗯嗯,{NICKNAME}记住你的昵称了哦,{msg}", + f"好突然,突然要叫你昵称什么的...{msg}..", + f"{NICKNAME}会好好记住的{msg}的,放心吧", + f"好..好.,那窝以后就叫你{msg}了.", ] - ).format(msg) + ) ) logger.info(f"USER {event.user_id} 设置昵称 {msg}") else: @@ -97,14 +98,14 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): await my_nickname.send( random.choice( [ - "我肯定记得你啊,你是{}啊", - "我不会忘记你的,你也不要忘记我!{}", - "哼哼,真寻记忆力可是很好的,{}", - "嗯?你是失忆了嘛...{}..", - "不要小看真寻的记忆力啊!笨蛋{}!QAQ", - "哎?{}..怎么了吗..突然这样问..", + f"我肯定记得你啊,你是{nickname_}啊", + f"我不会忘记你的,你也不要忘记我!{nickname_}", + f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}", + f"嗯?你是失忆了嘛...{nickname_}..", + f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ", + f"哎?{nickname_}..怎么了吗..突然这样问..", ] - ).format(nickname_) + ) ) else: nickname_ = event.sender.card if event.sender.card else event.sender.nickname @@ -122,14 +123,14 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): await my_nickname.send( random.choice( [ - "我肯定记得你啊,你是{}啊", - "我不会忘记你的,你也不要忘记我!{}", - "哼哼,真寻记忆力可是很好的,{}", - "嗯?你是失忆了嘛...{}..", - "不要小看真寻的记忆力啊!笨蛋{}!QAQ", - "哎?{}..怎么了吗..突然这样问..", + f"我肯定记得你啊,你是{nickname_}啊", + f"我不会忘记你的,你也不要忘记我!{nickname_}", + f"哼哼,{NICKNAME}记忆力可是很好的,{nickname_}", + f"嗯?你是失忆了嘛...{nickname_}..", + f"不要小看{NICKNAME}的记忆力啊!笨蛋{nickname_}!QAQ", + f"哎?{nickname_}..怎么了吗..突然这样问..", ] - ).format(nickname_) + ) ) else: nickname_ = (await bot.get_stranger_info(user_id=event.user_id))["nickname"] @@ -149,13 +150,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): await cancel_nickname.send( random.choice( [ - "呜..真寻睡一觉就会忘记的..和梦一样..{}", - "窝知道了..{}..", - "是真寻哪里做的不好嘛..好吧..晚安{}", - "呃,{},下次我绝对绝对绝对不会再忘记你!", - "可..可恶!{}!太可恶了!呜", + f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}", + f"窝知道了..{nickname_}..", + f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}", + f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!", + f"可..可恶!{nickname_}!太可恶了!呜", ] - ).format(nickname_) + ) ) await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, "") await BanUser.ban(event.user_id, 9, 60) @@ -170,13 +171,13 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): await cancel_nickname.send( random.choice( [ - "呜..真寻睡一觉就会忘记的..和梦一样..{}", - "窝知道了..{}..", - "是真寻哪里做的不好嘛..好吧..晚安 {}", - "呃,{},下次我绝对绝对绝对不会再忘记你!", - "可..可恶!{}!太可恶了!呜", + f"呜..{NICKNAME}睡一觉就会忘记的..和梦一样..{nickname_}", + f"窝知道了..{nickname_}..", + f"是{NICKNAME}哪里做的不好嘛..好吧..晚安{nickname_}", + f"呃,{nickname_},下次我绝对绝对绝对不会再忘记你!", + f"可..可恶!{nickname_}!太可恶了!呜", ] - ).format(nickname_) + ) ) await FriendUser.get_user_name(event.user_id) await BanUser.ban(event.user_id, 9, 60) diff --git a/plugins/nonebot_plugin_manager/__init__.py b/plugins/nonebot_plugin_manager/__init__.py deleted file mode 100644 index 52fe47b0..00000000 --- a/plugins/nonebot_plugin_manager/__init__.py +++ /dev/null @@ -1,122 +0,0 @@ -from nonebot.plugin import on_shell_command, get_loaded_plugins, export -from nonebot.matcher import Matcher -from nonebot.typing import T_State -from nonebot.exception import IgnoredException -from nonebot.message import run_preprocessor -from nonebot.adapters.cqhttp import ( - Event, - Bot, - GroupMessageEvent, - PrivateMessageEvent, - MessageEvent, -) -from configs.config import plugins2info_dict -from utils.utils import FreqLimiter -from models.ban_user import BanUser -from .data import ( - block_plugin, - unblock_plugin, - get_group_plugin_list, - auto_update_plugin_list, -) -from .parser import npm_parser - -# 导出给其他插件使用 -export = export() -export.block_plugin = block_plugin -export.unblock_plugin = unblock_plugin -export.get_group_plugin_list = get_group_plugin_list - -# 注册 shell_like 事件响应器 -plugin_manager = on_shell_command("npm", parser=npm_parser, priority=1) - -flmt = FreqLimiter(60) - - -# 在 Matcher 运行前检测其是否启用 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): - if not isinstance(event, MessageEvent) and matcher.module != 'poke': - return - plugin = matcher.module - group_id = _get_group_id(event) - loaded_plugin_list = _get_loaded_plugin_list() - plugin_list = auto_update_plugin_list(loaded_plugin_list) - - # 无视本插件的 Matcher - if ( - plugin not in plugins2info_dict - or matcher.priority in [1, 9] - or await BanUser.is_ban(event.user_id) - ): - return - if isinstance(event, PrivateMessageEvent) and plugin_list[plugin]["default"]: - return - if not plugin_list[plugin]["default"]: - if str(event.user_id) in bot.config.superusers: - return - if isinstance(event, GroupMessageEvent): - if flmt.check(event.group_id): - flmt.start_cd(event.group_id) - await bot.send_group_msg(group_id=event.group_id, message="此功能正在维护...") - else: - if flmt.check(event.user_id): - flmt.start_cd(event.user_id) - await bot.send_private_msg(user_id=event.user_id, message="此功能正在维护...") - raise IgnoredException(f"Nonebot Plugin Manager has blocked {plugin} !") - - if group_id in plugin_list[plugin]: - if not plugin_list[plugin][group_id]: - if plugin != "ai" and matcher.type == "message": - if flmt.check(event.group_id): - flmt.start_cd(event.group_id) - await bot.send_group_msg( - group_id=event.group_id, message="该群未开启此功能.." - ) - raise IgnoredException(f"Nonebot Plugin Manager has blocked {plugin} !") - - -@plugin_manager.handle() -async def _(bot: Bot, event: Event, state: T_State): - args = state["args"] - group_id = _get_group_id(event) - is_admin = _is_admin(event) - is_superuser = _is_superuser(bot, event) - if group_id == 0: - group_id = "default" - - if hasattr(args, "handle"): - await plugin_manager.finish(args.handle(args, group_id, is_admin, is_superuser)) - - -# 获取插件列表,并自动排除本插件 -def _get_loaded_plugin_list() -> list: - return list( - filter( - lambda plugin: plugin != "nonebot_plugin_manager", - map(lambda plugin: plugin.name, get_loaded_plugins()), - ) - ) - - -def _get_group_id(event: Event) -> str: - try: - group_id = event.group_id - except AttributeError: - group_id = "default" - return str(group_id) if group_id else "default" - - -def _is_admin(event: Event) -> bool: - return ( - event.sender.role in ["admin", "owner"] - if isinstance(event, GroupMessageEvent) - else False - ) - - -def _is_superuser(bot: Bot, event: Event) -> bool: - return str(event.user_id) in bot.config.superusers - - -plugins_dict = {} diff --git a/plugins/nonebot_plugin_manager/data.py b/plugins/nonebot_plugin_manager/data.py deleted file mode 100644 index ed5363c5..00000000 --- a/plugins/nonebot_plugin_manager/data.py +++ /dev/null @@ -1,121 +0,0 @@ -import json -import httpx -from pathlib import Path -from configs.config import plugins2info_dict - -_DATA_PATH = Path() / "data" / "manager" / "plugin_list.json" - - -def get_store_plugin_info(plugin: str) -> str: - store_plugin_list = _get_store_plugin_list() - if plugin in store_plugin_list: - plugin = store_plugin_list[plugin] - return ( - f"ID: {plugin['id']}\n" - f"Name: {plugin['name']}\n" - f"Description: {plugin['desc']}\n" - f"Version: {httpx.get('https://pypi.org/pypi/'+plugin['link']+'/json').json()['info']['version']}\n" - f"Author: {plugin['author']}\n" - f"Repo: https://github.com/{plugin['repo']}" - ) - else: - return "查无此插件!" - - -def get_group_plugin_list(group_id: str) -> dict: - plugin_list = _load_plugin_list() - group_plugin_list = {} - for plugin in plugin_list: - if group_id in plugin_list[plugin]: - group_plugin_list[plugin] = plugin_list[plugin][group_id] - else: - group_plugin_list[plugin] = plugin_list[plugin]["default"] - return group_plugin_list - - -def get_store_pulgin_list() -> str: - message = "商店插件列表如下:" - for plugin in _get_store_plugin_list(): - if plugin in _load_plugin_list() or plugin == "nonebot_plugin_manager": - message += f"\n[o] {plugin}" - else: - message += f"\n[x] {plugin}" - return message - - -def auto_update_plugin_list(loaded_plugin_list: list, keep_history: bool = False): - plugin_list = _load_plugin_list() - for plugin in loaded_plugin_list: - if plugin not in plugin_list: - plugin_list[plugin] = {"default": True} - if not keep_history: - plugin_list = { - key: plugin_list[key] for key in plugin_list if key in loaded_plugin_list - } - _dump_plugin_list(plugin_list) - return plugin_list - - -def block_plugin(group_id: str, *plugins: str): - return _update_plugin_list(group_id, True, *plugins) - - -def unblock_plugin(group_id: str, *plugins: str): - return _update_plugin_list(group_id, False, *plugins) - - -# 获取商店插件列表 -def _get_store_plugin_list() -> dict: - store_plugin_list = {} - for plugin in httpx.get( - "https://cdn.jsdelivr.net/gh/nonebot/nonebot2@master/docs/.vuepress/public/plugins.json" - ).json(): - store_plugin_list.update({plugin["id"]: plugin}) - return store_plugin_list - - -# 更新插件列表 -def _update_plugin_list(group_id: str, block: bool, *plugins: str) -> str: - plugin_list = _load_plugin_list() - message = "结果如下:" - operate = "屏蔽" if block else "启用" - for plugin in plugins: - for values in plugins2info_dict.values(): - if plugin in values['cmd']: - plugin = list(plugins2info_dict.keys())[list(plugins2info_dict.values()).index(values)] - # print(plugin) - break - message += "\n" - if plugin in plugin_list: - if ( - not group_id in plugin_list[plugin] - or plugin_list[plugin][group_id] == block - ): - plugin_list[plugin][group_id] = not block - message += f"插件{plugin}{operate}成功!" - print(plugin_list[plugin][group_id]) - else: - message += f"插件{plugin}已经{operate}!" - else: - message += f"插件{plugin}不存在!" - _dump_plugin_list(plugin_list) - return message - - -# 加载插件列表 -def _load_plugin_list() -> dict: - try: - return json.load(_DATA_PATH.open("r", encoding="utf-8")) - except FileNotFoundError: - return {} - - -# 保存插件列表 -def _dump_plugin_list(plugin_list: dict): - _DATA_PATH.parent.mkdir(parents=True, exist_ok=True) - json.dump( - plugin_list, - _DATA_PATH.open("w", encoding="utf-8"), - indent=4, - separators=(",", ": "), - ) diff --git a/plugins/nonebot_plugin_manager/handle.py b/plugins/nonebot_plugin_manager/handle.py deleted file mode 100644 index 1900ed2f..00000000 --- a/plugins/nonebot_plugin_manager/handle.py +++ /dev/null @@ -1,138 +0,0 @@ -from argparse import Namespace - -from .data import * - - -def handle_list( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - message = "" - - if args.store: - if is_superuser: - return get_store_pulgin_list() - else: - return "获取商店插件列表需要超级用户权限!" - - if args.default: - if is_superuser: - group_id = "default" - message += "默认" - else: - return "获取默认插件列表需要超级用户权限!" - - if args.group: - if is_superuser: - group_id = args.group - message += f"群{args.group}" - else: - return "获取指定群插件列表需要超级用户权限!" - - message += "插件列表如下:\n" - message += "\n".join( - f"[{'o' if get_group_plugin_list(group_id)[plugin] else 'x'}] {plugin}" - for plugin in get_group_plugin_list(group_id) - ) - return message - - -def handle_block( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - - if not is_admin and not is_superuser: - return "管理插件需要群管理员权限!" - - message = "" - - if args.default: - if is_superuser: - group_id = "default" - message += "默认" - else: - return "管理默认插件需要超级用户权限!" - - if args.group: - if is_superuser: - group_id = args.group - message += f"群{args.group}" - else: - return "管理指定群插件需要超级用户权限!" - - message += block_plugin(group_id, *args.plugins) - return message - - -def handle_unblock( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - message = "" - - if not is_admin and not is_superuser: - return "管理插件需要群管理员权限!" - - if args.default: - if is_superuser: - group_id = "default" - message += "默认" - else: - return "管理默认插件需要超级用户权限!" - - if args.group: - if is_superuser: - group_id = args.group - message += f"群{args.group}" - else: - return "管理指定群插件需要超级用户权限!" - - message += unblock_plugin(group_id, *args.plugins) - return message - - -def handle_info( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - - if not is_admin and not is_superuser: - return "管理插件需要超级权限!" - - return get_store_plugin_info(args.plugin) - - -def handle_install( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - pass - - -def handle_update( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - pass - - -def handle_uninstall( - args: Namespace, - group_id: str, - is_admin: bool, - is_superuser: bool, -) -> str: - pass diff --git a/plugins/nonebot_plugin_manager/parser.py b/plugins/nonebot_plugin_manager/parser.py deleted file mode 100644 index 71d2455d..00000000 --- a/plugins/nonebot_plugin_manager/parser.py +++ /dev/null @@ -1,60 +0,0 @@ -from nonebot.rule import ArgumentParser - -from .handle import * - -npm_parser = ArgumentParser("npm", add_help=False) -npm_parser.add_argument( - "-h", "--help", action="store_true", help="show this help message and exit" -) - -npm_subparsers = npm_parser.add_subparsers() - -list_parser = npm_subparsers.add_parser("list", help="show plugin list") -list_parser.add_argument( - "-s", "--store", action="store_true", help="show plugin store list" -) -list_parser.add_argument( - "-d", "--default", action="store_true", help="show default plugin list" -) -list_parser.add_argument("-g", "--group", action="store", help="show group plugin list") -list_parser.set_defaults(handle=handle_list) - -block_parser = npm_subparsers.add_parser("block", help="block plugin") -block_parser.add_argument("plugins", nargs="*", help="plugins you want to block") -block_parser.add_argument("-d", "--default", action="store_true", help="set default") -block_parser.add_argument("-a", "--all", action="store_true", help="select all plugin") -block_parser.add_argument("-g", "--group", action="store", help="set in group") -block_parser.set_defaults(handle=handle_block) - -unblock_parser = npm_subparsers.add_parser("unblock", help="unblock plugin") -unblock_parser.add_argument("plugins", nargs="*", help="plugins you want to unblock") -unblock_parser.add_argument("-d", "--default", action="store_true", help="set default") -unblock_parser.add_argument( - "-a", "--all", action="store_true", help="select all plugin" -) -unblock_parser.add_argument("-g", "--group", action="store", help="set in group") -unblock_parser.set_defaults(handle=handle_unblock) - -info_parser = npm_subparsers.add_parser("info", help="show plugin info") -info_parser.add_argument("plugin", help="plugins you want to know") -info_parser.set_defaults(handle=handle_info) - -install_parser = npm_subparsers.add_parser("install", help="install plugin") -install_parser.add_argument("plugins", nargs="*", help="plugins you want to install") -install_parser.add_argument("-i", "--index", action="store", help="point to a mirror") -install_parser.set_defaults(handle=handle_install) - -update_parser = npm_subparsers.add_parser("update", help="update plugin") -update_parser.add_argument("plugins", nargs="*", help="plugins you want to update") -update_parser.add_argument("-a", "--all", action="store_true", help="select all plugin") -update_parser.add_argument("-i", "--index", action="store", help="point to a mirror") -update_parser.set_defaults(handle=handle_update) - -uninstall_parser = npm_subparsers.add_parser("uninstall", help="uninstall plugin") -uninstall_parser.add_argument( - "plugins", nargs="*", help="plugins you want to uninstall" -) -uninstall_parser.add_argument( - "-a", "--all", action="store_true", help="select all plugin" -) -uninstall_parser.set_defaults(handle=handle_uninstall) diff --git a/plugins/one_friend/__init__.py b/plugins/one_friend/__init__.py index 2ae2763f..60a9217d 100644 --- a/plugins/one_friend/__init__.py +++ b/plugins/one_friend/__init__.py @@ -15,7 +15,7 @@ __plugin_name__ = "我有一个朋友" __plugin_usage__ = "用法:我有一个朋友说/问 [消息] [at](不艾特则群员随机)" one_friend = on_regex( - "^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我" "帮他问|让我帮忙问|让我帮忙问问|问).*", + "^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问).*", priority=5, block=True, ) @@ -41,10 +41,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): ) ] ) + user_name = '朋友' else: qq = qq[0] + at_user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) + user_name = at_user['card'] if at_user['card'] else at_user['nickname'] msg = re.search( - r"^我.*?朋友.*?" r"(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)", msg + r"^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)", msg ) msg = msg.group(2) if not msg: @@ -52,9 +55,8 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = msg.replace("他", "我").replace("她", "我").replace("它", "我") ava = CreateImg(100, 100, background=BytesIO(await get_pic(qq))) ava.circle() - text = CreateImg(60, 30, font_size=30) - text.text((0, 0), "朋友") - # text.show() + text = CreateImg(300, 30, font_size=30) + text.text((0, 0), user_name) A = CreateImg(700, 150, font_size=25, color="white") A.paste(ava, (30, 25), True) A.paste(text, (150, 40)) diff --git a/plugins/open_cases/__init__.py b/plugins/open_cases/__init__.py index d779667c..4a8a1ee7 100644 --- a/plugins/open_cases/__init__.py +++ b/plugins/open_cases/__init__.py @@ -15,6 +15,7 @@ from .open_cases_c import ( open_shilian_case, ) from .utils import util_get_buff_price, util_get_buff_img, update_count_daily +from configs.config import NICKNAME __plugin_name__ = "开箱" __plugin_usage__ = ( @@ -26,7 +27,7 @@ __plugin_usage__ = ( "3.命悬一线武器箱\n\t" "4.裂空武器箱\n\t" "5.光谱武器箱\n" - "示例:小真寻开箱 突围大行动(不输入指定武器箱则随机)\n" + f"示例:{NICKNAME}开箱 突围大行动(不输入指定武器箱则随机)\n" "示例:我的开箱(开箱统计)\n" "示例:群开箱统计\n" "示例:我的金色" diff --git a/plugins/parse_bilibili_json.py b/plugins/parse_bilibili_json.py index 2cee2340..ff02bfe5 100644 --- a/plugins/parse_bilibili_json.py +++ b/plugins/parse_bilibili_json.py @@ -2,17 +2,21 @@ from nonebot import on_message from services.log import logger from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.typing import T_State -from utils.utils import get_message_json, get_local_proxy -import json +from utils.utils import get_message_json, get_local_proxy, get_message_text from utils.user_agent import get_user_agent from nonebot.adapters.cqhttp.permission import GROUP from bilibili_api import video from utils.message_builder import image from models.group_remind import GroupRemind from nonebot.adapters.cqhttp.exception import ActionFailed +from utils.image_utils import CreateImg +from utils.browser import get_browser +from configs.path_config import IMAGE_PATH +import asyncio import time import aiohttp from bilibili_api import settings +import ujson as json if get_local_proxy(): settings.proxy = get_local_proxy() @@ -22,43 +26,110 @@ parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False) @parse_bilibili_json.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if await GroupRemind.get_status(event.group_id, "blpar") and get_message_json( - event.json() - ): - try: - data = json.loads(get_message_json(event.json())[0]["data"]) - except (IndexError, KeyError): - return - if data: - if data.get("desc") == "哔哩哔哩": + if await GroupRemind.get_status(event.group_id, "blpar"): + vd_info = None + if get_message_json(event.json()): + try: + data = json.loads(get_message_json(event.json())[0]["data"]) + except (IndexError, KeyError): + data = None + if data: + # 转发视频 + if data.get("desc") == "哔哩哔哩": + async with aiohttp.ClientSession( + headers=get_user_agent() + ) as session: + async with session.get( + data["meta"]["detail_1"]["qqdocurl"], + proxy=get_local_proxy(), + timeout=7, + ) as response: + url = str(response.url).split("?")[0] + bvid = url.split("/")[-1] + vd_info = await video.Video(bvid=bvid).get_info() + # 转发专栏 + if ( + data.get("meta") + and data["meta"].get("news") + and data["meta"]["news"].get("desc") == "哔哩哔哩专栏" + ): + url = data["meta"]["news"]["jumpUrl"] + page = None + try: + browser = await get_browser() + if not browser: + return + page = await browser.new_page( + user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + " (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36" + ) + await page.goto(url, wait_until="networkidle", timeout=10000) + await page.set_viewport_size({"width": 2560, "height": 1080}) + await page.click("#app > div") + # await page.click("text=继续阅读全文", timeout=3000) + div = await page.query_selector("#app > div") + await div.screenshot( + path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png", + timeout=100000, + ) + await asyncio.get_event_loop().run_in_executor( + None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png" + ) + await parse_bilibili_json.send( + image(f"cv_{event.user_id}.png", "temp") + ) + await page.close() + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}" + ) + except Exception as e: + logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}") + if page: + await page.close() + return + # BV + if get_message_text(event.json()): + msg = get_message_text(event.json()) + if "https://b23.tv" in msg: + url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13] async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get( - data["meta"]["detail_1"]["qqdocurl"], + url, proxy=get_local_proxy(), timeout=7, ) as response: url = str(response.url).split("?")[0] bvid = url.split("/")[-1] vd_info = await video.Video(bvid=bvid).get_info() - aid = vd_info["aid"] - title = vd_info["title"] - author = vd_info["owner"]["name"] - reply = vd_info["stat"]["reply"] # 回复 - favorite = vd_info["stat"]["favorite"] # 收藏 - coin = vd_info["stat"]["coin"] # 投币 - # like = vd_info['stat']['like'] # 点赞 - # danmu = vd_info['stat']['danmaku'] # 弹幕 - date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"])) - try: - await parse_bilibili_json.send( - image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n" - f"UP:{author}\n" - f"上传日期:{date}\n" - f"回复:{reply},收藏:{favorite},投币:{coin}\n" - f"{url}" - ) - except ActionFailed: - logger.warning(f"{event.group_id} 发送bilibili解析失败") - logger.info( - f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}" + if "https://www.bilibili.com/video" in msg: + url = msg.split("?")[0] + msg = url.split("/")[-1] + vd_info = await video.Video(bvid=msg).get_info() + if vd_info: + aid = vd_info["aid"] + title = vd_info["title"] + author = vd_info["owner"]["name"] + reply = vd_info["stat"]["reply"] # 回复 + favorite = vd_info["stat"]["favorite"] # 收藏 + coin = vd_info["stat"]["coin"] # 投币 + # like = vd_info['stat']['like'] # 点赞 + # danmu = vd_info['stat']['danmaku'] # 弹幕 + date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"])) + try: + await parse_bilibili_json.send( + image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n" + f"UP:{author}\n" + f"上传日期:{date}\n" + f"回复:{reply},收藏:{favorite},投币:{coin}\n" + f"{url}" ) + except ActionFailed: + logger.warning(f"{event.group_id} 发送bilibili解析失败") + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}" + ) + + +def resize(path: str): + A = CreateImg(0, 0, background=path, ratio=0.5) + A.save(path) diff --git a/plugins/pixiv/__init__.py b/plugins/pixiv/__init__.py index 22481b2b..7b55d199 100644 --- a/plugins/pixiv/__init__.py +++ b/plugins/pixiv/__init__.py @@ -81,7 +81,6 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): user_id=event.user_id, message=f"P站排行榜或搜图正在搜索噢,不要重复触发命令呀" ) raise IgnoredException("pixiv插件正在访问!") - _ulmt.set_True(event.user_id) @run_postprocessor @@ -106,6 +105,7 @@ pixiv_keyword = on_command("搜图", priority=5, block=True) @pixiv_rank.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()).strip() + _ulmt.set_True(event.user_id) msg = msg.split(" ") msg = [m for m in msg if m] if not msg: @@ -150,6 +150,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @pixiv_keyword.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()).strip() + _ulmt.set_True(event.user_id) if event.message_type == "group": if msg.find("r18") != -1: await pixiv_keyword.finish("(脸红#) 你不会害羞的 八嘎!", at_sender=True) diff --git a/plugins/reimu/__init__.py b/plugins/reimu/__init__.py index 95e74a9f..26b7b46b 100644 --- a/plugins/reimu/__init__.py +++ b/plugins/reimu/__init__.py @@ -2,11 +2,11 @@ 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, Event, PrivateMessageEvent +from nonebot.adapters.cqhttp import Bot, PrivateMessageEvent from nonebot.typing import T_State from utils.utils import is_number, get_message_text, UserExistLimiter, scheduler from models.count_user import UserCount -from configs.config import COUNT_PER_DAY_REIMU +from configs.config import COUNT_PER_DAY_REIMU, NICKNAME __plugin_name__ = "上车" __plugin_usage__ = r""" @@ -63,7 +63,7 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): keyword = state["keyword"] page = state["page"] await UserCount.add_count(event.user_id, "reimu") - await reimu.send("已经帮你关好车门了,请等待发车(不加真寻好友的话是欣赏不到旅途的风景的)", at_sender=True) + 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) diff --git a/plugins/remind/__init__.py b/plugins/remind/__init__.py index dc912641..02b763d8 100644 --- a/plugins/remind/__init__.py +++ b/plugins/remind/__init__.py @@ -5,6 +5,7 @@ from models.group_remind import GroupRemind from models.group_info import GroupInfo from models.friend_user import FriendUser from nonebot.adapters.cqhttp.exception import ActionFailed +from configs.config import NICKNAME __name__ = "早晚安 [Hidden]" @@ -56,7 +57,7 @@ async def _(): result = image("sleep.jpg", "zhenxun") try: await bot.send_group_msg( - group_id=g, message="小真寻要睡觉了,你们也要早点睡呀" + result + group_id=g, message=f"{NICKNAME}要睡觉了,你们也要早点睡呀" + result ) except ActionFailed: logger.warning(f"{g} 群被禁言中,无法发送晚安") diff --git a/plugins/roll.py b/plugins/roll.py index 9aee9ffb..14968aaa 100644 --- a/plugins/roll.py +++ b/plugins/roll.py @@ -3,6 +3,7 @@ from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from utils.utils import get_message_text from services.log import logger +from configs.config import NICKNAME import random import asyncio @@ -36,7 +37,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await roll.send( random.choice( [ - f"让真寻看看是什么结果!答案是:‘{x}’", + f"让{NICKNAME}看看是什么结果!答案是:‘{x}’", f"根据命运的指引,接下来{user_name} ‘{x}’ 会比较好", f"祈愿被回应了!是 ‘{x}’!", f"结束了,{user_name},命运之轮停在了 ‘{x}’!", diff --git a/plugins/russian/__init__.py b/plugins/russian/__init__.py index 5f24ae28..c9b68326 100644 --- a/plugins/russian/__init__.py +++ b/plugins/russian/__init__.py @@ -11,7 +11,7 @@ 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 +from configs.config import MAX_RUSSIAN_BET_GOLD, NICKNAME __plugin_name__ = '俄罗斯轮盘' @@ -248,7 +248,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if event.user_id != rs_player[event.group_id][1] and event.user_id != rs_player[event.group_id][2]: await shot.finish(random.choice([ f'不要打扰 {player1_name} 和 {player2_name} 的决斗啊!', - '给我好好做好一个观众!不然小真寻就要生气了', + f'给我好好做好一个观众!不然{NICKNAME}就要生气了', f'不要捣乱啊baka{(await GroupInfoUser.get_member_info(event.user_id, event.group_id)).user_name}!' ]), at_sender=True) await shot.finish(f'你的左轮不是连发的!该 ' @@ -322,7 +322,7 @@ async def end_game(bot: Bot, event: GroupMessageEvent): f'\t累计败场:{lose_user.fail_count}\n' f'\t累计输掉金币:{lose_user.lose_money}\n' f'-------------------\n' - f'哼哼,真寻从中收取了 {float(rand)}%({fee}金币) 作为手续费!\n' + f'哼哼,{NICKNAME}从中收取了 {float(rand)}%({fee}金币) 作为手续费!\n' f'子弹排列:{bullet_str[:-1]}') rs_player[event.group_id] = {} diff --git a/plugins/search_anime/data_source.py b/plugins/search_anime/data_source.py index 6526387e..d4498ac5 100644 --- a/plugins/search_anime/data_source.py +++ b/plugins/search_anime/data_source.py @@ -1,4 +1,5 @@ from lxml import etree +from lxml import etree import feedparser from urllib import parse from services.log import logger diff --git a/plugins/search_buff_skin_price/__init__.py b/plugins/search_buff_skin_price/__init__.py index 485e1afc..b5d0890c 100644 --- a/plugins/search_buff_skin_price/__init__.py +++ b/plugins/search_buff_skin_price/__init__.py @@ -6,6 +6,7 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.rule import to_me from nonebot.permission import SUPERUSER from utils.utils import UserExistLimiter, get_message_text +from configs.config import NICKNAME __plugin_name__ = "查询皮肤" @@ -50,7 +51,7 @@ async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): try: result, status_code = await get_price(name) except FileNotFoundError: - await search_skin.finish('请先对真寻说"设置cookie"来设置cookie!') + 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) diff --git a/plugins/send_img/__init__.py b/plugins/send_img/__init__.py index 66db756f..56fc54eb 100644 --- a/plugins/send_img/__init__.py +++ b/plugins/send_img/__init__.py @@ -27,7 +27,6 @@ __plugin_usage__ = ( _flmt = FreqLimiter(1) -IMAGE_DIR_LIST.remove("色图") cmd = set(IMAGE_DIR_LIST) # print(cmd) diff --git a/plugins/send_setu/__init__.py b/plugins/send_setu/__init__.py index 367184bd..0879efea 100644 --- a/plugins/send_setu/__init__.py +++ b/plugins/send_setu/__init__.py @@ -34,7 +34,7 @@ from .data_source import ( add_data_to_database, ) from nonebot.adapters.cqhttp.exception import ActionFailed -from configs.config import ONLY_USE_LOCAL_SETU, WITHDRAW_SETU_TIME +from configs.config import ONLY_USE_LOCAL_SETU, WITHDRAW_SETU_TIME, NICKNAME from utils.message_builder import at import re import asyncio @@ -60,7 +60,10 @@ __plugin_usage__ = f"""示例: 如果图片数量与数字不符, 原因1:网络不好,网线被拔QAQ 原因2:搜索到的总数小于数字 - 原因3:图太色或者小错误了】""" + 原因3:图太色或者小错误了】 +示例: + 色图 萝莉|少女 白丝|黑丝 + 色图 萝莉 猫娘""" _flmt = FreqLimiter(5) _ulmt = UserExistLimiter() @@ -83,7 +86,6 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): user_id=event.user_id, message=f"您有色图正在处理,请稍等....." ) raise IgnoredException("色图正在处理!") - _ulmt.set_True(event.user_id) @run_postprocessor @@ -118,7 +120,7 @@ setu = on_command( "色图", aliases={"涩图", "不够色", "来一发", "再来点", "色图r"}, priority=5, block=True ) -setu_reg = on_regex("(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图", priority=5, block=True) +setu_reg = on_regex("(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图$", priority=5, block=True) find_setu = on_command("查色图", priority=5, block=True) @@ -134,6 +136,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): luox = get_luoxiang(impression) if luox: await setu.finish(luox) + _ulmt.set_True(event.user_id) if not _flmt.check(event.user_id): await setu.finish("您冲得太快了,请稍候再冲", at_sender=True) _flmt.start_cd(event.user_id) @@ -253,7 +256,7 @@ async def send_setu_handle( # 非 id,在线搜索 tags = msg.split() # 真寻的色图?怎么可能 - if "真寻" in tags: + if f"{NICKNAME}" in tags: await matcher.finish("咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀") # 本地先拿图,下载失败补上去 setu_list, code = await get_setu_list(tags=msg.split(), r18=r18) @@ -301,6 +304,8 @@ async def send_setu_handle( return # 本地无图 或 超过上下限 if code != 200 or (not setu_list and ONLY_USE_LOCAL_SETU): + if code == 999: + await matcher.finish('网络连接失败...', at_sender=True) await matcher.finish(setu_list[0], at_sender=True) elif not setu_list: await matcher.finish(error_info, at_sender=True) diff --git a/plugins/send_setu/data_source.py b/plugins/send_setu/data_source.py index 1463f5ca..c44c5fb0 100644 --- a/plugins/send_setu/data_source.py +++ b/plugins/send_setu/data_source.py @@ -7,6 +7,7 @@ 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 from models.setu import Setu import aiohttp import aiofiles @@ -29,12 +30,12 @@ r18_path = "_r18/" 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[:20] if len(tags) > 20 else tags + tags = tags[:3] if len(tags) > 3 else tags params = { "r18": r18, # 添加r18参数 0为否,1为是,2为混合 - "tag": "|".join(tags), # 若指定tag - "num": 100, # 一次返回的结果数量,范围为1到10,不提供 APIKEY 时固定为1 - "size": ["original"], # 是否使用 master_1200 缩略图,以节省流量或提升加载速度 + "tag": tags, # 若指定tag + "num": 100, # 一次返回的结果数量 + "size": ["original"], } async with aiohttp.ClientSession() as session: for count in range(3): @@ -105,8 +106,7 @@ async def search_online_setu( > 1024 * 1024 * 1.5 ): compressed_image( - os.path.join(path_, f"{index}.jpg"), - os.path.join(path_, f"{index}.jpg"), + f"{IMAGE_PATH}/{path_}/{index}.jpg", ) logger.info(f"下载 lolicon图片 {url_} 成功, id:{index}") return image(file, path_), index @@ -188,12 +188,12 @@ def gen_message(setu_image: Setu, img_msg: bool = False): # 罗翔老师! def get_luoxiang(impression): - probability = impression + 70 + probability = impression + INITIAL_SETU_PROBABILITY * 100 if probability < random.randint(1, 101): return ( "我为什么要给你发这个?" + image(random.choice(os.listdir(IMAGE_PATH + "luoxiang/")), "luoxiang") - + "\n(快向小真寻签到提升好感度吧!)" + + f"\n(快向{NICKNAME}签到提升好感度吧!)" ) return None diff --git a/plugins/shop/gold_redbag/__init__.py b/plugins/shop/gold_redbag/__init__.py index 4a6e1c78..2b78f3a3 100644 --- a/plugins/shop/gold_redbag/__init__.py +++ b/plugins/shop/gold_redbag/__init__.py @@ -22,6 +22,7 @@ from services.log import logger from nonebot.permission import SUPERUSER from nonebot.rule import to_me from datetime import datetime, timedelta +from configs.config import NICKNAME import random import time @@ -278,7 +279,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): gl = await bot.get_group_list(self_id=int(bot.self_id)) gl = [g["group_id"] for g in gl] for g in gl: - init_redbag(int(bot.self_id), g, "可爱的小真寻", amount, num, int(bot.self_id), 1) + init_redbag(int(bot.self_id), g, f"{NICKNAME}", amount, num, int(bot.self_id), 1) scheduler.add_job( end_festive_redbag, "date", @@ -289,7 +290,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): try: await bot.send_group_msg( group_id=g, - message=f"可爱的小真寻发起了金币红包\n金额:{amount}\n数量:{num}\n" + message=f"{NICKNAME}发起了金币红包\n金额:{amount}\n数量:{num}\n" + image( b64=await generate_send_redbag_pic(int(bot.self_id), greetings) ), @@ -380,7 +381,7 @@ async def get_redbag_img(user_id: int, group_id: int): async def end_festive_redbag(bot: Bot, group_id: int): global festive_redbag_data message = ( - f"真寻的节日红包过时了,一共开启了 " + f"{NICKNAME}的节日红包过时了,一共开启了 " f"{festive_redbag_data[group_id]['num'] - len(festive_redbag_data[group_id]['redbag'])}" f" 个红包,共 {festive_redbag_data[group_id]['open_amount']} 金币" ) diff --git a/plugins/sign_in/group_user_checkin.py b/plugins/sign_in/group_user_checkin.py index 90f2a554..4fa4f75b 100644 --- a/plugins/sign_in/group_user_checkin.py +++ b/plugins/sign_in/group_user_checkin.py @@ -6,7 +6,7 @@ from services.db_context import db from models.sigin_group_user import SignGroupUser from models.group_member_info import GroupInfoUser from models.bag_user import BagUser -from configs.config import MAX_SIGN_GOLD +from configs.config import MAX_SIGN_GOLD, NICKNAME from utils.image_utils import CreateImg import aiohttp from asyncio.exceptions import TimeoutError @@ -219,6 +219,6 @@ async def _pst(users: list, impressions: list, groups: list): width += 580 W = CreateImg(1740, 3700, color="#FFE4C4", font_size=130) W.paste(A, (0, 260)) - font_w, font_h = W.getsize("真寻的好感度总榜") - W.text((int((1740 - font_w) / 2), int((260 - font_h) / 2)), "真寻的好感度总榜") + font_w, font_h = W.getsize(f"{NICKNAME}的好感度总榜") + W.text((int((1740 - font_w) / 2), int((260 - font_h) / 2)), f"{NICKNAME}的好感度总榜") return W.pic2bs4() diff --git a/plugins/statistics_hook.py b/plugins/statistics_hook.py index 6eb2be98..fa274eb7 100644 --- a/plugins/statistics_hook.py +++ b/plugins/statistics_hook.py @@ -86,7 +86,7 @@ async def _( state: T_State, ): global _prefix_count_dict - if matcher.type == "message" and matcher.priority not in [1, 9]: + if matcher.type == "message" and matcher.priority not in [1, 9] and matcher.module not in ['update_info']: model = matcher.module day_index = _prefix_count_dict["day_index"] # print(f'model --> {model}') diff --git a/plugins/super_help/__init__.py b/plugins/super_help/__init__.py index f41b1fc5..992ba559 100644 --- a/plugins/super_help/__init__.py +++ b/plugins/super_help/__init__.py @@ -3,24 +3,21 @@ from nonebot.permission import SUPERUSER from nonebot.typing import T_State from nonebot.adapters import Bot, Event from nonebot.rule import to_me +from utils.image_utils import CreateImg +from configs.path_config import IMAGE_PATH +from utils.message_builder import image -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): - result = """超级用户帮助: +result = """超级用户帮助: +*:可多个类型参数 ?:可省略参数 1.添加/删除管理 [at] [level] 2.查看群组/查看好友 - 3.广播 --> 指令:广播- [msg] + 3.广播- [msg] 4.更新色图 - 5.回复 --> 指令:/t命令帮助 - 6.更新/设置cookie --> 指令:更新/设置cookie [cookie] - 7.开启广播通知 --> 指令:开启广播通知 [群号] - 8.退群 --> 指令:退群 [群号] + 5./t命令帮助 + 6.更新/设置cookie [cookie] + 7.开启/关闭广播通知 [群号] + 8.退群 [群号] 9.自检 10.更新价格/更加图片 [武器箱] 11.更新好友信息 @@ -29,16 +26,31 @@ async def _(bot: Bot, event: Event, state: T_State): 14.添加商品 [名称]-[价格]-[描述]-[折扣]-[限时时间] 15.删除商品 [名称(序号)] 16.修改商品 -name [名称(序号)] -price [价格] -des [描述] -discount [折扣] -time [限时] - 17.节日红包 [金额] [数量] [祝福语](可省) *[指定群](可省) + 17.节日红包 [金额] [数量] [祝福语](可省) *?[指定群 18.更新原神今日素材 19.更新原神资源信息 - 20.添加pix关键词/uid/pid *[关键词/uid/pid] [-f](强制通过不检测) + 20.添加pix关键词/uid/pid *[关键词/uid/pid] ?[-f](强制通过不检测) 21.通过/取消pix关键词 [keyword/pid:pid/uid:uid] 22.删除pix关键词 [keyword/uid/pid:pid] 23.更新pix关键词 [keyword/pid:pid/uid:uid] [num] - 24.删除pix图片 *[pid] [-b](同时加入黑名单)? + 24.删除pix图片 *[pid] ?[-b](同时加入黑名单) 25.查看pix图库 [keyword] 26.pix检测更新 [update] 27.检查更新真寻 - 28.真寻重启""" - await super_help.finish(result, at_sender=True) + 28.真寻重启 + 29.添加/删除群白名单 *[群号] + 30.关闭[功能] ?[群号](有群号时禁用指定群)""" + +height = len(result.split('\n')) * 24 +A = CreateImg(1000, height, font_size=20) +A.text((10, 10), result) +A.save(f'{IMAGE_PATH}/super_help.png') + +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): + await super_help.finish(image('super_help.png'), at_sender=True) diff --git a/plugins/update_info.py b/plugins/update_info.py index abfe89fc..f31b37d1 100644 --- a/plugins/update_info.py +++ b/plugins/update_info.py @@ -8,7 +8,7 @@ __plugin_name__ = '更新信息' __plugin_usage__ = '无' -update_info = on_command("更新信息", priority=5, block=True) +update_info = on_command("更新信息", aliases={'更新日志'}, priority=5, block=True) @update_info.handle() diff --git a/plugins/update_pic.py b/plugins/update_pic.py index bfa14ecc..b6c92b21 100644 --- a/plugins/update_pic.py +++ b/plugins/update_pic.py @@ -11,33 +11,34 @@ import aiofiles import aiohttp from utils.utils import is_number, get_message_text from utils.image_utils import CreateImg, pic2b64 +from configs.config import NICKNAME import cv2 import numpy as np __plugin_name__ = "图片" -__plugin_usage__ = """图片用法(目前): +__plugin_usage__ = f"""图片用法(目前): 可以输入 指定操作 或 序号 来进行选择 1.修改尺寸 宽(x) 高(y) 图片(在文字后跟图片即可) - 示例:小真寻修改图片 修改尺寸 200 300 图片(在文字后跟图片即可) + 示例:{NICKNAME}修改图片 修改尺寸 200 300 图片(在文字后跟图片即可) 2.等比压缩 比例(x) 图片(在文字后跟图片即可) - 示例:小真寻修改图片 等比压缩 2 图片(在文字后跟图片即可) + 示例:{NICKNAME}修改图片 等比压缩 2 图片(在文字后跟图片即可) 3.旋转图片 角度(x) 图片(在文字后跟图片即可) - 示例:小真寻修改图片 旋转图片 45 图片(在文字后跟图片即可) + 示例:{NICKNAME}修改图片 旋转图片 45 图片(在文字后跟图片即可) 4.水平翻转 - 示例:小真寻修改图片 水平翻转 图片 + 示例:{NICKNAME}修改图片 水平翻转 图片 5.铅笔滤镜 - 示例:小真寻修改图片 铅笔滤镜 图片 + 示例:{NICKNAME}修改图片 铅笔滤镜 图片 6.模糊效果 - 示例:小真寻修改图片 模糊效果 图片 + 示例:{NICKNAME}修改图片 模糊效果 图片 7.锐化效果 - 示例:小真寻修改图片 锐化效果 图片 + 示例:{NICKNAME}修改图片 锐化效果 图片 8.高斯模糊 - 示例:小真寻修改图片 高斯模糊 图片 + 示例:{NICKNAME}修改图片 高斯模糊 图片 9.边缘检测 - 示例:小真寻修改图片 边缘检测 图片 + 示例:{NICKNAME}修改图片 边缘检测 图片 10.底色替换 颜色 颜色 图片 (不专业)支持:(红,蓝)->(红,蓝,白,绿,黄) - 示例:小真寻修改图片 底色替换 蓝色 红色 图片""" + 示例:{NICKNAME}修改图片 底色替换 蓝色 红色 图片""" # IMAGE_LOCAL = IMAGE_PATH + "temp/{}_update.png" method_flag = "" @@ -219,7 +220,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): img = img.resize((int(x * width), int(x * height))) result += image(b64=pic2b64(img)) else: - await update_img.finish("小真寻不支持图片压缩后宽或高大于8000的存在!!") + await update_img.finish(f"{NICKNAME}不支持图片压缩后宽或高大于8000的存在!!") if method in ["旋转图片", "3"]: for i in range(index): img = Image.open(IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png") diff --git a/plugins/update_setu/data_source.py b/plugins/update_setu/data_source.py index 4cceb2a1..fcc776a8 100644 --- a/plugins/update_setu/data_source.py +++ b/plugins/update_setu/data_source.py @@ -93,8 +93,10 @@ async def update_setu_img(): for image in image_list: count += 1 path = _path / "_r18" if image.is_r18 else _path / "_setu" - rar_path = "r18_rar" if image.is_r18 else "rar" + 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: @@ -103,7 +105,7 @@ async def update_setu_img(): ) as response: if response.status == 200: async with aiofiles.open( - f"{IMAGE_PATH}/{rar_path}/{image.local_id}.jpg", + rar_path / f'{image.local_id}.jpg', "wb", ) as f: await f.write(await response.read()) @@ -111,28 +113,25 @@ async def update_setu_img(): try: if ( os.path.getsize( - f"{IMAGE_PATH}/{rar_path}/{image.local_id}.jpg" + rar_path / f'{image.local_id}.jpg', ) > 1024 * 1024 * 1.5 ): compressed_image( - os.path.join( - rar_path, f"{image.local_id}.jpg" - ), - os.path.join(path, f"{image.local_id}.jpg"), + rar_path / f"{image.local_id}.jpg", + path / f"{image.local_id}.jpg" ) else: logger.info( - f"不需要压缩,移动图片 {IMAGE_PATH}/{rar_path}/{image.local_id}.jpg " + f"不需要压缩,移动图片{rar_path}/{image.local_id}.jpg " f"--> /{path}/{image.local_id}.jpg" ) os.rename( - f"{IMAGE_PATH}/{rar_path}/{image.local_id}.jpg", + f"{rar_path}/{image.local_id}.jpg", f"{path}/{image.local_id}.jpg", ) except FileNotFoundError: logger.warning(f"文件 {image.local_id}.jpg 不存在,跳过...") - _success -= 1 continue img_hash = str( get_img_hash( @@ -146,7 +145,6 @@ async def update_setu_img(): except (TimeoutError, ClientConnectorError) as e: logger.warning(f"{image.local_id}.jpg 更新失败 ..{type(e)}:{e}") except Exception as e: - _success -= 1 logger.error(f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}") if type(e) not in error_type: error_type.append(type(e)) diff --git a/plugins/upload_img/__init__.py b/plugins/upload_img/__init__.py index 82ca5741..107051f9 100644 --- a/plugins/upload_img/__init__.py +++ b/plugins/upload_img/__init__.py @@ -1,15 +1,9 @@ from nonebot import on_command -from configs.path_config import IMAGE_PATH -from services.log import logger -import os from nonebot.rule import to_me from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from utils.utils import get_message_imgs, get_message_text -import aiohttp -import aiofiles -from utils.utils import cn2py -from configs.config import IMAGE_DIR_LIST +from .data_source import upload_image_to_local __plugin_name__ = "上传图片" __plugin_usage__ = ( @@ -18,22 +12,24 @@ __plugin_usage__ = ( "2.上传图片 [序号] [图片], 即在相应目录下添加图片\n\t\t示例: 上传图片 1 [图片]" ) - upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True) +continuous_upload_img = on_command('连续上传图片', rule=to_me(), priority=5, block=True) + @upload_img.args_parser -async def parse(bot: Bot, event: MessageEvent, state: T_State): - if str(event.get_message()) in ["取消", "算了"]: +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if msg in ["取消", "算了"]: await upload_img.finish("已取消操作..", at_sender=True) if state["_current_key"] in ["path"]: - if str(event.get_message()) not in IMAGE_DIR_LIST: + if msg not in IMAGE_DIR_LIST: await upload_img.reject("此目录不正确,请重新输入目录!") - state[state["_current_key"]] = str(event.get_message()) + state['path'] = msg if state["_current_key"] in ["imgs"]: if not get_message_imgs(event.json()): await upload_img.reject("图呢图呢图呢图呢!GKD!") - state[state["_current_key"]] = get_message_imgs(event.json()) + state['imgs'] = get_message_imgs(event.json()) @upload_img.handle() @@ -41,59 +37,65 @@ 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 str(event.get_message()) in ["帮助"]: - await upload_img.finish(__plugin_usage__) - if raw_arg.split("[")[0] in IMAGE_DIR_LIST: - state["path"] = raw_arg.split("[")[0] + if raw_arg in IMAGE_DIR_LIST: + state["path"] = raw_arg if img_list: state["imgs"] = img_list -@upload_img.got("path", prompt="要将图片上传至什么图库呢?") +@upload_img.got('path', prompt='要将图片上传至什么图库呢?') +async def _(bot: Bot, event: MessageEvent, state: T_State): + pass + + @upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") async def _(bot: Bot, event: MessageEvent, state: T_State): - path = IMAGE_PATH + cn2py(state["path"]) + '/' - img_list = state["imgs"] - img_id = len(os.listdir(path)) - failed_list = [] - success_id = "" - async with aiohttp.ClientSession() as session: - for img_url in img_list: - try: - async with session.get(img_url, timeout=7) as response: - if response.status == 200: - async with aiofiles.open( - path + str(img_id) + ".jpg", "wb" - ) as f: - await f.write(await response.read()) - success_id += str(img_id) + "," - img_id += 1 - else: - failed_list.append(img_url) - logger.warning(f"图片:{img_url} 下载失败....") - except TimeoutError as e: - logger.warning(f"图片:{img_url} 下载超时....e:{e}") - if img_url not in failed_list: - failed_list.append(img_url) - failed_result = "" - for img in failed_list: - failed_result += str(img) + "\n" - logger.info( - f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" - f" 上传图片至 {state['path']} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}" - ) - if failed_result: - await upload_img.finish( - f"这次一共为 {state['path']}库 添加了 {len(img_list) - len(failed_list)} 张图片\n" - f"依次的Id为:{success_id[:-1]}\n" - f"上传失败:{failed_result[:-1]}\n" - f"小真寻感谢您对图库的扩充!WW", - at_sender=True, - ) + path = state['path'] + img_list = state['imgs'] + group_id = 0 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + await upload_img.send(await upload_image_to_local(img_list, path, event.user_id, group_id)) + + +@continuous_upload_img.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["取消", "算了"]: + await continuous_upload_img.finish("已取消操作..", at_sender=True) + if state["_current_key"] in ["path"]: + if str(event.get_message()) not in IMAGE_DIR_LIST: + await continuous_upload_img.reject("此目录不正确,请重新输入目录!") + state[state["_current_key"]] = str(event.get_message()) else: - await upload_img.finish( - f"这次一共为 {state['path']}库 添加了 {len(img_list)} 张图片\n" - f"依次的Id为:{success_id[:-1]}\n" - f"小真寻感谢您对图库的扩充!WW", - at_sender=True, - ) + if get_message_text(event.json()) not in ['stop']: + img = get_message_imgs(event.json()) + if img: + state['tmp'].extend(img) + await continuous_upload_img.reject('图再来!!') + else: + state['imgs'] = state['tmp'] + + +@continuous_upload_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + path = get_message_imgs(event.json()) + if path in IMAGE_DIR_LIST: + state['path'] = path + await continuous_upload_img.send('图来!!') + state['tmp'] = [] + + +@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + pass + + +@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") +async def _(bot: Bot, event: MessageEvent, state: T_State): + path = state['path'] + img_list = state['imgs'] + group_id = 0 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + await continuous_upload_img.send(await upload_image_to_local(img_list, path, event.user_id, group_id)) + diff --git a/plugins/upload_img/data_source.py b/plugins/upload_img/data_source.py new file mode 100644 index 00000000..7123d04b --- /dev/null +++ b/plugins/upload_img/data_source.py @@ -0,0 +1,51 @@ +from configs.config import NICKNAME +from typing import List +from configs.path_config import IMAGE_PATH +from services.log import logger +from utils.utils import cn2py +from pathlib import Path +import aiofiles +import aiohttp +import os + + +async def upload_image_to_local( + img_list: List[str], path: str, user_id: int, group_id: int = 0 +) -> str: + _path = path + path = Path(IMAGE_PATH) / cn2py(path) + path.mkdir(parents=True, exist_ok=True) + img_id = len(os.listdir(path)) + failed_list = [] + success_id = "" + async with aiohttp.ClientSession() as session: + for img_url in img_list: + try: + async with session.get(img_url, timeout=7) as response: + if response.status == 200: + async with aiofiles.open(path / f"{img_id}.jpg", "wb") as f: + await f.write(await response.read()) + success_id += str(img_id) + "," + img_id += 1 + else: + failed_list.append(img_url) + logger.warning(f"图片:{img_url} 下载失败....") + except TimeoutError as e: + logger.warning(f"图片:{img_url} 下载超时....e:{e}") + if img_url not in failed_list: + failed_list.append(img_url) + failed_result = "" + for img in failed_list: + failed_result += str(img) + "\n" + logger.info( + f"USER {user_id} GROUP {group_id}" + f" 上传图片至 {_path} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}" + ) + if failed_list: + return ( + f"这次一共为 {_path}库 添加了 {len(img_list) - len(failed_list)} 张图片\n" + f"依次的Id为:{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" + ) + else: + return f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为:" \ + f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW" diff --git a/plugins/weather/__init__.py b/plugins/weather/__init__.py index 0fecb438..f48c891b 100644 --- a/plugins/weather/__init__.py +++ b/plugins/weather/__init__.py @@ -17,8 +17,11 @@ weather = on_regex(r".*?(.*)市?的?天气.*?", priority=5, block=True) @weather.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) - msg = re.search(r".*?(.*)市?的?天气.*?", msg) - msg = msg.group(1) + msg1 = re.search(r".*?(.*)市?的?天气.*?", msg) + msg2 = re.search(r".*?天气(.*).*?", msg) + msg1 = msg1.group(1) + msg2 = msg2.group(1) + msg = msg1 if msg1 else msg2 if msg[-1] == "的": msg = msg[:-1] if msg[-1] != "市": diff --git a/plugins/weather/data_source.py b/plugins/weather/data_source.py index c8f6c7f0..bb7806d4 100644 --- a/plugins/weather/data_source.py +++ b/plugins/weather/data_source.py @@ -2,6 +2,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 TXT_PATH +from configs.config import NICKNAME from asyncio.exceptions import TimeoutError from typing import List from nonebot import Driver @@ -26,7 +27,7 @@ async def get_weather_of_city(city: str) -> str: if code == 999: return "不要查一个省份的天气啊,很累人的!" elif code == 998: - return "真寻只可以查询国内的天气喔..." + return f"{NICKNAME}只可以查询国内城市的天气喔..." else: async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get( @@ -35,7 +36,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 "你为啥不查火星的天气呢?小真寻只支持国内天气查询!!" + image( + return f"你为啥不查火星的天气呢?{NICKNAME}只支持国内天气查询!!" + image( "shengqi", "zhenxun" ) elif data_json["desc"] == "OK": diff --git a/plugins/yiqing/data_source.py b/plugins/yiqing/data_source.py index 5248de41..c3aed80a 100644 --- a/plugins/yiqing/data_source.py +++ b/plugins/yiqing/data_source.py @@ -1,5 +1,6 @@ from utils.user_agent import get_user_agent from configs.path_config import TXT_PATH +from configs.config import NICKNAME from typing import List from pathlib import Path import ujson as json @@ -37,7 +38,7 @@ async def get_yiqing_data(area: str): province = p city = area if not province and not city: - return "小真寻只支持国内的疫情查询喔..." + return f"{NICKNAME}只支持国内的疫情查询喔..." async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(url, timeout=7) as response: epidemic_data = json.loads((await response.json())["data"]) diff --git a/resources/img/other/webtop.png b/resources/img/other/webtop.png new file mode 100644 index 0000000000000000000000000000000000000000..6f2eb1efa0acd84903d878d81e3c677fedb49a11 GIT binary patch literal 170709 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 Optional[Browser]: - try: - global _browser - browser = await async_playwright().start() - _browser = await browser.chromium.launch(**kwargs) - return _browser - except NotImplementedError: - logger.warning("win环境下 初始化playwright失败....请替换环境至linux") - return None + # try: + global _browser + browser = await async_playwright().start() + _browser = await browser.chromium.launch(**kwargs) + return _browser + # except NotImplementedError: + # logger.warning("win环境下 初始化playwright失败....请替换环境至linux") + # return None async def get_browser(**kwargs) -> Browser: diff --git a/utils/image_utils.py b/utils/image_utils.py index fc16df56..7b8fa547 100644 --- a/utils/image_utils.py +++ b/utils/image_utils.py @@ -3,7 +3,7 @@ from PIL import Image, ImageFile, ImageDraw, ImageFont from imagehash import ImageHash from io import BytesIO from matplotlib import pyplot as plt -from typing import Tuple, Optional +from typing import Tuple, Optional, Union from pathlib import Path import cv2 import base64 @@ -45,7 +45,7 @@ def get_img_hash(image_file: str) -> ImageHash: return hash_value -def compressed_image(in_file: str, out_file: str = None, ratio: float = 0.9): +def compressed_image(in_file: Union[str, Path], out_file: Union[str, Path] = None, ratio: float = 0.9): """ 说明: 压缩图片 @@ -54,8 +54,11 @@ def compressed_image(in_file: str, out_file: str = None, ratio: float = 0.9): :param out_file: 压缩后输出的文件路径 :param ratio: 压缩率,宽高 * 压缩率 """ - in_file = Path(IMAGE_PATH) / in_file - out_file = Path(IMAGE_PATH) / out_file if out_file else in_file + in_file = Path(IMAGE_PATH) / in_file if isinstance(in_file, str) else in_file + if out_file: + out_file = Path(IMAGE_PATH) / out_file if isinstance(out_file, str) else out_file + else: + out_file = in_file h, w, d = cv2.imread(str(in_file.absolute())).shape img = cv2.resize(cv2.imread(str(in_file.absolute())), (int(w * ratio), int(h * ratio))) cv2.imwrite(str(out_file.absolute()), img) diff --git a/utils/static_data/__init__.py b/utils/static_data/__init__.py new file mode 100644 index 00000000..7563e9c0 --- /dev/null +++ b/utils/static_data/__init__.py @@ -0,0 +1,11 @@ +from typing import Optional +from .group_manager import GroupManager +from pathlib import Path +from .data_source import init + +# 群权限 +group_manager: Optional[GroupManager] = GroupManager( + Path() / "data" / "manager" / "group_manager.json" +) + +init(group_manager) diff --git a/utils/static_data/data_class.py b/utils/static_data/data_class.py new file mode 100644 index 00000000..cca80a3b --- /dev/null +++ b/utils/static_data/data_class.py @@ -0,0 +1,51 @@ +from typing import Union +from pathlib import Path +import ujson as json + + +class StaticData: + """ + 静态数据共享类 + """ + + def __init__(self, file: Path): + file.parent.mkdir(exist_ok=True, parents=True) + self.file = file + self.data = {} + if file.exists(): + self.data: dict = json.load(open(file, "r", encoding="utf8")) + + def set(self, key, value): + self.data[key] = value + + def get(self, key): + return self.data.get(key) + + def delete(self, key): + if self.data.get(key) is not None: + del self.data[key] + + 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) + + def reload(self): + if self.file.exists(): + self.data: dict = json.load(open(self.file, "r", encoding="utf8")) + + def is_exists(self): + return self.file.exists() + + def is_empty(self): + return bool(len(self.data)) + + def __str__(self): + return str(self.data) + + def __setitem__(self, key, value): + self.data[key] = value + + def __getitem__(self, key): + return self.data[key] + diff --git a/utils/static_data/data_source.py b/utils/static_data/data_source.py new file mode 100644 index 00000000..75ce71cc --- /dev/null +++ b/utils/static_data/data_source.py @@ -0,0 +1,27 @@ +from pathlib import Path +import ujson as json +from .group_manager import GroupManager + + +def init(group_manager: GroupManager): + 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() + group_manager.save() diff --git a/utils/static_data/group_manager.py b/utils/static_data/group_manager.py new file mode 100644 index 00000000..d34bfc74 --- /dev/null +++ b/utils/static_data/group_manager.py @@ -0,0 +1,186 @@ +from typing import Optional +from pathlib import Path +from .data_class import StaticData + + +class GroupManager(StaticData): + """ + 群权限 | 功能 | 聊天时间 管理器 + """ + + def __init__(self, file: Path): + super().__init__(file) + if not self.data: + self.data = {"super": {"close_plugins": {}, "white_group_list": []}, "group_manager": {}} + + def block_plugin( + self, plugin_cmd: str, group_id: Optional[str] = None, block_type: str = "all" + ): + """ + 说明: + 锁定插件 + 参数: + :param plugin_cmd: 功能模块名 + :param group_id: 群组,None时为超级用户禁用 + :param block_type: 限制类型 + """ + self._set_plugin_status(plugin_cmd, "block", group_id, block_type) + + def unblock_plugin(self, plugin_cmd: str, group_id: Optional[str] = None): + """ + 说明: + 解锁插件 + 参数: + :param plugin_cmd: 功能模块名 + :param group_id: 群组 + """ + self._set_plugin_status(plugin_cmd, "unblock", group_id) + + def set_group_level(self, group_id: str, level: int): + """ + 说明: + 设置群权限 + 参数: + :param group_id: 群组 + :param level: 权限等级 + """ + if not self.data["group_manager"].get(group_id): + self._init_group(group_id) + self.data["group_manager"][group_id]["level"] = level + self.save() + + def get_plugin_status( + self, plugin_cmd: str, group_id: Optional[str] = None, block_type: str = "all" + ) -> bool: + """ + 说明: + 获取插件状态 + 参数: + :param plugin_cmd: 功能模块名 + :param group_id: 群组 + :param block_type: 限制类型 + """ + 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 + ) + 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 "" + + def get_group_level(self, group_id: str) -> int: + """ + 说明: + 获取群等级 + 参数: + :param group_id: 群号 + """ + if not self.data["group_manager"].get(group_id): + self._init_group(group_id) + return self.data["group_manager"][group_id]["level"] + + def check_group_is_white(self, group_id: int) -> bool: + """ + 说明: + 检测群聊是否在白名单 + 参数: + :param group_id: 群号 + """ + return group_id in self.data['super']['white_group_list'] + + def add_group_white_list(self, group_id: int): + """ + 说明: + 将群聊加入白名单 + 参数: + :param group_id: 群号 + """ + if group_id not in self.data['super']['white_group_list']: + self.data['super']['white_group_list'].append(group_id) + + def delete_group_white_list(self, group_id: int): + """ + 说明: + 将群聊从白名单中删除 + 参数: + :param group_id: 群号 + """ + if group_id in self.data['super']['white_group_list']: + self.data['super']['white_group_list'].remove(group_id) + + def _set_plugin_status( + self, + plugin_cmd: str, + status: str, + group_id: Optional[str], + block_type: str = "all", + ): + """ + 说明: + 设置功能开关状态 + 参数: + :param plugin_cmd: 功能模块名 + :param status: 功能状态 + :param group_id: 群组 + :param block_type: 限制类型 + """ + 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] + self.save() + + def _init_group(self, group_id: str): + """ + 说明: + 初始化群数据 + 参数: + :param group_id: 群号 + """ + self.data["group_manager"][group_id] = {"level": 5, "close_plugins": []}