ruff reformatted
This commit is contained in:
@@ -12,5 +12,5 @@ __all__ = [
|
||||
"chat_manager",
|
||||
"message_manager",
|
||||
"MessageStorage",
|
||||
"auto_speak_manager"
|
||||
"auto_speak_manager",
|
||||
]
|
||||
|
||||
@@ -40,14 +40,14 @@ class ChatBot:
|
||||
async def _create_PFC_chat(self, message: MessageRecv):
|
||||
try:
|
||||
chat_id = str(message.chat_stream.stream_id)
|
||||
|
||||
|
||||
if global_config.enable_pfc_chatting:
|
||||
# 获取或创建对话实例
|
||||
conversation = await Conversation.get_instance(chat_id)
|
||||
if conversation is None:
|
||||
logger.error(f"创建或获取对话实例失败: {chat_id}")
|
||||
return
|
||||
|
||||
|
||||
# 如果是新创建的实例,启动对话系统
|
||||
if conversation.state == ConversationState.INIT:
|
||||
asyncio.create_task(conversation.start())
|
||||
@@ -71,16 +71,16 @@ class ChatBot:
|
||||
- 包含思维流状态管理
|
||||
- 在回复前进行观察和状态更新
|
||||
- 回复后更新思维流状态
|
||||
|
||||
|
||||
2. reasoning模式:使用推理系统进行回复
|
||||
- 直接使用意愿管理器计算回复概率
|
||||
- 没有思维流相关的状态管理
|
||||
- 更简单直接的回复逻辑
|
||||
|
||||
|
||||
3. pfc_chatting模式:仅进行消息处理
|
||||
- 不进行任何回复
|
||||
- 只处理和存储消息
|
||||
|
||||
|
||||
所有模式都包含:
|
||||
- 消息过滤
|
||||
- 记忆激活
|
||||
@@ -98,7 +98,7 @@ class ChatBot:
|
||||
if userinfo.user_id in global_config.ban_user_id:
|
||||
logger.debug(f"用户{userinfo.user_id}被禁止回复")
|
||||
return
|
||||
|
||||
|
||||
if global_config.enable_pfc_chatting:
|
||||
try:
|
||||
if groupinfo is None and global_config.enable_friend_chat:
|
||||
@@ -127,7 +127,7 @@ class ChatBot:
|
||||
logger.error(f"处理PFC消息失败: {e}")
|
||||
else:
|
||||
if groupinfo is None and global_config.enable_friend_chat:
|
||||
# 私聊处理流程
|
||||
# 私聊处理流程
|
||||
# await self._handle_private_chat(message)
|
||||
if global_config.response_mode == "heart_flow":
|
||||
await self.think_flow_chat.process_message(message_data)
|
||||
|
||||
@@ -38,11 +38,11 @@ class EmojiManager:
|
||||
self.llm_emotion_judge = LLM_request(
|
||||
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):
|
||||
@@ -51,7 +51,7 @@ class EmojiManager:
|
||||
|
||||
def _update_emoji_count(self):
|
||||
"""更新表情包数量统计
|
||||
|
||||
|
||||
检查数据库中的表情包数量并更新到 self.emoji_num
|
||||
"""
|
||||
try:
|
||||
@@ -376,7 +376,6 @@ class EmojiManager:
|
||||
|
||||
except Exception:
|
||||
logger.exception("[错误] 扫描表情包失败")
|
||||
|
||||
|
||||
def check_emoji_file_integrity(self):
|
||||
"""检查表情包文件完整性
|
||||
@@ -451,7 +450,7 @@ class EmojiManager:
|
||||
|
||||
def check_emoji_file_full(self):
|
||||
"""检查表情包文件是否完整,如果数量超出限制且允许删除,则删除多余的表情包
|
||||
|
||||
|
||||
删除规则:
|
||||
1. 优先删除创建时间更早的表情包
|
||||
2. 优先删除使用次数少的表情包,但使用次数多的也有小概率被删除
|
||||
@@ -460,23 +459,23 @@ class EmojiManager:
|
||||
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)
|
||||
@@ -485,11 +484,11 @@ class EmojiManager:
|
||||
# 使用指数衰减函数计算权重,使用次数越多权重越小
|
||||
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]
|
||||
@@ -497,13 +496,13 @@ class EmojiManager:
|
||||
total_weight = sum(current_weights)
|
||||
if total_weight == 0:
|
||||
break
|
||||
normalized_weights = [w/total_weight for w in current_weights]
|
||||
|
||||
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:
|
||||
@@ -512,26 +511,26 @@ class EmojiManager:
|
||||
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_register(self):
|
||||
"""定期检查表情包完整性和数量"""
|
||||
while True:
|
||||
@@ -542,7 +541,7 @@ class EmojiManager:
|
||||
logger.info("[扫描] 开始扫描新表情包...")
|
||||
if self.emoji_num < self.emoji_num_max:
|
||||
await self.scan_new_emojis()
|
||||
if (self.emoji_num > self.emoji_num_max):
|
||||
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("表情包数量超过最大限制,终止注册")
|
||||
@@ -551,7 +550,7 @@ class EmojiManager:
|
||||
logger.warning("表情包数量超过最大限制,开始删除表情包")
|
||||
self.check_emoji_file_full()
|
||||
await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60)
|
||||
|
||||
|
||||
async def delete_all_images(self):
|
||||
"""删除 data/image 目录下的所有文件"""
|
||||
try:
|
||||
@@ -559,10 +558,10 @@ class EmojiManager:
|
||||
if not os.path.exists(image_dir):
|
||||
logger.warning(f"[警告] 目录不存在: {image_dir}")
|
||||
return
|
||||
|
||||
|
||||
deleted_count = 0
|
||||
failed_count = 0
|
||||
|
||||
|
||||
# 遍历目录下的所有文件
|
||||
for filename in os.listdir(image_dir):
|
||||
file_path = os.path.join(image_dir, filename)
|
||||
@@ -574,11 +573,12 @@ class EmojiManager:
|
||||
except Exception as e:
|
||||
failed_count += 1
|
||||
logger.error(f"[错误] 删除文件失败 {file_path}: {str(e)}")
|
||||
|
||||
|
||||
logger.success(f"[清理] 已删除 {deleted_count} 个文件,失败 {failed_count} 个")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[错误] 删除图片目录失败: {str(e)}")
|
||||
|
||||
|
||||
# 创建全局单例
|
||||
emoji_manager = EmojiManager()
|
||||
|
||||
@@ -13,9 +13,10 @@ from ..config.config import global_config
|
||||
|
||||
logger = get_module_logger("message_buffer")
|
||||
|
||||
|
||||
@dataclass
|
||||
class CacheMessages:
|
||||
message: MessageRecv
|
||||
message: MessageRecv
|
||||
cache_determination: asyncio.Event = field(default_factory=asyncio.Event) # 判断缓冲是否产生结果
|
||||
result: str = "U"
|
||||
|
||||
@@ -25,7 +26,7 @@ class MessageBuffer:
|
||||
self.buffer_pool: Dict[str, OrderedDict[str, CacheMessages]] = {}
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
def get_person_id_(self, platform:str, user_id:str, group_info:GroupInfo):
|
||||
def get_person_id_(self, platform: str, user_id: str, group_info: GroupInfo):
|
||||
"""获取唯一id"""
|
||||
if group_info:
|
||||
group_id = group_info.group_id
|
||||
@@ -34,16 +35,17 @@ class MessageBuffer:
|
||||
key = f"{platform}_{user_id}_{group_id}"
|
||||
return hashlib.md5(key.encode()).hexdigest()
|
||||
|
||||
async def start_caching_messages(self, message:MessageRecv):
|
||||
async def start_caching_messages(self, message: MessageRecv):
|
||||
"""添加消息,启动缓冲"""
|
||||
if not global_config.message_buffer:
|
||||
person_id = person_info_manager.get_person_id(message.message_info.user_info.platform,
|
||||
message.message_info.user_info.user_id)
|
||||
person_id = person_info_manager.get_person_id(
|
||||
message.message_info.user_info.platform, message.message_info.user_info.user_id
|
||||
)
|
||||
asyncio.create_task(self.save_message_interval(person_id, message.message_info))
|
||||
return
|
||||
person_id_ = self.get_person_id_(message.message_info.platform,
|
||||
message.message_info.user_info.user_id,
|
||||
message.message_info.group_info)
|
||||
person_id_ = self.get_person_id_(
|
||||
message.message_info.platform, message.message_info.user_info.user_id, message.message_info.group_info
|
||||
)
|
||||
|
||||
async with self.lock:
|
||||
if person_id_ not in self.buffer_pool:
|
||||
@@ -64,25 +66,24 @@ class MessageBuffer:
|
||||
break
|
||||
elif msg.result == "F":
|
||||
recent_F_count += 1
|
||||
|
||||
|
||||
# 判断条件:最近T之后有超过3-5条F
|
||||
if (recent_F_count >= random.randint(3, 5)):
|
||||
if recent_F_count >= random.randint(3, 5):
|
||||
new_msg = CacheMessages(message=message, result="T")
|
||||
new_msg.cache_determination.set()
|
||||
self.buffer_pool[person_id_][message.message_info.message_id] = new_msg
|
||||
logger.debug(f"快速处理消息(已堆积{recent_F_count}条F): {message.message_info.message_id}")
|
||||
return
|
||||
|
||||
|
||||
# 添加新消息
|
||||
self.buffer_pool[person_id_][message.message_info.message_id] = CacheMessages(message=message)
|
||||
|
||||
|
||||
# 启动3秒缓冲计时器
|
||||
person_id = person_info_manager.get_person_id(message.message_info.user_info.platform,
|
||||
message.message_info.user_info.user_id)
|
||||
person_id = person_info_manager.get_person_id(
|
||||
message.message_info.user_info.platform, message.message_info.user_info.user_id
|
||||
)
|
||||
asyncio.create_task(self.save_message_interval(person_id, message.message_info))
|
||||
asyncio.create_task(self._debounce_processor(person_id_,
|
||||
message.message_info.message_id,
|
||||
person_id))
|
||||
asyncio.create_task(self._debounce_processor(person_id_, message.message_info.message_id, person_id))
|
||||
|
||||
async def _debounce_processor(self, person_id_: str, message_id: str, person_id: str):
|
||||
"""等待3秒无新消息"""
|
||||
@@ -92,36 +93,33 @@ class MessageBuffer:
|
||||
return
|
||||
interval_time = max(0.5, int(interval_time) / 1000)
|
||||
await asyncio.sleep(interval_time)
|
||||
|
||||
|
||||
async with self.lock:
|
||||
if (person_id_ not in self.buffer_pool or
|
||||
message_id not in self.buffer_pool[person_id_]):
|
||||
if person_id_ not in self.buffer_pool or message_id not in self.buffer_pool[person_id_]:
|
||||
logger.debug(f"消息已被清理,msgid: {message_id}")
|
||||
return
|
||||
|
||||
|
||||
cache_msg = self.buffer_pool[person_id_][message_id]
|
||||
if cache_msg.result == "U":
|
||||
cache_msg.result = "T"
|
||||
cache_msg.cache_determination.set()
|
||||
|
||||
|
||||
async def query_buffer_result(self, message:MessageRecv) -> bool:
|
||||
async def query_buffer_result(self, message: MessageRecv) -> bool:
|
||||
"""查询缓冲结果,并清理"""
|
||||
if not global_config.message_buffer:
|
||||
return True
|
||||
person_id_ = self.get_person_id_(message.message_info.platform,
|
||||
message.message_info.user_info.user_id,
|
||||
message.message_info.group_info)
|
||||
|
||||
|
||||
person_id_ = self.get_person_id_(
|
||||
message.message_info.platform, message.message_info.user_info.user_id, message.message_info.group_info
|
||||
)
|
||||
|
||||
async with self.lock:
|
||||
user_msgs = self.buffer_pool.get(person_id_, {})
|
||||
cache_msg = user_msgs.get(message.message_info.message_id)
|
||||
|
||||
|
||||
if not cache_msg:
|
||||
logger.debug(f"查询异常,消息不存在,msgid: {message.message_info.message_id}")
|
||||
return False # 消息不存在或已清理
|
||||
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(cache_msg.cache_determination.wait(), timeout=10)
|
||||
result = cache_msg.result == "T"
|
||||
@@ -144,9 +142,8 @@ class MessageBuffer:
|
||||
keep_msgs[msg_id] = msg
|
||||
elif msg.result == "F":
|
||||
# 收集F消息的文本内容
|
||||
if (hasattr(msg.message, 'processed_plain_text')
|
||||
and msg.message.processed_plain_text):
|
||||
if msg.message.message_segment.type == "text":
|
||||
if hasattr(msg.message, "processed_plain_text") and msg.message.processed_plain_text:
|
||||
if msg.message.message_segment.type == "text":
|
||||
combined_text.append(msg.message.processed_plain_text)
|
||||
elif msg.message.message_segment.type != "text":
|
||||
is_update = False
|
||||
@@ -157,20 +154,20 @@ class MessageBuffer:
|
||||
if combined_text and combined_text[0] != message.processed_plain_text and is_update:
|
||||
if type == "text":
|
||||
message.processed_plain_text = "".join(combined_text)
|
||||
logger.debug(f"整合了{len(combined_text)-1}条F消息的内容到当前消息")
|
||||
logger.debug(f"整合了{len(combined_text) - 1}条F消息的内容到当前消息")
|
||||
elif type == "emoji":
|
||||
combined_text.pop()
|
||||
message.processed_plain_text = "".join(combined_text)
|
||||
message.is_emoji = False
|
||||
logger.debug(f"整合了{len(combined_text)-1}条F消息的内容,覆盖当前emoji消息")
|
||||
logger.debug(f"整合了{len(combined_text) - 1}条F消息的内容,覆盖当前emoji消息")
|
||||
|
||||
self.buffer_pool[person_id_] = keep_msgs
|
||||
return result
|
||||
except asyncio.TimeoutError:
|
||||
logger.debug(f"查询超时消息id: {message.message_info.message_id}")
|
||||
return False
|
||||
|
||||
async def save_message_interval(self, person_id:str, message:BaseMessageInfo):
|
||||
|
||||
async def save_message_interval(self, person_id: str, message: BaseMessageInfo):
|
||||
message_interval_list = await person_info_manager.get_value(person_id, "msg_interval_list")
|
||||
now_time_ms = int(round(time.time() * 1000))
|
||||
if len(message_interval_list) < 1000:
|
||||
@@ -179,12 +176,12 @@ class MessageBuffer:
|
||||
message_interval_list.pop(0)
|
||||
message_interval_list.append(now_time_ms)
|
||||
data = {
|
||||
"platform" : message.platform,
|
||||
"user_id" : message.user_info.user_id,
|
||||
"nickname" : message.user_info.user_nickname,
|
||||
"konw_time" : int(time.time())
|
||||
"platform": message.platform,
|
||||
"user_id": message.user_info.user_id,
|
||||
"nickname": message.user_info.user_nickname,
|
||||
"konw_time": int(time.time()),
|
||||
}
|
||||
await person_info_manager.update_one_field(person_id, "msg_interval_list", message_interval_list, data)
|
||||
|
||||
|
||||
message_buffer = MessageBuffer()
|
||||
message_buffer = MessageBuffer()
|
||||
|
||||
@@ -68,7 +68,8 @@ class Message_Sender:
|
||||
typing_time = calculate_typing_time(
|
||||
input_string=message.processed_plain_text,
|
||||
thinking_start_time=message.thinking_start_time,
|
||||
is_emoji=message.is_emoji)
|
||||
is_emoji=message.is_emoji,
|
||||
)
|
||||
logger.debug(f"{message.processed_plain_text},{typing_time},计算输入时间结束")
|
||||
await asyncio.sleep(typing_time)
|
||||
logger.debug(f"{message.processed_plain_text},{typing_time},等待输入时间结束")
|
||||
@@ -227,7 +228,7 @@ class MessageManager:
|
||||
await message_earliest.process()
|
||||
|
||||
# print(f"message_earliest.thinking_start_tim22222e:{message_earliest.thinking_start_time}")
|
||||
|
||||
|
||||
await message_sender.send_message(message_earliest)
|
||||
|
||||
await self.storage.store_message(message_earliest, message_earliest.chat_stream)
|
||||
|
||||
@@ -56,14 +56,13 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> bool:
|
||||
logger.info("被@,回复概率设置为100%")
|
||||
else:
|
||||
if not is_mentioned:
|
||||
|
||||
# 判断是否被回复
|
||||
if re.match(f"回复[\s\S]*?\({global_config.BOT_QQ}\)的消息,说:", message.processed_plain_text):
|
||||
is_mentioned = True
|
||||
|
||||
|
||||
# 判断内容中是否被提及
|
||||
message_content = re.sub(r'\@[\s\S]*?((\d+))','', message.processed_plain_text)
|
||||
message_content = re.sub(r'回复[\s\S]*?\((\d+)\)的消息,说: ','', message_content)
|
||||
message_content = re.sub(r"\@[\s\S]*?((\d+))", "", message.processed_plain_text)
|
||||
message_content = re.sub(r"回复[\s\S]*?\((\d+)\)的消息,说: ", "", message_content)
|
||||
for keyword in keywords:
|
||||
if keyword in message_content:
|
||||
is_mentioned = True
|
||||
@@ -359,7 +358,13 @@ def process_llm_response(text: str) -> List[str]:
|
||||
return sentences
|
||||
|
||||
|
||||
def calculate_typing_time(input_string: str, thinking_start_time: float, chinese_time: float = 0.2, english_time: float = 0.1, is_emoji: bool = False) -> float:
|
||||
def calculate_typing_time(
|
||||
input_string: str,
|
||||
thinking_start_time: float,
|
||||
chinese_time: float = 0.2,
|
||||
english_time: float = 0.1,
|
||||
is_emoji: bool = False,
|
||||
) -> float:
|
||||
"""
|
||||
计算输入字符串所需的时间,中文和英文字符有不同的输入时间
|
||||
input_string (str): 输入的字符串
|
||||
@@ -393,19 +398,18 @@ def calculate_typing_time(input_string: str, thinking_start_time: float, chinese
|
||||
total_time += chinese_time
|
||||
else: # 其他字符(如英文)
|
||||
total_time += english_time
|
||||
|
||||
|
||||
|
||||
if is_emoji:
|
||||
total_time = 1
|
||||
|
||||
|
||||
if time.time() - thinking_start_time > 10:
|
||||
total_time = 1
|
||||
|
||||
|
||||
# print(f"thinking_start_time:{thinking_start_time}")
|
||||
# print(f"nowtime:{time.time()}")
|
||||
# print(f"nowtime - thinking_start_time:{time.time() - thinking_start_time}")
|
||||
# print(f"{total_time}")
|
||||
|
||||
|
||||
return total_time # 加上回车时间
|
||||
|
||||
|
||||
@@ -535,39 +539,32 @@ def count_messages_between(start_time: float, end_time: float, stream_id: str) -
|
||||
try:
|
||||
# 获取开始时间之前最新的一条消息
|
||||
start_message = db.messages.find_one(
|
||||
{
|
||||
"chat_id": stream_id,
|
||||
"time": {"$lte": start_time}
|
||||
},
|
||||
sort=[("time", -1), ("_id", -1)] # 按时间倒序,_id倒序(最后插入的在前)
|
||||
{"chat_id": stream_id, "time": {"$lte": start_time}},
|
||||
sort=[("time", -1), ("_id", -1)], # 按时间倒序,_id倒序(最后插入的在前)
|
||||
)
|
||||
|
||||
|
||||
# 获取结束时间最近的一条消息
|
||||
# 先找到结束时间点的所有消息
|
||||
end_time_messages = list(db.messages.find(
|
||||
{
|
||||
"chat_id": stream_id,
|
||||
"time": {"$lte": end_time}
|
||||
},
|
||||
sort=[("time", -1)] # 先按时间倒序
|
||||
).limit(10)) # 限制查询数量,避免性能问题
|
||||
|
||||
end_time_messages = list(
|
||||
db.messages.find(
|
||||
{"chat_id": stream_id, "time": {"$lte": end_time}},
|
||||
sort=[("time", -1)], # 先按时间倒序
|
||||
).limit(10)
|
||||
) # 限制查询数量,避免性能问题
|
||||
|
||||
if not end_time_messages:
|
||||
logger.warning(f"未找到结束时间 {end_time} 之前的消息")
|
||||
return 0, 0
|
||||
|
||||
|
||||
# 找到最大时间
|
||||
max_time = end_time_messages[0]["time"]
|
||||
# 在最大时间的消息中找最后插入的(_id最大的)
|
||||
end_message = max(
|
||||
[msg for msg in end_time_messages if msg["time"] == max_time],
|
||||
key=lambda x: x["_id"]
|
||||
)
|
||||
|
||||
end_message = max([msg for msg in end_time_messages if msg["time"] == max_time], key=lambda x: x["_id"])
|
||||
|
||||
if not start_message:
|
||||
logger.warning(f"未找到开始时间 {start_time} 之前的消息")
|
||||
return 0, 0
|
||||
|
||||
|
||||
# 调试输出
|
||||
# print("\n=== 消息范围信息 ===")
|
||||
# print("Start message:", {
|
||||
@@ -587,20 +584,16 @@ def count_messages_between(start_time: float, end_time: float, stream_id: str) -
|
||||
# 如果结束消息的时间等于开始时间,返回0
|
||||
if end_message["time"] == start_message["time"]:
|
||||
return 0, 0
|
||||
|
||||
|
||||
# 获取并打印这个时间范围内的所有消息
|
||||
# print("\n=== 时间范围内的所有消息 ===")
|
||||
all_messages = list(db.messages.find(
|
||||
{
|
||||
"chat_id": stream_id,
|
||||
"time": {
|
||||
"$gte": start_message["time"],
|
||||
"$lte": end_message["time"]
|
||||
}
|
||||
},
|
||||
sort=[("time", 1), ("_id", 1)] # 按时间正序,_id正序
|
||||
))
|
||||
|
||||
all_messages = list(
|
||||
db.messages.find(
|
||||
{"chat_id": stream_id, "time": {"$gte": start_message["time"], "$lte": end_message["time"]}},
|
||||
sort=[("time", 1), ("_id", 1)], # 按时间正序,_id正序
|
||||
)
|
||||
)
|
||||
|
||||
count = 0
|
||||
total_length = 0
|
||||
for msg in all_messages:
|
||||
@@ -615,10 +608,10 @@ def count_messages_between(start_time: float, end_time: float, stream_id: str) -
|
||||
# "text_length": text_length,
|
||||
# "_id": str(msg.get("_id"))
|
||||
# })
|
||||
|
||||
|
||||
# 如果时间不同,需要把end_message本身也计入
|
||||
return count - 1, total_length
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"计算消息数量时出错: {str(e)}")
|
||||
return 0, 0
|
||||
|
||||
@@ -239,13 +239,13 @@ class ImageManager:
|
||||
# 解码base64
|
||||
gif_data = base64.b64decode(gif_base64)
|
||||
gif = Image.open(io.BytesIO(gif_data))
|
||||
|
||||
|
||||
# 收集所有帧
|
||||
frames = []
|
||||
try:
|
||||
while True:
|
||||
gif.seek(len(frames))
|
||||
frame = gif.convert('RGB')
|
||||
frame = gif.convert("RGB")
|
||||
frames.append(frame.copy())
|
||||
except EOFError:
|
||||
pass
|
||||
@@ -264,18 +264,19 @@ class ImageManager:
|
||||
|
||||
# 获取单帧的尺寸
|
||||
frame_width, frame_height = selected_frames[0].size
|
||||
|
||||
|
||||
# 计算目标尺寸,保持宽高比
|
||||
target_height = 200 # 固定高度
|
||||
target_width = int((target_height / frame_height) * frame_width)
|
||||
|
||||
|
||||
# 调整所有帧的大小
|
||||
resized_frames = [frame.resize((target_width, target_height), Image.Resampling.LANCZOS)
|
||||
for frame in selected_frames]
|
||||
resized_frames = [
|
||||
frame.resize((target_width, target_height), Image.Resampling.LANCZOS) for frame in selected_frames
|
||||
]
|
||||
|
||||
# 创建拼接图像
|
||||
total_width = target_width * len(resized_frames)
|
||||
combined_image = Image.new('RGB', (total_width, target_height))
|
||||
combined_image = Image.new("RGB", (total_width, target_height))
|
||||
|
||||
# 水平拼接图像
|
||||
for idx, frame in enumerate(resized_frames):
|
||||
@@ -283,11 +284,11 @@ class ImageManager:
|
||||
|
||||
# 转换为base64
|
||||
buffer = io.BytesIO()
|
||||
combined_image.save(buffer, format='JPEG', quality=85)
|
||||
result_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
||||
|
||||
combined_image.save(buffer, format="JPEG", quality=85)
|
||||
result_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
|
||||
|
||||
return result_base64
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"GIF转换失败: {str(e)}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user