diff --git a/.gitignore b/.gitignore index 267374b..b2d679d 100644 --- a/.gitignore +++ b/.gitignore @@ -271,4 +271,5 @@ $RECYCLE.BIN/ config.toml config.toml.back -test \ No newline at end of file +test +data/qq_bot.json \ No newline at end of file diff --git a/assets/maimai.ico b/assets/maimai.ico deleted file mode 100644 index 578b11c..0000000 Binary files a/assets/maimai.ico and /dev/null differ diff --git a/requirements.txt b/requirements.txt index 41e8eb1..c0ad7e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,6 @@ requests maim_message loguru pillow -tomli \ No newline at end of file +tomli +tomlkit +rich \ No newline at end of file diff --git a/src/config/official_configs.py b/src/config/official_configs.py index d8928a8..d652f35 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -61,6 +61,9 @@ class ChatConfig(ConfigBase): ban_user_id: list[int] = field(default_factory=[]) """被封禁的用户ID列表,封禁后将无法与其进行交互""" + ban_qq_bot: bool = False + """是否屏蔽QQ官方机器人,若为True,则所有QQ官方机器人将无法与MaiMCore进行交互""" + enable_poke: bool = True """是否启用戳一戳功能""" diff --git a/src/recv_handler.py b/src/recv_handler.py index 687f693..cba510b 100644 --- a/src/recv_handler.py +++ b/src/recv_handler.py @@ -27,6 +27,8 @@ from .utils import ( get_self_info, get_stranger_info, get_message_detail, + read_bot_id, + update_bot_id, ) from .response_pool import get_response @@ -38,6 +40,7 @@ class RecvHandler: self.server_connection: Server.ServerConnection = None self.interval = global_config.napcat_server.heartbeat_interval self._interval_checking = False + self.bot_id_list: Dict[int, bool] = {} async def handle_meta_event(self, message: dict) -> None: event_type = message.get("meta_event_type") @@ -69,7 +72,7 @@ class RecvHandler: logger.debug("心跳正常") await asyncio.sleep(self.interval) - def check_allow_to_chat(self, user_id: int, group_id: Optional[int]) -> bool: + async def check_allow_to_chat(self, user_id: int, group_id: Optional[int]) -> bool: # sourcery skip: hoist-statement-from-if, merge-else-if-into-elif """ 检查是否允许聊天 @@ -79,7 +82,32 @@ class RecvHandler: Returns: bool: 是否允许聊天 """ + user_id = str(user_id) logger.debug(f"群聊id: {group_id}, 用户id: {user_id}") + if global_config.chat.ban_qq_bot and group_id: + logger.debug("开始判断是否为机器人") + if not self.bot_id_list: + self.bot_id_list = read_bot_id() + if user_id in self.bot_id_list: + if self.bot_id_list[user_id]: + logger.warning("QQ官方机器人消息拦截已启用,消息被丢弃") + return False + else: + member_info = await get_member_info(self.server_connection, group_id, user_id) + if member_info: + is_bot = member_info.get("is_robot") + if is_bot is None: + logger.warning("无法获取用户是否为机器人,默认为不是但是不进行更新") + else: + if is_bot: + logger.warning("QQ官方机器人消息拦截已启用,消息被丢弃,新机器人加入拦截名单") + self.bot_id_list[user_id] = True + update_bot_id(self.bot_id_list) + return False + else: + self.bot_id_list[user_id] = False + update_bot_id(self.bot_id_list) + logger.debug("开始检查聊天白名单/黑名单") if group_id: if global_config.chat.group_list_type == "whitelist" and group_id not in global_config.chat.group_list: logger.warning("群聊不在聊天白名单中,消息被丢弃") @@ -122,7 +150,7 @@ class RecvHandler: if sub_type == MessageType.Private.friend: sender_info: dict = raw_message.get("sender") - if not self.check_allow_to_chat(sender_info.get("user_id"), None): + if not await self.check_allow_to_chat(sender_info.get("user_id"), None): return None # 发送者用户信息 @@ -181,7 +209,7 @@ class RecvHandler: if sub_type == MessageType.Group.normal: sender_info: dict = raw_message.get("sender") - if not self.check_allow_to_chat(sender_info.get("user_id"), raw_message.get("group_id")): + if not await self.check_allow_to_chat(sender_info.get("user_id"), raw_message.get("group_id")): return None # 发送者用户信息 @@ -486,7 +514,7 @@ class RecvHandler: user_id = raw_message.get("user_id") target_id = raw_message.get("target_id") - if not self.check_allow_to_chat(user_id, group_id): + if not await self.check_allow_to_chat(user_id, group_id): logger.warning("notice消息被丢弃") return None diff --git a/src/utils.py b/src/utils.py index fb0b5b4..9e9941c 100644 --- a/src/utils.py +++ b/src/utils.py @@ -7,9 +7,10 @@ from .response_pool import get_response import urllib3 import ssl - +from pathlib import Path from PIL import Image import io +import os class SSLAdapter(urllib3.PoolManager): @@ -88,6 +89,7 @@ async def get_image_base64(url: str) -> str: def convert_image_to_gif(image_base64: str) -> str: + # sourcery skip: extract-method """ 将Base64编码的图片转换为GIF格式 Parameters: @@ -192,3 +194,43 @@ async def get_message_detail(websocket: Server.ServerConnection, message_id: str return None logger.debug(response) return response.get("data") + + +def update_bot_id(data: dict) -> None: + """ + 更新用户是否为机器人的字典到根目录下的data文件夹中的qq_bot.json。 + Parameters: + data: dict: 包含需要更新的信息。 + """ + json_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "qq_bot.json") + try: + with open(json_path, "w", encoding="utf-8") as json_file: + json.dump(data, json_file, ensure_ascii=False, indent=4) + logger.info(f"ID字典已更新到文件: {json_path}") + except Exception as e: + logger.error(f"更新ID字典失败: {e}") + + +def read_bot_id() -> dict: + """ + 从根目录下的data文件夹中的文件读取机器人ID。 + Returns: + list: 读取的机器人ID信息。 + """ + json_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "qq_bot.json") + try: + with open(json_path, "r", encoding="utf-8") as json_file: + data = json.load(json_file) + logger.info(f"已读取机器人ID信息: {data}") + return data + except FileNotFoundError: + logger.warning(f"文件未找到: {json_path},正在自动创建文件") + json_path = Path(os.path.dirname(os.path.dirname(__file__))) / "data" / "qq_bot.json" + # 确保父目录存在 + json_path.parent.mkdir(parents=True, exist_ok=True) + # 创建空文件 + json_path.touch(exist_ok=True) + return {} + except Exception as e: + logger.error(f"读取机器人ID失败: {e}") + return {} diff --git a/template/template_config.toml b/template/template_config.toml index b4cdce0..8598260 100644 --- a/template/template_config.toml +++ b/template/template_config.toml @@ -24,6 +24,7 @@ private_list = [] # 私聊名单 # 当private_list_type为whitelist时,只有私聊名单中的用户可以聊天 # 当private_list_type为blacklist时,私聊名单中的任何用户无法聊天 ban_user_id = [] # 全局禁止名单(全局禁止名单中的用户无法进行任何聊天) +ban_qq_bot = false # 是否屏蔽QQ官方机器人 enable_poke = true # 是否启用戳一戳功能 [voice] # 发送语音设置