diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a4245d0a0..ce2623260 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,6 +12,23 @@ body: - label: "我确认在 Issues 列表中并无其他人已经提出过与此问题相同或相似的问题" required: true - label: "我使用了 Docker" +- type: dropdown + attributes: + label: "使用的分支" + description: "请选择您正在使用的版本分支" + options: + - main + - main-fix + - refactor + validations: + required: true +- type: input + attributes: + label: "具体版本号" + description: "请输入您使用的具体版本号" + placeholder: "例如:0.5.11、0.5.8" + validations: + required: true - type: textarea attributes: label: 遇到的问题 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 7a97f485e..69cb54710 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 @@ -168,13 +169,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/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 992b2f498..34c788d98 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -345,9 +345,12 @@ class ChatBot: platform="qq", ) - group_info = GroupInfo( - group_id=event.group_id, group_name=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 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/memory_system/memory.py b/src/plugins/memory_system/memory.py index 0952e0024..49beba5ba 100644 --- a/src/plugins/memory_system/memory.py +++ b/src/plugins/memory_system/memory.py @@ -882,8 +882,8 @@ class Hippocampus: matched_topics.add(input_topic) adjusted_sim = sim * penalty topic_similarities[input_topic] = max(topic_similarities.get(input_topic, 0), adjusted_sim) - logger.debug( - f"[激活] 主题「{input_topic}」-> 「{memory_topic}」(内容数: {content_count}, 相似度: {adjusted_sim:.3f})") + # logger.debug( + # f"[激活] 主题「{input_topic}」-> 「{memory_topic}」(内容数: {content_count}, 相似度: {adjusted_sim:.3f})") # 计算主题匹配率和平均相似度 topic_match = len(matched_topics) / len(identified_topics) diff --git a/src/plugins/models/utils_model.py b/src/plugins/models/utils_model.py index 893bece14..e2bc9f77c 100644 --- a/src/plugins/models/utils_model.py +++ b/src/plugins/models/utils_model.py @@ -165,8 +165,8 @@ class LLM_request: # 判断是否为流式 stream_mode = self.params.get("stream", False) logger_msg = "进入流式输出模式," if stream_mode else "" - logger.debug(f"{logger_msg}发送请求到URL: {api_url}") - logger.info(f"使用模型: {self.model_name}") + # logger.debug(f"{logger_msg}发送请求到URL: {api_url}") + # logger.info(f"使用模型: {self.model_name}") # 构建请求体 if image_base64: diff --git a/webui.py b/webui.py index a9041749b..e22f250ea 100644 --- a/webui.py +++ b/webui.py @@ -157,34 +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 = [] - if len(lst) != 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): @@ -228,7 +200,7 @@ def save_config_to_file(t_config_data): 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配置已保存" @@ -298,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 @@ -358,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 @@ -383,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 "群聊设置已保存" @@ -393,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-环境设置"): @@ -539,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, @@ -608,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结果" )] @@ -658,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): @@ -783,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): @@ -892,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( @@ -961,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-群组设置"): @@ -1093,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] ) @@ -1138,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