diff --git a/plugins/ai_auto_response/bot_ai.py b/plugins/ai_auto_response/bot_ai.py index b5cb914..016367f 100644 --- a/plugins/ai_auto_response/bot_ai.py +++ b/plugins/ai_auto_response/bot_ai.py @@ -1,5 +1,5 @@ import re -from datetime import datetime, time +from datetime import datetime, time, timedelta import toml import os @@ -10,31 +10,40 @@ class InterventionBot: self.config = {} if config_path and os.path.exists(config_path): self.config = toml.load(config_path) - - # 从配置中获取关键词 + + # 从配置中获取关键词和阈值 keywords = self.config.get("Keywords", {}) time_window = self.config.get("TimeWindow", {}) - + reply_threshold = self.config.get("ReplyThreshold", {}) + # 表情符号库 - self.emojis = keywords.get("emojis", ["[捂脸]", "[奸笑]", "[可怜]", "[擦汗]", "[发呆]", "[抠鼻]", "[破涕为笑]", "[旺柴]"]) + self.emojis = keywords.get("emojis", + ["[捂脸]", "[奸笑]", "[可怜]", "[擦汗]", "[发呆]", "[抠鼻]", "[破涕为笑]", "[旺柴]"]) # 话题关键词 - self.hot_topics = keywords.get("hot_topics", ["咖啡", "手机", "小米", "华为", "苹果", "价格", "流畅", "螺蛳粉", "外卖"]) - self.fish_keywords = keywords.get("fish_keywords", ["鱼缸", "鱼便", "红边", "造浪", "养鱼", "进货", "鳑鲏", "吸鳅"]) - self.tech_keywords = keywords.get("tech_keywords", ["MIUI", "鸿蒙", "iPhone", "安卓", "推送", "充电", "屏幕", "电池"]) - self.mechanism_keywords = keywords.get("mechanism_keywords", ["积分", "AI ", "功能列表", "黑丝", "打劫", "指令"]) + self.hot_topics = keywords.get("hot_topics", + ["咖啡", "手机", "小米", "华为", "苹果", "价格", "流畅", "螺蛳粉", "外卖"]) + self.fish_keywords = keywords.get("fish_keywords", + ["鱼缸", "鱼便", "红边", "造浪", "养鱼", "进货", "鳑鲏", "吸鳅"]) + self.tech_keywords = keywords.get("tech_keywords", + ["MIUI", "鸿蒙", "iPhone", "安卓", "推送", "充电", "屏幕", "电池"]) + self.mechanism_keywords = keywords.get("mechanism_keywords", + ["积分", "AI ", "功能列表", "黑丝", "打劫", "指令"]) self.news_keywords = keywords.get("news_keywords", ["新闻", "骨灰房", "法院", "判决", "住建局"]) - + # 早晨签到时间窗口 morning_start_hour = time_window.get("morning_start_hour", 8) morning_start_minute = time_window.get("morning_start_minute", 0) morning_end_hour = time_window.get("morning_end_hour", 8) morning_end_minute = time_window.get("morning_end_minute", 30) - self.morning_window = ( - time(morning_start_hour, morning_start_minute), + time(morning_start_hour, morning_start_minute), time(morning_end_hour, morning_end_minute) ) + # 回复阈值配置 + self.messages_per_minute_threshold = reply_threshold.get("messages_per_minute_threshold", 3) + self.analysis_window_minutes = reply_threshold.get("analysis_window_minutes", 5) + def is_morning_window(self, timestamp): """检查是否在早晨签到时间窗口""" try: @@ -45,6 +54,8 @@ class InterventionBot: def detect_topic(self, message): """检测消息所属话题类型""" + if not isinstance(message, str): + return None message_lower = message.lower() if any(keyword in message_lower for keyword in self.fish_keywords): return "fish" @@ -88,7 +99,40 @@ class InterventionBot: """规则7:猎奇或社会新闻反应""" return self.detect_topic(message) == "news" - def should_intervene(self, timestamp, message, messages): + def rule_high_reply_rate(self, timestamp, chat_log): + """规则8:高回复频率(每分钟消息数超过阈值)""" + try: + # 解析当前时间 + current_time = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S") + window_start = current_time - timedelta(minutes=self.analysis_window_minutes) + + # 计算时间窗口内的消息数 + recent_messages = [] + for msg in chat_log: + try: + msg_time = datetime.strptime(msg["timestamp"], "%Y-%m-%d %H:%M:%S") + if window_start <= msg_time <= current_time: + recent_messages.append(msg) + except (ValueError, KeyError, TypeError): + continue + + # 如果消息太少,不触发 + if len(recent_messages) < self.messages_per_minute_threshold: + return False + + # 计算消息频率 + messages_per_minute = len(recent_messages) / self.analysis_window_minutes + + # 记录日志,便于调试 + if messages_per_minute >= self.messages_per_minute_threshold: + print(f"[高频率检测] 当前消息频率: {messages_per_minute:.2f}/分钟,阈值: {self.messages_per_minute_threshold}/分钟") + + return messages_per_minute >= self.messages_per_minute_threshold + except Exception as e: + print(f"[高频率检测] 错误: {e}") + return False + + def should_intervene(self, timestamp, message, messages, chat_log): """判断是否需要介入""" rules = [ self.rule_morning_signin, @@ -97,20 +141,24 @@ class InterventionBot: self.rule_fish_discussion, self.rule_mechanism_interaction, self.rule_humor_tease, - self.rule_news_reaction + self.rule_news_reaction, + self.rule_high_reply_rate ] for rule in rules: if rule == self.rule_morning_signin: if rule(timestamp, messages): return True + elif rule == self.rule_high_reply_rate: + if rule(timestamp, chat_log): + return True elif rule(message, messages): return True return False - def process_message(self, timestamp, message, messages): + def process_message(self, timestamp, message, messages, chat_log): """处理单条消息,返回介入状态""" - if self.should_intervene(timestamp, message, messages): + if self.should_intervene(timestamp, message, messages, chat_log): return True return False @@ -122,7 +170,7 @@ class InterventionBot: for i, line in enumerate(chat_log): timestamp = line["timestamp"] message = line["message"] - intervention = self.process_message(timestamp, message, messages[:i + 1]) + intervention = self.process_message(timestamp, message, messages[:i + 1], chat_log) results.append({ "timestamp": timestamp, "message": message, diff --git a/plugins/ai_auto_response/config.toml b/plugins/ai_auto_response/config.toml index 6666974..1cbbe22 100644 --- a/plugins/ai_auto_response/config.toml +++ b/plugins/ai_auto_response/config.toml @@ -18,4 +18,10 @@ news_keywords = ["新闻", "骨灰房", "法院", "判决", "住建局"] morning_start_hour = 8 morning_start_minute = 0 morning_end_hour = 8 -morning_end_minute = 30 \ No newline at end of file +morning_end_minute = 30 + +[ReplyThreshold] +# 每分钟消息数阈值,超过此值将触发AI介入 +messages_per_minute_threshold = 3 +# 分析窗口大小(分钟) +analysis_window_minutes = 5 \ No newline at end of file diff --git a/plugins/ai_auto_response/main.py b/plugins/ai_auto_response/main.py index 00402ca..1e41551 100644 --- a/plugins/ai_auto_response/main.py +++ b/plugins/ai_auto_response/main.py @@ -61,12 +61,12 @@ class AIAutoResponsePlugin(MessagePluginInterface): # 加载配置 config_path = os.path.join(os.path.dirname(__file__), "config.toml") - self.enable = self._config.get("AIAutoResponse", {}).get("enable", True) + self.enable = self._config.get("enable", True) + self._commands = ["ai介入", "ai对话", "ai自动回复"] # 从配置中获取DIFY API密钥 - self.dify_api_key = self._config.get("AIAutoResponse", {}).get("dify_api_key", "") - self.dify_api_url = self._config.get("AIAutoResponse", {}).get("dify_api_url", - "http://192.168.2.240/v1/chat-messages") + self.dify_api_key = self._config.get("dify_api_key", "") + self.dify_api_url = self._config.get("dify_api_url", "http://192.168.2.240/v1/chat-messages") # 初始化介入机器人 self.intervention_bot = InterventionBot(config_path) @@ -103,11 +103,14 @@ class AIAutoResponsePlugin(MessagePluginInterface): self.group_messages[roomid] = [] # 添加新消息 - self.group_messages[roomid].append({ + current_message = { "timestamp": message.get("timestamp", ""), "message": content, "sender": message.get("sender", "") - }) + } + + # 添加新消息 + self.group_messages[roomid].append(current_message) # 限制消息数量 if len(self.group_messages[roomid]) > self.max_messages: @@ -117,7 +120,8 @@ class AIAutoResponsePlugin(MessagePluginInterface): messages = [msg["message"] for msg in self.group_messages[roomid]] timestamp = message.get("timestamp", "") - return self.intervention_bot.should_intervene(timestamp, content, messages) + # 传递完整的聊天记录给should_intervene方法 + return self.intervention_bot.should_intervene(timestamp, content, messages, self.group_messages[roomid]) return False @@ -136,6 +140,10 @@ class AIAutoResponsePlugin(MessagePluginInterface): # 获取最近的消息 messages = [msg["message"] for msg in self.group_messages[roomid]] timestamp = message.get("timestamp", "") + + # 记录触发原因 + if self.intervention_bot.rule_high_reply_rate(timestamp, self.group_messages[roomid]): + self.LOG.info(f"[{roomid}] 触发高频率回复规则,准备生成回复") # 生成回复 response = self._generate_response_with_dify(content, messages) @@ -143,6 +151,8 @@ class AIAutoResponsePlugin(MessagePluginInterface): # 发送回复 await bot.send_text_message(roomid, response, sender) return True, "自动回复成功" + else: + return False, "生成回复失败" except Exception as e: self.LOG.error(f"处理AI自动对话出错: {e}")