From 6bbd94373ac49dd94cbbc586b5643d8bdb57507d Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 4 Apr 2025 00:52:44 +0800 Subject: [PATCH] =?UTF-8?q?better=EF=BC=9A=E7=8E=B0=E5=9C=A8=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E6=97=A0=E9=99=90=E5=88=B6=E7=9A=84=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E8=A1=A8=E6=83=85=E5=8C=85=EF=BC=8C=E6=8B=A5=E6=9C=89=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/emoji_manager.py | 118 ++++++++++++++++++++++++++++- src/plugins/chat/message_sender.py | 6 +- src/plugins/config/config.py | 5 ++ template/bot_config_template.toml | 8 +- 4 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py index 279dfb464..6c41d9c78 100644 --- a/src/plugins/chat/emoji_manager.py +++ b/src/plugins/chat/emoji_manager.py @@ -39,12 +39,28 @@ class EmojiManager: model=global_config.llm_emotion_judge, max_tokens=600, temperature=0.8, request_type="emoji" ) # 更高的温度,更少的token(后续可以根据情绪来调整温度) + self.emoji_num = 0 + self.emoji_num_max = global_config.max_emoji_num + self.emoji_num_max_reach_deletion = global_config.max_reach_deletion + logger.info("启动表情包管理器") def _ensure_emoji_dir(self): """确保表情存储目录存在""" os.makedirs(self.EMOJI_DIR, exist_ok=True) + def _update_emoji_count(self): + """更新表情包数量统计 + + 检查数据库中的表情包数量并更新到 self.emoji_num + """ + try: + self._ensure_db() + self.emoji_num = db.emoji.count_documents({}) + logger.info(f"[统计] 当前表情包数量: {self.emoji_num}") + except Exception as e: + logger.error(f"[错误] 更新表情包数量失败: {str(e)}") + def initialize(self): """初始化数据库连接和表情目录""" if not self._initialized: @@ -52,6 +68,8 @@ class EmojiManager: self._ensure_emoji_collection() self._ensure_emoji_dir() self._initialized = True + # 更新表情包数量 + self._update_emoji_count() # 启动时执行一次完整性检查 self.check_emoji_file_integrity() except Exception: @@ -344,8 +362,19 @@ class EmojiManager: """定期扫描新表情包""" while True: logger.info("[扫描] 开始扫描新表情包...") - await self.scan_new_emojis() - await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) + if (self.emoji_num > self.emoji_num_max): + logger.warning(f"[警告] 表情包数量超过最大限制: {self.emoji_num} > {self.emoji_num_max},跳过注册") + if not global_config.max_reach_deletion: + logger.warning(f"表情包数量超过最大限制,终止注册") + break + else: + logger.warning(f"表情包数量超过最大限制,开始删除表情包") + self.check_emoji_file_full() + else: + await self.scan_new_emojis() + await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) + + def check_emoji_file_integrity(self): """检查表情包文件完整性 @@ -418,8 +447,93 @@ class EmojiManager: logger.error(f"[错误] 检查表情包完整性失败: {str(e)}") logger.error(traceback.format_exc()) + def check_emoji_file_full(self): + """检查表情包文件是否完整,如果数量超出限制且允许删除,则删除多余的表情包 + + 删除规则: + 1. 优先删除创建时间更早的表情包 + 2. 优先删除使用次数少的表情包,但使用次数多的也有小概率被删除 + """ + try: + self._ensure_db() + # 更新表情包数量 + self._update_emoji_count() + + # 检查是否超出限制 + if self.emoji_num <= self.emoji_num_max: + return + + # 如果超出限制但不允许删除,则只记录警告 + if not global_config.max_reach_deletion: + logger.warning(f"[警告] 表情包数量({self.emoji_num})超出限制({self.emoji_num_max}),但未开启自动删除") + return + + # 计算需要删除的数量 + delete_count = self.emoji_num - self.emoji_num_max + logger.info(f"[清理] 需要删除 {delete_count} 个表情包") + + # 获取所有表情包,按时间戳升序(旧的在前)排序 + all_emojis = list(db.emoji.find().sort([("timestamp", 1)])) + + # 计算权重:使用次数越多,被删除的概率越小 + weights = [] + max_usage = max((emoji.get("usage_count", 0) for emoji in all_emojis), default=1) + for emoji in all_emojis: + usage_count = emoji.get("usage_count", 0) + # 使用指数衰减函数计算权重,使用次数越多权重越小 + weight = 1.0 / (1.0 + usage_count / max(1, max_usage)) + weights.append(weight) + + # 根据权重随机选择要删除的表情包 + to_delete = [] + remaining_indices = list(range(len(all_emojis))) + + while len(to_delete) < delete_count and remaining_indices: + # 计算当前剩余表情包的权重 + current_weights = [weights[i] for i in remaining_indices] + # 归一化权重 + total_weight = sum(current_weights) + if total_weight == 0: + break + normalized_weights = [w/total_weight for w in current_weights] + + # 随机选择一个表情包 + selected_idx = random.choices(remaining_indices, weights=normalized_weights, k=1)[0] + to_delete.append(all_emojis[selected_idx]) + remaining_indices.remove(selected_idx) + + # 删除选中的表情包 + deleted_count = 0 + for emoji in to_delete: + try: + # 删除文件 + if "path" in emoji and os.path.exists(emoji["path"]): + os.remove(emoji["path"]) + logger.info(f"[删除] 文件: {emoji['path']} (使用次数: {emoji.get('usage_count', 0)})") + + # 删除数据库记录 + db.emoji.delete_one({"_id": emoji["_id"]}) + deleted_count += 1 + + # 同时从images集合中删除 + if "hash" in emoji: + db.images.delete_one({"hash": emoji["hash"]}) + + except Exception as e: + logger.error(f"[错误] 删除表情包失败: {str(e)}") + continue + + # 更新表情包数量 + self._update_emoji_count() + logger.success(f"[清理] 已删除 {deleted_count} 个表情包,当前数量: {self.emoji_num}") + + except Exception as e: + logger.error(f"[错误] 检查表情包数量失败: {str(e)}") + async def start_periodic_check(self): + """定期检查表情包完整性和数量""" while True: + self.check_emoji_file_full() self.check_emoji_file_integrity() await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index 70b5cf84d..9f30b63ed 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -85,16 +85,16 @@ class MessageContainer: self.max_size = max_size self.messages = [] self.last_send_time = 0 - self.thinking_timeout = 20 # 思考等待超时时间(秒) + self.thinking_wait_timeout = 20 # 思考等待超时时间(秒) def get_timeout_messages(self) -> List[MessageSending]: - """获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序""" + """获取所有超时的Message_Sending对象(思考时间超过20秒),按thinking_start_time排序""" current_time = time.time() timeout_messages = [] for msg in self.messages: if isinstance(msg, MessageSending): - if current_time - msg.thinking_start_time > self.thinking_timeout: + if current_time - msg.thinking_start_time > self.thinking_wait_timeout: timeout_messages.append(msg) # 按thinking_start_time排序,时间早的在前面 diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index 6db225a4b..ac0e7a264 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -182,6 +182,8 @@ class BotConfig: # MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率 # emoji + max_emoji_num: int = 200 # 表情包最大数量 + max_reach_deletion: bool = True # 开启则在达到最大数量时删除表情包,关闭则不会继续收集表情包 EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟) EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟) EMOJI_SAVE: bool = True # 偷表情包 @@ -361,6 +363,9 @@ class BotConfig: config.EMOJI_CHECK_PROMPT = emoji_config.get("check_prompt", config.EMOJI_CHECK_PROMPT) config.EMOJI_SAVE = emoji_config.get("auto_save", config.EMOJI_SAVE) config.EMOJI_CHECK = emoji_config.get("enable_check", config.EMOJI_CHECK) + if config.INNER_VERSION in SpecifierSet(">=1.1.1"): + config.max_emoji_num = emoji_config.get("max_emoji_num", config.max_emoji_num) + config.max_reach_deletion = emoji_config.get("max_reach_deletion", config.max_reach_deletion) def bot(parent: dict): # 机器人基础配置 diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 2372b10b1..3b094a8b3 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "1.1.0" +version = "1.1.1" #以下是给开发人员阅读的,一般用户不需要阅读 @@ -93,8 +93,10 @@ emoji_response_penalty = 0.1 # 表情包回复惩罚系数,设为0为不回复 [emoji] -check_interval = 15 # 检查破损表情包的时间间隔(分钟) -register_interval = 60 # 注册表情包的时间间隔(分钟) +max_emoji_num = 120 # 表情包最大数量 +max_reach_deletion = true # 开启则在达到最大数量时删除表情包,关闭则不会继续收集表情包 +check_interval = 30 # 检查破损表情包的时间间隔(分钟) +register_interval = 30 # 注册表情包的时间间隔(分钟) auto_save = true # 是否保存表情包和图片 enable_check = false # 是否启用表情包过滤 check_prompt = "符合公序良俗" # 表情包过滤要求