diff --git a/.gitignore b/.gitignore index 2dd6de62e..d53f278d0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ llm_statistics.txt mongodb napcat run_dev.bat - +elua.confirmed # C extensions *.so diff --git a/EULA.md b/EULA.md new file mode 100644 index 000000000..c878ff81d --- /dev/null +++ b/EULA.md @@ -0,0 +1,69 @@ + +--- +# **MaimBot用户协议** +**生效日期:** 2025.3.14 + +--- + +### **特别声明** +1. **MaimBot为遵循GPLv3协议的开源项目** + - 代码托管于GitHub,**开发者不持有任何法律实体**,项目由社区共同维护; + - 用户可自由使用、修改、分发代码,但**必须遵守GPLv3许可证要求**(详见项目仓库)。 + +2. **无责任声明** + - 本项目**不提供任何形式的担保**,开发者及贡献者均不对使用后果负责; + - 所有功能依赖第三方API,**生成内容不受我方控制**。 + +--- + +### **一、基础说明** +1. **MaimBot是什么** + - MaimBot是基于第三方AI技术(如ChatGPT等)的自动回复机器人,**所有输出内容均由AI自动生成,不代表我方观点**。 + - 用户可提交自定义指令(Prompt),经我方内容过滤后调用第三方API生成结果,**输出可能存在错误、偏见或不适宜内容**。 + +--- + +### **二、用户责任** +1. **禁止内容** + 您承诺**不提交或生成以下内容**,否则我方有权永久封禁账号: + - 违法、暴力、色情、歧视性内容; + - 诈骗、谣言、恶意代码等危害他人或社会的内容; + - 侵犯他人隐私、肖像权、知识产权的内容。 + +2. **后果自负** + - 您需对**输入的指令(Prompt)和生成内容的使用负全责**; + - **禁止将结果用于医疗、法律、投资等专业领域**,否则风险自行承担。 + +--- + +### **三、我们不负责什么** +1. **技术问题** + - 因第三方API故障、网络延迟、内容过滤误判导致的服务异常; + - AI生成内容的不准确、冒犯性、时效性错误。 + +2. **用户行为** + - 因您违反本协议或滥用MaimBot导致的任何纠纷、损失; + - 他人通过您的账号生成的违规内容。 + +--- + +### **四、其他重要条款** +1. **隐私与数据** + - 您提交的指令和生成内容可能被匿名化后用于优化服务,**敏感信息请勿输入**; + - **我方会收集部分统计信息(如使用频率、基础指令类型)以改进服务,您可在[bot_config.toml]随时关闭此功能**。 + +2. **精神健康风险** + ⚠️ **MaimBot仅为工具型机器人,不具备情感交互能力。建议用户:** + - 避免过度依赖AI回复处理现实问题或情绪困扰; + - 如感到心理不适,请及时寻求专业心理咨询服务。 + - 如遇心理困扰,请寻求专业帮助(全国心理援助热线:12355)。 + +3. **封禁权利** + - 我方有权不经通知**删除违规内容、暂停或终止您的访问权限**。 + +4. **争议解决** + - 本协议适用中国法律,争议提交相关地区法院管辖; + - 若因GPLv3许可产生纠纷,以许可证官方解释为准。 + + +--- diff --git a/bot.py b/bot.py index 50d04fd08..8201426c9 100644 --- a/bot.py +++ b/bot.py @@ -2,6 +2,7 @@ import asyncio import os import shutil import sys +from pathlib import Path import nonebot import time @@ -164,13 +165,35 @@ async def uvicorn_main(): uvicorn_server = server await server.serve() +def check_eula(): + eula_file = Path("elua.confirmed") + + # 如果已经确认过EULA,直接返回 + if eula_file.exists(): + return + + print("使用MaiMBot前请先阅读ELUA协议,继续运行视为同意协议") + print("协议内容:https://github.com/SengokuCola/MaiMBot/blob/main/EULA.md") + print('输入"同意"或"confirmed"继续运行') + + while True: + user_input = input().strip().lower() # 转换为小写以忽略大小写 + if user_input in ['同意', 'confirmed']: + # 创建确认文件 + eula_file.touch() + break + else: + print('请输入"同意"或"confirmed"以继续运行') + def raw_main(): # 利用 TZ 环境变量设定程序工作的时区 # 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用 if platform.system().lower() != "windows": time.tzset() - + + check_eula() + easter_egg() init_config() init_env() diff --git a/config/auto_update.py b/config/auto_update.py index 28ab108da..d87b7c129 100644 --- a/config/auto_update.py +++ b/config/auto_update.py @@ -42,8 +42,16 @@ def update_config(): update_dict(target[key], value) else: try: - # 直接使用tomlkit的item方法创建新值 - target[key] = tomlkit.item(value) + # 对数组类型进行特殊处理 + if isinstance(value, list): + # 如果是空数组,确保它保持为空数组 + if not value: + target[key] = tomlkit.array() + else: + target[key] = tomlkit.array(value) + else: + # 其他类型使用item方法创建新值 + target[key] = tomlkit.item(value) except (TypeError, ValueError): # 如果转换失败,直接赋值 target[key] = value diff --git a/run.sh b/run.sh index c3f6969b6..663fc8a67 100644 --- a/run.sh +++ b/run.sh @@ -97,8 +97,8 @@ check_python() { # 5/6: 选择分支 choose_branch() { BRANCH=$(whiptail --title "🔀 [5/6] 选择 Maimbot 分支" --menu "请选择要安装的 Maimbot 分支:" 15 60 2 \ - "main" "稳定版本(推荐)" \ - "debug" "开发版本(可能不稳定)" 3>&1 1>&2 2>&3) + "main" "稳定版本(推荐,供下载使用)" \ + "main-fix" "生产环境紧急修复" 3>&1 1>&2 2>&3) if [[ -z "$BRANCH" ]]; then BRANCH="main" @@ -201,6 +201,8 @@ install_napcat() { } # 运行安装步骤 +whiptail --title "⚠️ 警告:安装前详阅" --msgbox "项目处于活跃开发阶段,代码可能随时更改\n文档未完善,有问题可以提交 Issue 或者 Discussion\nQQ机器人存在被限制风险,请自行了解,谨慎使用\n由于持续迭代,可能存在一些已知或未知的bug\n由于开发中,可能消耗较多token\n\n本脚本可能更新不及时,如遇到bug请优先尝试手动部署以确定是否为脚本问题" 14 60 + check_system check_mongodb check_napcat @@ -233,7 +235,7 @@ fi if [[ "$IS_INSTALL_NAPCAT" == "true" ]]; then echo -e "${GREEN}安装 NapCat...${RESET}" - curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh && bash napcat.sh + curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh && bash napcat.sh --cli y --docker n fi echo -e "${GREEN}创建 Python 虚拟环境...${RESET}" diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 704a9a18d..34c788d98 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -55,112 +55,15 @@ class ChatBot: if not self._started: self._started = True - async def handle_notice(self, event: NoticeEvent, bot: Bot) -> None: - """处理收到的通知""" - # 戳一戳通知 - if isinstance(event, PokeNotifyEvent): - # 不处理其他人的戳戳 - if not event.is_tome(): - return - - # 用户屏蔽,不区分私聊/群聊 - if event.user_id in global_config.ban_user_id: - return - - reply_poke_probability = 1.0 # 回复戳一戳的概率,如果要改可以在这里改,暂不提取到配置文件 - - if random() < reply_poke_probability: - raw_message = "[戳了戳]你" # 默认类型 - if info := event.raw_info: - poke_type = info[2].get("txt", "戳了戳") # 戳戳类型,例如“拍一拍”、“揉一揉”、“捏一捏” - custom_poke_message = info[4].get("txt", "") # 自定义戳戳消息,若不存在会为空字符串 - raw_message = f"[{poke_type}]你{custom_poke_message}" - - raw_message += "(这是一个类似摸摸头的友善行为,而不是恶意行为,请不要作出攻击发言)" - await self.directly_reply(raw_message, event.user_id, event.group_id) - - if isinstance(event, GroupRecallNoticeEvent) or isinstance(event, FriendRecallNoticeEvent): - 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") - - chat = await chat_manager.get_or_create_stream( - platform=user_info.platform, user_info=user_info, group_info=group_info - ) - - await self.storage.store_recalled_message(event.message_id, time.time(), chat) - - async def handle_message(self, event: MessageEvent, bot: Bot) -> None: - """处理收到的消息""" - - self.bot = bot # 更新 bot 实例 - - # 用户屏蔽,不区分私聊/群聊 - 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: # 私聊过滤 - return - else: - try: - user_info = UserInfo( - user_id=event.user_id, - user_nickname=(await bot.get_stranger_info(user_id=event.user_id, no_cache=True))["nickname"], - user_cardname=None, - platform="qq", - ) - except Exception as e: - logger.error(f"获取陌生人信息失败: {e}") - return - logger.debug(user_info) - - # group_info = GroupInfo(group_id=0, group_name="私聊", platform="qq") - group_info = None - - # 处理群聊消息 - else: - # 白名单设定由nontbot侧完成 - if event.group_id: - if event.group_id not in global_config.talk_allowed_groups: - return - - user_info = UserInfo( - user_id=event.user_id, - user_nickname=event.sender.nickname, - user_cardname=event.sender.card or None, - platform="qq", - ) - - group_info = GroupInfo(group_id=event.group_id, group_name=None, platform="qq") - - # group_info = await bot.get_group_info(group_id=event.group_id) - # sender_info = await bot.get_group_member_info(group_id=event.group_id, user_id=event.user_id, no_cache=True) - - message_cq = MessageRecvCQ( - message_id=event.message_id, - user_info=user_info, - raw_message=str(event.original_message), - group_info=group_info, - reply_message=event.reply, - platform="qq", - ) + async def message_process(self, message_cq: MessageRecvCQ) -> None: + """处理转化后的统一格式消息 + 1. 过滤消息 + 2. 记忆激活 + 3. 意愿激活 + 4. 生成回复并发送 + 5. 更新关系 + 6. 更新情绪 + """ await message_cq.initialize() message_json = message_cq.to_dict() # 哦我嘞个json @@ -181,7 +84,9 @@ class ChatBot: await relationship_manager.update_relationship( chat_stream=chat, ) - await relationship_manager.update_relationship_value(chat_stream=chat, relationship_value=0.5) + await relationship_manager.update_relationship_value( + chat_stream=chat, relationship_value=0.5 + ) await message.process() @@ -203,11 +108,15 @@ class ChatBot: logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") return - current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(messageinfo.time)) + current_time = time.strftime( + "%Y-%m-%d %H:%M:%S", time.localtime(messageinfo.time) + ) #根据话题计算激活度 topic = "" - interested_rate = await hippocampus.memory_activate_value(message.processed_plain_text) / 100 + interested_rate = ( + await hippocampus.memory_activate_value(message.processed_plain_text) / 100 + ) logger.debug(f"对{message.processed_plain_text}的激活度:{interested_rate}") # logger.info(f"\033[1;32m[主题识别]\033[0m 使用{global_config.topic_extract}主题: {topic}") @@ -216,7 +125,6 @@ class ChatBot: is_mentioned = is_mentioned_bot_in_message(message) reply_probability = await willing_manager.change_reply_willing_received( chat_stream=chat, - topic=topic[0] if topic else None, is_mentioned_bot=is_mentioned, config=global_config, is_emoji=message.is_emoji, @@ -264,7 +172,10 @@ class ChatBot: # 找到message,删除 # print(f"开始找思考消息") for msg in container.messages: - if isinstance(msg, MessageThinking) and msg.message_info.message_id == think_id: + if ( + isinstance(msg, MessageThinking) + and msg.message_info.message_id == think_id + ): # print(f"找到思考消息: {msg}") thinking_message = msg container.messages.remove(msg) @@ -358,79 +269,171 @@ class ChatBot: chat_stream=chat, relationship_value=valuedict[emotion[0]] ) # 使用情绪管理器更新情绪 - self.mood_manager.update_mood_from_emotion(emotion[0], global_config.mood_intensity_factor) + self.mood_manager.update_mood_from_emotion( + emotion[0], global_config.mood_intensity_factor + ) # willing_manager.change_reply_willing_after_sent( # chat_stream=chat # ) - async def directly_reply(self, raw_message: str, user_id: int, group_id: int): - """ - 直接回复发来的消息,不经过意愿管理器 - """ + async def handle_notice(self, event: NoticeEvent, bot: Bot) -> None: + """处理收到的通知""" + if isinstance(event, PokeNotifyEvent): + # 戳一戳 通知 + # 不处理其他人的戳戳 + if not event.is_tome(): + return - # 构造用户信息和群组信息 - user_info = UserInfo( - user_id=user_id, - user_nickname=get_user_nickname(user_id) or None, - user_cardname=get_user_cardname(user_id) or None, - platform="qq", - ) - group_info = GroupInfo(group_id=group_id, group_name=None, platform="qq") + # 用户屏蔽,不区分私聊/群聊 + if event.user_id in global_config.ban_user_id: + return + + # 白名单模式 + if event.group_id: + if event.group_id not in global_config.talk_allowed_groups: + return + + raw_message = f"[戳了戳]{global_config.BOT_NICKNAME}" # 默认类型 + if info := event.raw_info: + poke_type = info[2].get( + "txt", "戳了戳" + ) # 戳戳类型,例如“拍一拍”、“揉一揉”、“捏一捏” + custom_poke_message = info[4].get( + "txt", "" + ) # 自定义戳戳消息,若不存在会为空字符串 + raw_message = ( + f"[{poke_type}]{global_config.BOT_NICKNAME}{custom_poke_message}" + ) + + raw_message += "(这是一个类似摸摸头的友善行为,而不是恶意行为,请不要作出攻击发言)" + + user_info = UserInfo( + user_id=event.user_id, + user_nickname=( + await bot.get_stranger_info(user_id=event.user_id, no_cache=True) + )["nickname"], + user_cardname=None, + platform="qq", + ) + + if event.group_id: + group_info = GroupInfo( + group_id=event.group_id, group_name=None, platform="qq" + ) + else: + group_info = None + + message_cq = MessageRecvCQ( + message_id=0, + user_info=user_info, + raw_message=str(raw_message), + group_info=group_info, + reply_message=None, + platform="qq", + ) + + await self.message_process(message_cq) + + elif isinstance(event, GroupRecallNoticeEvent) or isinstance( + event, FriendRecallNoticeEvent + ): + 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", + ) + + if isinstance(event, GroupRecallNoticeEvent): + group_info = GroupInfo( + group_id=event.group_id, group_name=None, platform="qq" + ) + else: + group_info = None + + chat = await chat_manager.get_or_create_stream( + platform=user_info.platform, user_info=user_info, group_info=group_info + ) + + await self.storage.store_recalled_message( + event.message_id, time.time(), chat + ) + + async def handle_message(self, event: MessageEvent, bot: Bot) -> None: + """处理收到的消息""" + + self.bot = bot # 更新 bot 实例 + + # 用户屏蔽,不区分私聊/群聊 + 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: # 私聊过滤 + return + else: + try: + user_info = UserInfo( + user_id=event.user_id, + user_nickname=( + await bot.get_stranger_info( + user_id=event.user_id, no_cache=True + ) + )["nickname"], + user_cardname=None, + platform="qq", + ) + except Exception as e: + logger.error(f"获取陌生人信息失败: {e}") + return + logger.debug(user_info) + + # group_info = GroupInfo(group_id=0, group_name="私聊", platform="qq") + group_info = None + + # 处理群聊消息 + else: + # 白名单设定由nontbot侧完成 + if event.group_id: + if event.group_id not in global_config.talk_allowed_groups: + return + + user_info = UserInfo( + user_id=event.user_id, + user_nickname=event.sender.nickname, + user_cardname=event.sender.card or None, + platform="qq", + ) + + group_info = GroupInfo( + group_id=event.group_id, group_name=None, platform="qq" + ) + + # group_info = await bot.get_group_info(group_id=event.group_id) + # sender_info = await bot.get_group_member_info(group_id=event.group_id, user_id=event.user_id, no_cache=True) message_cq = MessageRecvCQ( - message_id=None, + message_id=event.message_id, user_info=user_info, - raw_message=raw_message, + raw_message=str(event.original_message), group_info=group_info, - reply_message=None, + reply_message=event.reply, platform="qq", ) - await message_cq.initialize() - message_json = message_cq.to_dict() - - 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, - ) - - current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(messageinfo.time)) - logger.info( - f"[{current_time}][{chat.group_info.group_name if chat.group_info else '私聊'}]{chat.user_info.user_nickname}:" - f"{message.processed_plain_text}" - ) - - # 使用大模型生成回复 - 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) + await self.message_process(message_cq) # 创建全局ChatBot实例 chat_bot = ChatBot() diff --git a/src/plugins/chat/cq_code.py b/src/plugins/chat/cq_code.py index 2edc011b2..8967698dd 100644 --- a/src/plugins/chat/cq_code.py +++ b/src/plugins/chat/cq_code.py @@ -249,6 +249,13 @@ class CQCode: if self.reply_message is None: return None + if hasattr(self.reply_message, "group_id"): + group_info = GroupInfo( + platform="qq", group_id=self.reply_message.group_id, group_name="" + ) + else: + group_info = None + if self.reply_message.sender.user_id: message_obj = MessageRecvCQ( user_info=UserInfo( @@ -256,7 +263,7 @@ class CQCode: ), message_id=self.reply_message.message_id, raw_message=str(self.reply_message.message), - group_info=GroupInfo(group_id=self.reply_message.group_id), + group_info=group_info, ) await message_obj.initialize() diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index b138af3b6..2debbb522 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -50,7 +50,6 @@ class Message_Sender: if not is_recalled: message_json = message.to_dict() message_send = MessageSendCQ(data=message_json) - # logger.debug(message_send.message_info,message_send.raw_message) message_preview = truncate_message(message.processed_plain_text) if message_send.message_info.group_info and message_send.message_info.group_info.group_id: try: @@ -188,16 +187,17 @@ class MessageManager: else: if ( message_earliest.is_head - and message_earliest.update_thinking_time() > 30 + and message_earliest.update_thinking_time() > 10 and not message_earliest.is_private_message() # 避免在私聊时插入reply ): message_earliest.set_reply() - await message_sender.send_message(message_earliest) + await message_earliest.process() + + await message_sender.send_message(message_earliest) + + - print( - f"\033[1;34m[调试]\033[0m 消息“{truncate_message(message_earliest.processed_plain_text)}”正在发送中" - ) await self.storage.store_message(message_earliest, message_earliest.chat_stream, None) @@ -217,11 +217,11 @@ class MessageManager: and not message_earliest.is_private_message() # 避免在私聊时插入reply ): msg.set_reply() + + await msg.process() + await message_sender.send_message(msg) - - # if msg.is_emoji: - # msg.processed_plain_text = "[表情包]" - await msg.process() + await self.storage.store_message(msg, msg.chat_stream, None) if not container.remove_message(msg): diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index 1e17130be..14ae81c7a 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -11,10 +11,9 @@ class WillingManager: async def _decay_reply_willing(self): """定期衰减回复意愿""" while True: - await asyncio.sleep(3) + await asyncio.sleep(1) for chat_id in self.chat_reply_willing: - # 每分钟衰减10%的回复意愿 - self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.6) + self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.9) def get_willing(self, chat_stream: ChatStream) -> float: """获取指定聊天流的回复意愿""" @@ -28,7 +27,6 @@ class WillingManager: async def change_reply_willing_received(self, chat_stream: ChatStream, - topic: str = None, is_mentioned_bot: bool = False, config = None, is_emoji: bool = False, @@ -37,14 +35,14 @@ class WillingManager: """改变指定聊天流的回复意愿并返回回复概率""" chat_id = chat_stream.stream_id current_willing = self.chat_reply_willing.get(chat_id, 0) - - if topic and current_willing < 1: - current_willing += 0.2 - elif topic: - current_willing += 0.05 + + interested_rate = interested_rate * config.response_interested_rate_amplifier + + if interested_rate > 0.5: + current_willing += (interested_rate - 0.5) if is_mentioned_bot and current_willing < 1.0: - current_willing += 0.9 + current_willing += 1 elif is_mentioned_bot: current_willing += 0.05 @@ -53,7 +51,8 @@ class WillingManager: self.chat_reply_willing[chat_id] = min(current_willing, 3.0) - reply_probability = (current_willing - 0.5) * 2 + + reply_probability = min(max((current_willing - 0.5),0.03)* config.response_willing_amplifier * 2,1) # 检查群组权限(如果是群聊) if chat_stream.group_info and config: @@ -63,9 +62,6 @@ class WillingManager: if chat_stream.group_info.group_id in config.talk_frequency_down_groups: reply_probability = reply_probability / 3.5 - - if is_mentioned_bot and sender_id == "1026294844": - reply_probability = 1 return reply_probability diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 16a28d96e..89ebbe162 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -20,14 +20,14 @@ alias_names = ["小麦", "阿麦"] [personality] prompt_personality = [ - "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", # 贴吧人格 - "是一个女大学生,你有黑色头发,你会刷小红书", # 小红书人格 - "是一个女大学生,你会刷b站,对ACG文化感兴趣" # b站人格 + "用一句话或几句话描述性格特点和其他特征", + "用一句话或几句话描述性格特点和其他特征", + "例如,是一个热爱国家热爱党的新时代好青年" ] personality_1_probability = 0.6 # 第一种人格出现概率 personality_2_probability = 0.3 # 第二种人格出现概率 personality_3_probability = 0.1 # 第三种人格出现概率,请确保三个概率相加等于1 -prompt_schedule = "一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书" +prompt_schedule = "用一句话或几句话描述描述性格特点和其他特征" [message] min_text_length = 2 # 与麦麦聊天时麦麦只会回答文本大于等于此数的消息 diff --git a/webui.py b/webui.py index bd2e9ee0b..e22f250ea 100644 --- a/webui.py +++ b/webui.py @@ -157,31 +157,6 @@ def format_list_to_str(lst): res = res[:-1] return "[" + res + "]" -def format_list_to_str_alias(lst): - """ - 将Python列表转换为形如["src2.plugins.chat"]的字符串格式 - format_list_to_str(['src2.plugins.chat']) - '["src2.plugins.chat"]' - format_list_to_str([1, "two", 3.0]) - '[1, "two", 3.0]' - """ - resarr = lst.split(", ") - return resarr - -def format_list_to_int(lst): - resarr = [] - if len(lst) != 0: - resarr = lst.split(", ") - # print(resarr) - # print(type(resarr)) - ans = [] - if len(resarr) != 0: - for lsts in resarr: - temp = int(lsts) - ans.append(temp) - # print(ans) - # print(type(ans)) - return ans #env保存函数 def save_trigger(server_address, server_port, final_result_list,t_mongodb_host,t_mongodb_port,t_mongodb_database_name,t_chatanywhere_base_url,t_chatanywhere_key,t_siliconflow_base_url,t_siliconflow_key,t_deepseek_base_url,t_deepseek_key): @@ -208,13 +183,24 @@ def save_trigger(server_address, server_port, final_result_list,t_mongodb_host,t #============================================== #主要配置文件保存函数 def save_config_to_file(t_config_data): - with open("config/bot_config.toml", "w", encoding="utf-8") as f: + filename = "config/bot_config.toml" + backup_filename = f"{filename}.bak" + if not os.path.exists(backup_filename): + if os.path.exists(filename): + logger.info(f"{filename} 已存在,正在备份到 {backup_filename}...") + shutil.copy(filename, backup_filename) # 备份文件 + logger.success(f"文件已备份到 {backup_filename}") + else: + logger.warning(f"{filename} 不存在,无法进行备份。") + + + with open(filename, "w", encoding="utf-8") as f: toml.dump(t_config_data, f) logger.success("配置已保存到 bot_config.toml 文件中") def save_bot_config(t_qqbot_qq, t_nickname,t_nickname_final_result): config_data["bot"]["qq"] = int(t_qqbot_qq) config_data["bot"]["nickname"] = t_nickname - config_data["bot"]["alias_names"] = format_list_to_str_alias(t_nickname_final_result) + config_data["bot"]["alias_names"] = t_nickname_final_result save_config_to_file(config_data) logger.info("Bot配置已保存") return "Bot配置已保存" @@ -284,8 +270,8 @@ def save_message_and_emoji_config(t_min_text_length, config_data["message"]["response_willing_amplifier"] = t_response_willing_amplifier config_data["message"]["response_interested_rate_amplifier"] = t_response_interested_rate_amplifier config_data["message"]["down_frequency_rate"] = t_down_frequency_rate - config_data["message"]["ban_words"] = format_list_to_str_alias(t_ban_words_final_result) - config_data["message"]["ban_msgs_regex"] = format_list_to_str_alias(t_ban_msgs_regex_final_result) + config_data["message"]["ban_words"] =t_ban_words_final_result + config_data["message"]["ban_msgs_regex"] = t_ban_msgs_regex_final_result config_data["emoji"]["check_interval"] = t_check_interval config_data["emoji"]["register_interval"] = t_register_interval config_data["emoji"]["auto_save"] = t_auto_save @@ -344,7 +330,7 @@ def save_memory_mood_config(t_build_memory_interval, t_memory_compress_rate, t_f config_data["memory"]["forget_memory_interval"] = t_forget_memory_interval config_data["memory"]["memory_forget_time"] = t_memory_forget_time config_data["memory"]["memory_forget_percentage"] = t_memory_forget_percentage - config_data["memory"]["memory_ban_words"] = format_list_to_str_alias(t_memory_ban_words_final_result) + config_data["memory"]["memory_ban_words"] = t_memory_ban_words_final_result config_data["mood"]["update_interval"] = t_mood_update_interval config_data["mood"]["decay_rate"] = t_mood_decay_rate config_data["mood"]["intensity_factor"] = t_mood_intensity_factor @@ -369,9 +355,9 @@ def save_other_config(t_keywords_reaction_enabled,t_enable_advance_output, t_ena def save_group_config(t_talk_allowed_final_result, t_talk_frequency_down_final_result, t_ban_user_id_final_result,): - config_data["groups"]["talk_allowed"] = format_list_to_int(t_talk_allowed_final_result) - config_data["groups"]["talk_frequency_down"] = format_list_to_int(t_talk_frequency_down_final_result) - config_data["groups"]["ban_user_id"] = format_list_to_int(t_ban_user_id_final_result) + config_data["groups"]["talk_allowed"] = t_talk_allowed_final_result + config_data["groups"]["talk_frequency_down"] = t_talk_frequency_down_final_result + config_data["groups"]["ban_user_id"] = t_ban_user_id_final_result save_config_to_file(config_data) logger.info("群聊设置已保存到 bot_config.toml 文件中") return "群聊设置已保存" @@ -379,11 +365,11 @@ def save_group_config(t_talk_allowed_final_result, with gr.Blocks(title="MaimBot配置文件编辑") as app: gr.Markdown( value=""" - 欢迎使用由墨梓柒MotricSeven编写的MaimBot配置文件编辑器\n + ### 欢迎使用由墨梓柒MotricSeven编写的MaimBot配置文件编辑器\n """ ) gr.Markdown( - value="配置文件版本:" + config_data["inner"]["version"] + value="### 配置文件版本:" + config_data["inner"]["version"] ) with gr.Tabs(): with gr.TabItem("0-环境设置"): @@ -525,7 +511,7 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: interactive=True ) with gr.Row(): - save_env_btn = gr.Button("保存环境配置") + save_env_btn = gr.Button("保存环境配置",variant="primary") with gr.Row(): save_env_btn.click( save_trigger, @@ -594,7 +580,7 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: elem_classes="save_bot_btn" ).click( save_bot_config, - inputs=[qqbot_qq, nickname,nickname_final_result], + inputs=[qqbot_qq, nickname,nickname_list_state], outputs=[gr.Textbox( label="保存Bot结果" )] @@ -644,18 +630,19 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: interactive=True ) with gr.Row(): - gr.Button( + personal_save_btn = gr.Button( "保存人格配置", variant="primary", elem_id="save_personality_btn", elem_classes="save_personality_btn" - ).click( - save_personality_config, - inputs=[personality_1, personality_2, personality_3, prompt_schedule], - outputs=[gr.Textbox( - label="保存人格结果" - )] ) + with gr.Row(): + personal_save_message = gr.Textbox(label="保存人格结果") + personal_save_btn.click( + save_personality_config, + inputs=[personality_1, personality_2, personality_3, prompt_schedule], + outputs=[personal_save_message] + ) with gr.TabItem("3-消息&表情包设置"): with gr.Row(): with gr.Column(scale=3): @@ -729,24 +716,24 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: with gr.Row(): ban_msgs_regex_list_display = gr.TextArea( value="\n".join(ban_msgs_regex_list), - label="违禁词列表", + label="违禁消息正则列表", interactive=False, lines=5 ) with gr.Row(): with gr.Column(scale=3): - ban_msgs_regex_new_item_input = gr.Textbox(label="添加新违禁词") + ban_msgs_regex_new_item_input = gr.Textbox(label="添加新违禁消息正则") ban_msgs_regex_add_btn = gr.Button("添加", scale=1) with gr.Row(): with gr.Column(scale=3): ban_msgs_regex_item_to_delete = gr.Dropdown( choices=ban_msgs_regex_list, - label="选择要删除的违禁词" + label="选择要删除的违禁消息正则" ) ban_msgs_regex_delete_btn = gr.Button("删除", scale=1) - ban_msgs_regex_final_result = gr.Text(label="修改后的违禁词") + ban_msgs_regex_final_result = gr.Text(label="修改后的违禁消息正则") ban_msgs_regex_add_btn.click( add_item, inputs=[ban_msgs_regex_new_item_input, ban_msgs_regex_list_state], @@ -769,33 +756,36 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: with gr.Row(): check_prompt = gr.Textbox(value=config_data['emoji']['check_prompt'], label="表情包过滤要求") with gr.Row(): - gr.Button( + emoji_save_btn = gr.Button( "保存消息&表情包设置", variant="primary", elem_id="save_personality_btn", elem_classes="save_personality_btn" - ).click( - save_message_and_emoji_config, - inputs=[ - min_text_length, - max_context_size, - emoji_chance, - thinking_timeout, - response_willing_amplifier, - response_interested_rate_amplifier, - down_frequency_rate, - ban_words_final_result, - ban_msgs_regex_final_result, - check_interval, - register_interval, - auto_save, - enable_check, - check_prompt - ], - outputs=[gr.Textbox( - label="消息&表情包设置保存结果" - )] ) + with gr.Row(): + emoji_save_message = gr.Textbox( + label="消息&表情包设置保存结果" + ) + emoji_save_btn.click( + save_message_and_emoji_config, + inputs=[ + min_text_length, + max_context_size, + emoji_chance, + thinking_timeout, + response_willing_amplifier, + response_interested_rate_amplifier, + down_frequency_rate, + ban_words_list_state, + ban_msgs_regex_list_state, + check_interval, + register_interval, + auto_save, + enable_check, + check_prompt + ], + outputs=[emoji_save_message] + ) with gr.TabItem("4-回复&模型设置"): with gr.Row(): with gr.Column(scale=3): @@ -878,7 +868,7 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: with gr.Row(): vlm_model_provider = gr.Dropdown(choices=["SILICONFLOW","DEEP_SEEK", "CHAT_ANY_WHERE"], value=config_data['model']['vlm']['provider'], label="识图模型提供商") with gr.Row(): - save_model_btn = gr.Button("保存回复&模型设置") + save_model_btn = gr.Button("保存回复&模型设置",variant="primary", elem_id="save_model_btn") with gr.Row(): save_btn_message = gr.Textbox() save_model_btn.click( @@ -947,13 +937,13 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: with gr.Row(): mood_intensity_factor = gr.Number(value=config_data['mood']['mood_intensity_factor'], label="心情强度因子") with gr.Row(): - save_memory_mood_btn = gr.Button("保存 [Memory] 配置") + save_memory_mood_btn = gr.Button("保存记忆&心情设置",variant="primary") with gr.Row(): save_memory_mood_message = gr.Textbox() with gr.Row(): save_memory_mood_btn.click( save_memory_mood_config, - inputs=[build_memory_interval, memory_compress_rate, forget_memory_interval, memory_forget_time, memory_forget_percentage, memory_ban_words_final_result, mood_update_interval, mood_decay_rate, mood_intensity_factor], + inputs=[build_memory_interval, memory_compress_rate, forget_memory_interval, memory_forget_time, memory_forget_percentage, memory_ban_words_list_state, mood_update_interval, mood_decay_rate, mood_intensity_factor], outputs=[save_memory_mood_message] ) with gr.TabItem("6-群组设置"): @@ -1079,16 +1069,16 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: outputs=[ban_user_id_list_state, ban_user_id_list_display, ban_user_id_item_to_delete, ban_user_id_final_result] ) with gr.Row(): - save_group_btn = gr.Button("保存群组设置") + save_group_btn = gr.Button("保存群组设置",variant="primary") with gr.Row(): save_group_btn_message = gr.Textbox() with gr.Row(): save_group_btn.click( save_group_config, inputs=[ - talk_allowed_final_result, - talk_frequency_down_final_result, - ban_user_id_final_result, + talk_allowed_list_state, + talk_frequency_down_list_state, + ban_user_id_list_state, ], outputs=[save_group_btn_message] ) @@ -1124,7 +1114,7 @@ with gr.Blocks(title="MaimBot配置文件编辑") as app: with gr.Row(): word_replace_rate = gr.Slider(minimum=0, maximum=1, step=0.001, value=config_data['chinese_typo']['word_replace_rate'], label="整词替换概率") with gr.Row(): - save_other_config_btn = gr.Button("保存其他配置") + save_other_config_btn = gr.Button("保存其他配置",variant="primary") with gr.Row(): save_other_config_message = gr.Textbox() with gr.Row(): diff --git a/webui_conda.bat b/webui_conda.bat new file mode 100644 index 000000000..02a11327f --- /dev/null +++ b/webui_conda.bat @@ -0,0 +1,28 @@ +@echo on +echo Starting script... +echo Activating conda environment: maimbot +call conda activate maimbot +if errorlevel 1 ( + echo Failed to activate conda environment + pause + exit /b 1 +) +echo Conda environment activated successfully +echo Changing directory to C:\GitHub\MaiMBot +cd /d C:\GitHub\MaiMBot +if errorlevel 1 ( + echo Failed to change directory + pause + exit /b 1 +) +echo Current directory is: +cd + +python webui.py +if errorlevel 1 ( + echo Command failed with error code %errorlevel% + pause + exit /b 1 +) +echo Script completed successfully +pause \ No newline at end of file