From e7b9e9d20b7e6f44a81a36f532bff222b13b6fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=B4=E7=8C=AB?= Date: Thu, 13 Mar 2025 02:22:50 +0900 Subject: [PATCH 1/8] add ruff action to debug branch --- .github/workflows/ruff.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/workflows/ruff.yml diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 000000000..4adeffd74 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,8 @@ +name: Ruff +on: [ push, pull_request ] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v3 From 4ffffb3b606fbe60ab508c4e6c4b444f41ab5d0a Mon Sep 17 00:00:00 2001 From: ChangingSelf Date: Thu, 13 Mar 2025 01:38:53 +0800 Subject: [PATCH 2/8] =?UTF-8?q?close=20SengokuCola/MaiMBot#246=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=88=B3=E4=B8=80=E6=88=B3=E5=9B=9E=E5=BA=94=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/__init__.py | 20 +++++++---- src/plugins/chat/bot.py | 68 +++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index d7a7bd7e4..26b3d36da 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -3,8 +3,9 @@ import time import os from loguru import logger -from nonebot import get_driver, on_message, require -from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment,MessageEvent +from nonebot import get_driver, on_message, on_notice, require +from nonebot.rule import to_me +from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment, MessageEvent, NoticeEvent from nonebot.typing import T_State from ..moods.moods import MoodManager # 导入情绪管理器 @@ -39,6 +40,8 @@ logger.debug(f"正在唤醒{global_config.BOT_NICKNAME}......") chat_bot = ChatBot() # 注册消息处理器 msg_in = on_message(priority=5) +# 注册和bot相关的通知处理器 +notice_matcher = on_notice(priority=1) # 创建定时任务 scheduler = require("nonebot_plugin_apscheduler").scheduler @@ -95,19 +98,24 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await chat_bot.handle_message(event, bot) +@notice_matcher.handle() +async def _(bot: Bot, event: NoticeEvent, state: T_State): + logger.debug(f"收到通知:{event}") + await chat_bot.handle_notice(event, bot) + + # 添加build_memory定时任务 @scheduler.scheduled_job("interval", seconds=global_config.build_memory_interval, id="build_memory") async def build_memory_task(): """每build_memory_interval秒执行一次记忆构建""" - logger.debug( - "[记忆构建]" - "------------------------------------开始构建记忆--------------------------------------") + logger.debug("[记忆构建]------------------------------------开始构建记忆--------------------------------------") start_time = time.time() await hippocampus.operation_build_memory(chat_size=20) end_time = time.time() logger.success( f"[记忆构建]--------------------------记忆构建完成:耗时: {end_time - start_time:.2f} " - "秒-------------------------------------------") + "秒-------------------------------------------" + ) @scheduler.scheduled_job("interval", seconds=global_config.forget_memory_interval, id="forget_memory") diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 1db38477c..ffce56963 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -7,6 +7,8 @@ from nonebot.adapters.onebot.v11 import ( GroupMessageEvent, MessageEvent, PrivateMessageEvent, + NoticeEvent, + PokeNotifyEvent, ) from ..memory_system.memory import hippocampus @@ -25,6 +27,7 @@ from .relationship_manager import relationship_manager from .storage import MessageStorage from .utils import calculate_typing_time, is_mentioned_bot_in_message from .utils_image import image_path_to_base64 +from .utils_user import get_user_nickname, get_user_cardname, get_groupname from .willing_manager import willing_manager # 导入意愿管理器 from .message_base import UserInfo, GroupInfo, Seg @@ -46,6 +49,69 @@ class ChatBot: if not self._started: self._started = True + async def handle_notice(self, event: NoticeEvent, bot: Bot) -> None: + """处理收到的通知""" + # 戳一戳通知 + if isinstance(event, PokeNotifyEvent): + # 用户屏蔽,不区分私聊/群聊 + if event.user_id in global_config.ban_user_id: + return + reply_poke_probability = 1 # 回复戳一戳的概率 + + if random() < reply_poke_probability: + user_info = UserInfo( + user_id=event.user_id, + user_nickname=get_user_nickname(event.user_id) or None, + user_cardname=get_user_cardname(event.user_id) or None, + platform="qq", + ) + group_info = GroupInfo(group_id=event.group_id, group_name=None, platform="qq") + message_cq = MessageRecvCQ( + message_id=None, + user_info=user_info, + raw_message=str("[戳了戳]你"), + group_info=group_info, + reply_message=None, + platform="qq", + ) + message_json = message_cq.to_dict() + + # 进入maimbot + message = MessageRecv(message_json) + groupinfo = message.message_info.group_info + userinfo = message.message_info.user_info + messageinfo = message.message_info + + chat = await chat_manager.get_or_create_stream( + platform=messageinfo.platform, user_info=userinfo, group_info=groupinfo + ) + message.update_chat_stream(chat) + await message.process() + + bot_user_info = UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=messageinfo.platform, + ) + + response, raw_content = await self.gpt.generate_response(message) + + if response: + for msg in response: + message_segment = Seg(type="text", data=msg) + + bot_message = MessageSending( + message_id=None, + chat_stream=chat, + bot_user_info=bot_user_info, + sender_info=userinfo, + message_segment=message_segment, + reply=None, + is_head=False, + is_emoji=False, + ) + message_manager.add_message(bot_message) + async def handle_message(self, event: MessageEvent, bot: Bot) -> None: """处理收到的消息""" @@ -143,7 +209,7 @@ class ChatBot: current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(messageinfo.time)) # topic=await topic_identifier.identify_topic_llm(message.processed_plain_text) - + topic = "" interested_rate = await hippocampus.memory_activate_value(message.processed_plain_text) / 100 logger.debug(f"对{message.processed_plain_text}的激活度:{interested_rate}") From fe7ef7d7315bb02fa4f885e782ca107837f3b264 Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Thu, 13 Mar 2025 02:51:53 +0800 Subject: [PATCH 3/8] =?UTF-8?q?chore:=20=E7=94=A8=E6=88=B7=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E4=BC=98=E5=8C=96=EF=BC=88=E6=8A=A5=E9=94=99=E4=BF=A1?= =?UTF-8?q?=E6=81=AF&log=EF=BC=89+=20ruff=E7=9A=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 96 ++++++++++++++++--------------- src/plugins/models/utils_model.py | 2 +- template.env | 2 +- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/bot.py b/bot.py index 48517fe24..b78cd0e03 100644 --- a/bot.py +++ b/bot.py @@ -17,19 +17,6 @@ env_mask = {key: os.getenv(key) for key in os.environ} uvicorn_server = None -# 配置日志 -log_path = os.path.join(os.getcwd(), "logs") -if not os.path.exists(log_path): - os.makedirs(log_path) - -# 添加文件日志,启用rotation和retention -logger.add( - os.path.join(log_path, "maimbot_{time:YYYY-MM-DD}.log"), - rotation="00:00", # 每天0点创建新文件 - retention="30 days", # 保留30天的日志 - level="INFO", - encoding="utf-8" -) def easter_egg(): # 彩蛋 @@ -76,7 +63,7 @@ def init_env(): # 首先加载基础环境变量.env if os.path.exists(".env"): - load_dotenv(".env",override=True) + load_dotenv(".env", override=True) logger.success("成功加载基础环境变量配置") @@ -90,10 +77,7 @@ def load_env(): logger.success("加载开发环境变量配置") load_dotenv(".env.dev", override=True) # override=True 允许覆盖已存在的环境变量 - fn_map = { - "prod": prod, - "dev": dev - } + fn_map = {"prod": prod, "dev": dev} env = os.getenv("ENVIRONMENT") logger.info(f"[load_env] 当前的 ENVIRONMENT 变量值:{env}") @@ -109,28 +93,53 @@ def load_env(): logger.error(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在") RuntimeError(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在") -def load_logger(): - logger.remove() # 移除默认配置 - if os.getenv("ENVIRONMENT") == "dev": - logger.add( - sys.stderr, - format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <7} | {name:.<8}:{function:.<8}:{line: >4} - {message}", - colorize=True, - level=os.getenv("LOG_LEVEL", "DEBUG"), # 根据环境设置日志级别,默认为DEBUG - ) - else: - logger.add( - sys.stderr, - format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <7} | {name:.<8}:{function:.<8}:{line: >4} - {message}", - colorize=True, - level=os.getenv("LOG_LEVEL", "INFO"), # 根据环境设置日志级别,默认为INFO - filter=lambda record: "nonebot" not in record["name"] - ) +def load_logger(): + logger.remove() + + # 配置日志基础路径 + log_path = os.path.join(os.getcwd(), "logs") + if not os.path.exists(log_path): + os.makedirs(log_path) + + current_env = os.getenv("ENV", "dev") + + # 公共配置参数 + log_level = os.getenv( + "LOG_LEVEL", + "INFO" if current_env == "prod" else "DEBUG" + ) + log_filter = lambda record: ( + ("nonebot" not in record["name"] or record["level"].no >= logger.level("ERROR").no + ) if current_env == "prod" else True + ) + log_format = ( + "{time:YYYY-MM-DD HH:mm:ss.SSS} " + "| {level: <7} " + "| {name:.<8}:{function:.<8}:{line: >4} " + "- {message}" + ) + + # 日志文件储存至/logs + logger.add( + os.path.join(log_path, "maimbot_{time:YYYY-MM-DD}.log"), + rotation="00:00", + retention="30 days", + format=log_format, + colorize=False, + level=log_level, + filter=log_filter, + encoding="utf-8" + ) + + # 终端输出 + logger.add( + sys.stderr, + format=log_format, + colorize=True, + level=log_level, + filter=log_filter + ) def scan_provider(env_config: dict): @@ -160,10 +169,7 @@ def scan_provider(env_config: dict): # 检查每个 provider 是否同时存在 url 和 key for provider_name, config in provider.items(): if config["url"] is None or config["key"] is None: - logger.error( - f"provider 内容:{config}\n" - f"env_config 内容:{env_config}" - ) + logger.error(f"provider 内容:{config}\nenv_config 内容:{env_config}") raise ValueError(f"请检查 '{provider_name}' 提供商配置是否丢失 BASE_URL 或 KEY 环境变量") @@ -192,7 +198,7 @@ async def uvicorn_main(): reload=os.getenv("ENVIRONMENT") == "dev", timeout_graceful_shutdown=5, log_config=None, - access_log=False + access_log=False, ) server = uvicorn.Server(config) uvicorn_server = server @@ -202,7 +208,7 @@ async def uvicorn_main(): def raw_main(): # 利用 TZ 环境变量设定程序工作的时区 # 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用 - if platform.system().lower() != 'windows': + if platform.system().lower() != "windows": time.tzset() easter_egg() diff --git a/src/plugins/models/utils_model.py b/src/plugins/models/utils_model.py index afe4baeb5..0f5bb335c 100644 --- a/src/plugins/models/utils_model.py +++ b/src/plugins/models/utils_model.py @@ -132,7 +132,7 @@ class LLM_request: # 常见Error Code Mapping error_code_mapping = { 400: "参数不正确", - 401: "API key 错误,认证失败", + 401: "API key 错误,认证失败,请检查/config/bot_config.toml和.env.prod中的配置是否正确哦~", 402: "账号余额不足", 403: "需要实名,或余额不足", 404: "Not Found", diff --git a/template.env b/template.env index d2a763112..322776ce7 100644 --- a/template.env +++ b/template.env @@ -23,7 +23,7 @@ CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 -#定义你要用的api的base_url +#定义你要用的api的key(需要去对应网站申请哦) DEEP_SEEK_KEY= CHAT_ANY_WHERE_KEY= SILICONFLOW_KEY= From 11e8b2fa5f00defaeb1ce2a770fb8afbd7b0d19b Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Thu, 13 Mar 2025 03:18:49 +0800 Subject: [PATCH 4/8] =?UTF-8?q?chore:=20ruff=E7=9A=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 20 ++++++-------------- src/common/database.py | 4 +--- src/plugins/chat/__init__.py | 9 ++++----- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/bot.py b/bot.py index b78cd0e03..b5d210cc3 100644 --- a/bot.py +++ b/bot.py @@ -105,13 +105,11 @@ def load_logger(): current_env = os.getenv("ENV", "dev") # 公共配置参数 - log_level = os.getenv( - "LOG_LEVEL", - "INFO" if current_env == "prod" else "DEBUG" - ) + log_level = os.getenv("LOG_LEVEL", "INFO" if current_env == "prod" else "DEBUG") log_filter = lambda record: ( - ("nonebot" not in record["name"] or record["level"].no >= logger.level("ERROR").no - ) if current_env == "prod" else True + ("nonebot" not in record["name"] or record["level"].no >= logger.level("ERROR").no) + if current_env == "prod" + else True ) log_format = ( "{time:YYYY-MM-DD HH:mm:ss.SSS} " @@ -129,17 +127,11 @@ def load_logger(): colorize=False, level=log_level, filter=log_filter, - encoding="utf-8" + encoding="utf-8", ) # 终端输出 - logger.add( - sys.stderr, - format=log_format, - colorize=True, - level=log_level, - filter=log_filter - ) + logger.add(sys.stderr, format=log_format, colorize=True, level=log_level, filter=log_filter) def scan_provider(env_config: dict): diff --git a/src/common/database.py b/src/common/database.py index ca73dc468..cd149e526 100644 --- a/src/common/database.py +++ b/src/common/database.py @@ -22,9 +22,7 @@ def __create_database_instance(): if username and password: # 如果有用户名和密码,使用认证连接 - return MongoClient( - host, port, username=username, password=password, authSource=auth_source - ) + return MongoClient(host, port, username=username, password=password, authSource=auth_source) # 否则使用无认证连接 return MongoClient(host, port) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index d7a7bd7e4..c4281d186 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -4,7 +4,7 @@ import os from loguru import logger from nonebot import get_driver, on_message, require -from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment,MessageEvent +from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment, MessageEvent from nonebot.typing import T_State from ..moods.moods import MoodManager # 导入情绪管理器 @@ -99,15 +99,14 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @scheduler.scheduled_job("interval", seconds=global_config.build_memory_interval, id="build_memory") async def build_memory_task(): """每build_memory_interval秒执行一次记忆构建""" - logger.debug( - "[记忆构建]" - "------------------------------------开始构建记忆--------------------------------------") + logger.debug("[记忆构建]------------------------------------开始构建记忆--------------------------------------") start_time = time.time() await hippocampus.operation_build_memory(chat_size=20) end_time = time.time() logger.success( f"[记忆构建]--------------------------记忆构建完成:耗时: {end_time - start_time:.2f} " - "秒-------------------------------------------") + "秒-------------------------------------------" + ) @scheduler.scheduled_job("interval", seconds=global_config.forget_memory_interval, id="forget_memory") From 3b5523d7e2e1a27f65a648952fdc9648c511f046 Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Thu, 13 Mar 2025 03:21:54 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E6=9A=82=E6=97=B6=E5=85=B3=E6=8E=89ruff?= =?UTF-8?q?=E7=9A=84checkout@v3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ruff.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 4adeffd74..812dac070 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -5,4 +5,3 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: astral-sh/ruff-action@v3 From 2c74a5dfb7a8d63a5d44be1f20eedecb5c59dc33 Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Thu, 13 Mar 2025 03:35:54 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E6=81=A2=E5=A4=8Druff=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ruff.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 812dac070..0d1e50c5a 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -5,3 +5,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v3 \ No newline at end of file From a2c29efb41d82b9313ee07a347181c522528ecb6 Mon Sep 17 00:00:00 2001 From: Pliosauroidea Date: Thu, 13 Mar 2025 09:27:56 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84envieonment=E8=AF=BB=E5=8F=96=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84debug=E8=BE=93=E5=87=BA=E6=8C=81=E7=BB=AD=E5=BC=80?= =?UTF-8?q?=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index b5d210cc3..a3a844a15 100644 --- a/bot.py +++ b/bot.py @@ -102,7 +102,7 @@ def load_logger(): if not os.path.exists(log_path): os.makedirs(log_path) - current_env = os.getenv("ENV", "dev") + current_env = os.getenv("ENVIRONMENT", "dev") # 公共配置参数 log_level = os.getenv("LOG_LEVEL", "INFO" if current_env == "prod" else "DEBUG") From f1e38e8b1320318cea73caa2be3329617efbc3f1 Mon Sep 17 00:00:00 2001 From: Cindy-Master <2606440373@qq.com> Date: Thu, 13 Mar 2025 09:32:33 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20ban=5Fuser=5Fid=E5=9C=A8=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E8=A2=AB=E7=BB=95=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/bot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 44931c75e..b90b3d0f3 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -120,7 +120,10 @@ class ChatBot: # 用户屏蔽,不区分私聊/群聊 if event.user_id in global_config.ban_user_id: return - + + if event.reply and hasattr(event.reply, 'sender') and hasattr(event.reply.sender, 'user_id') and event.reply.sender.user_id in global_config.ban_user_id: + logger.debug(f"跳过处理回复来自被ban用户 {event.reply.sender.user_id} 的消息") + return # 处理私聊消息 if isinstance(event, PrivateMessageEvent): if not global_config.enable_friend_chat: # 私聊过滤