Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
@@ -207,6 +207,9 @@ class VideoAnalyzer:
|
|||||||
"""检查视频是否已经分析过"""
|
"""检查视频是否已经分析过"""
|
||||||
try:
|
try:
|
||||||
async with get_db_session() as session:
|
async with get_db_session() as session:
|
||||||
|
if not session:
|
||||||
|
logger.warning("无法获取数据库会话,跳过视频存在性检查。")
|
||||||
|
return None
|
||||||
# 明确刷新会话以确保看到其他事务的最新提交
|
# 明确刷新会话以确保看到其他事务的最新提交
|
||||||
await session.expire_all()
|
await session.expire_all()
|
||||||
stmt = select(Videos).where(Videos.video_hash == video_hash)
|
stmt = select(Videos).where(Videos.video_hash == video_hash)
|
||||||
@@ -227,6 +230,9 @@ class VideoAnalyzer:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
async with get_db_session() as session:
|
async with get_db_session() as session:
|
||||||
|
if not session:
|
||||||
|
logger.warning("无法获取数据库会话,跳过视频结果存储。")
|
||||||
|
return None
|
||||||
# 只根据video_hash查找
|
# 只根据video_hash查找
|
||||||
stmt = select(Videos).where(Videos.video_hash == video_hash)
|
stmt = select(Videos).where(Videos.video_hash == video_hash)
|
||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
@@ -540,11 +546,14 @@ class VideoAnalyzer:
|
|||||||
# logger.info(f"✅ 多帧消息构建完成,包含{len(frames)}张图片")
|
# logger.info(f"✅ 多帧消息构建完成,包含{len(frames)}张图片")
|
||||||
|
|
||||||
# 获取模型信息和客户端
|
# 获取模型信息和客户端
|
||||||
model_info, api_provider, client = self.video_llm._select_model()
|
selection_result = self.video_llm._model_selector.select_best_available_model(set(), "response")
|
||||||
|
if not selection_result:
|
||||||
|
raise RuntimeError("无法为视频分析选择可用模型。")
|
||||||
|
model_info, api_provider, client = selection_result
|
||||||
# logger.info(f"使用模型: {model_info.name} 进行多帧分析")
|
# logger.info(f"使用模型: {model_info.name} 进行多帧分析")
|
||||||
|
|
||||||
# 直接执行多图片请求
|
# 直接执行多图片请求
|
||||||
api_response = await self.video_llm._execute_request(
|
api_response = await self.video_llm._executor.execute_request(
|
||||||
api_provider=api_provider,
|
api_provider=api_provider,
|
||||||
client=client,
|
client=client,
|
||||||
request_type=RequestType.RESPONSE,
|
request_type=RequestType.RESPONSE,
|
||||||
|
|||||||
@@ -461,11 +461,14 @@ class LegacyVideoAnalyzer:
|
|||||||
# logger.info(f"✅ 多帧消息构建完成,包含{len(frames)}张图片")
|
# logger.info(f"✅ 多帧消息构建完成,包含{len(frames)}张图片")
|
||||||
|
|
||||||
# 获取模型信息和客户端
|
# 获取模型信息和客户端
|
||||||
model_info, api_provider, client = self.video_llm._select_model()
|
selection_result = self.video_llm._model_selector.select_best_available_model(set(), "response")
|
||||||
|
if not selection_result:
|
||||||
|
raise RuntimeError("无法为视频分析选择可用模型 (legacy)。")
|
||||||
|
model_info, api_provider, client = selection_result
|
||||||
# logger.info(f"使用模型: {model_info.name} 进行多帧分析")
|
# logger.info(f"使用模型: {model_info.name} 进行多帧分析")
|
||||||
|
|
||||||
# 直接执行多图片请求
|
# 直接执行多图片请求
|
||||||
api_response = await self.video_llm._execute_request(
|
api_response = await self.video_llm._executor.execute_request(
|
||||||
api_provider=api_provider,
|
api_provider=api_provider,
|
||||||
client=client,
|
client=client,
|
||||||
request_type=RequestType.RESPONSE,
|
request_type=RequestType.RESPONSE,
|
||||||
|
|||||||
@@ -759,30 +759,38 @@ async def initialize_database():
|
|||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
|
async def get_db_session() -> AsyncGenerator[Optional[AsyncSession], None]:
|
||||||
"""异步数据库会话上下文管理器"""
|
"""
|
||||||
|
异步数据库会话上下文管理器。
|
||||||
|
在初始化失败时会yield None,调用方需要检查会话是否为None。
|
||||||
|
"""
|
||||||
session: Optional[AsyncSession] = None
|
session: Optional[AsyncSession] = None
|
||||||
|
SessionLocal = None
|
||||||
try:
|
try:
|
||||||
engine, SessionLocal = await initialize_database()
|
_, SessionLocal = await initialize_database()
|
||||||
if not SessionLocal:
|
if not SessionLocal:
|
||||||
raise RuntimeError("Database session not initialized")
|
logger.error("数据库会话工厂 (_SessionLocal) 未初始化。")
|
||||||
session = SessionLocal()
|
yield None
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"数据库初始化失败,无法创建会话: {e}")
|
||||||
|
yield None
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = SessionLocal()
|
||||||
# 对于 SQLite,在会话开始时设置 PRAGMA
|
# 对于 SQLite,在会话开始时设置 PRAGMA
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
if global_config.database.database_type == "sqlite":
|
if global_config.database.database_type == "sqlite":
|
||||||
try:
|
|
||||||
await session.execute(text("PRAGMA busy_timeout = 60000"))
|
await session.execute(text("PRAGMA busy_timeout = 60000"))
|
||||||
await session.execute(text("PRAGMA foreign_keys = ON"))
|
await session.execute(text("PRAGMA foreign_keys = ON"))
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"[SQLite] 设置会话 PRAGMA 失败: {e}")
|
|
||||||
|
|
||||||
yield session
|
yield session
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"数据库会话错误: {e}")
|
logger.error(f"数据库会话期间发生错误: {e}")
|
||||||
if session:
|
if session:
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
raise
|
raise # 将会话期间的错误重新抛出给调用者
|
||||||
finally:
|
finally:
|
||||||
if session:
|
if session:
|
||||||
await session.close()
|
await session.close()
|
||||||
|
|||||||
@@ -111,13 +111,43 @@ class ChatterPlanExecutor:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async def _execute_reply_actions(self, reply_actions: List[ActionPlannerInfo], plan: Plan) -> Dict[str, any]:
|
async def _execute_reply_actions(self, reply_actions: List[ActionPlannerInfo], plan: Plan) -> Dict[str, any]:
|
||||||
"""串行执行所有回复动作"""
|
"""串行执行所有回复动作,增加去重逻辑,避免对同一消息多次回复"""
|
||||||
results = []
|
results = []
|
||||||
total_actions = len(reply_actions)
|
|
||||||
if total_actions > 1:
|
# --- 新增去重逻辑 ---
|
||||||
|
unique_actions = []
|
||||||
|
replied_message_ids = set()
|
||||||
|
for action_info in reply_actions:
|
||||||
|
target_message = action_info.action_message
|
||||||
|
message_id = None
|
||||||
|
if target_message:
|
||||||
|
# 兼容 Pydantic 对象和字典两种情况
|
||||||
|
if hasattr(target_message, "message_id"):
|
||||||
|
message_id = getattr(target_message, "message_id", None)
|
||||||
|
elif isinstance(target_message, dict):
|
||||||
|
message_id = target_message.get("message_id")
|
||||||
|
|
||||||
|
if message_id:
|
||||||
|
if message_id not in replied_message_ids:
|
||||||
|
unique_actions.append(action_info)
|
||||||
|
replied_message_ids.add(message_id)
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"[多重回复] 检测到对消息ID '{message_id}' 的重复回复,已过滤。"
|
||||||
|
f" (动作: {action_info.action_type}, 原因: {action_info.reasoning})"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# 如果没有message_id,无法去重,直接添加
|
||||||
|
unique_actions.append(action_info)
|
||||||
|
# --- 去重逻辑结束 ---
|
||||||
|
|
||||||
|
total_actions = len(unique_actions)
|
||||||
|
if len(reply_actions) > total_actions:
|
||||||
|
logger.info(f"[多重回复] 原始回复任务 {len(reply_actions)} 个,去重后剩余 {total_actions} 个。")
|
||||||
|
elif total_actions > 1:
|
||||||
logger.info(f"[多重回复] 开始执行 {total_actions} 个回复任务。")
|
logger.info(f"[多重回复] 开始执行 {total_actions} 个回复任务。")
|
||||||
|
|
||||||
for i, action_info in enumerate(reply_actions):
|
for i, action_info in enumerate(unique_actions):
|
||||||
is_last_action = i == total_actions - 1
|
is_last_action = i == total_actions - 1
|
||||||
if total_actions > 1:
|
if total_actions > 1:
|
||||||
logger.info(f"[多重回复] 正在执行第 {i+1}/{total_actions} 个回复...")
|
logger.info(f"[多重回复] 正在执行第 {i+1}/{total_actions} 个回复...")
|
||||||
|
|||||||
@@ -740,26 +740,40 @@ class QZoneService:
|
|||||||
feeds_list = []
|
feeds_list = []
|
||||||
my_name = json_data.get("logininfo", {}).get("name", "")
|
my_name = json_data.get("logininfo", {}).get("name", "")
|
||||||
for msg in json_data.get("msglist", []):
|
for msg in json_data.get("msglist", []):
|
||||||
|
# 只有在处理好友说说时,才检查是否已评论并跳过
|
||||||
|
commentlist = msg.get("commentlist")
|
||||||
|
|
||||||
# 只有在处理好友说说时,才检查是否已评论并跳过
|
# 只有在处理好友说说时,才检查是否已评论并跳过
|
||||||
if not is_monitoring_own_feeds:
|
if not is_monitoring_own_feeds:
|
||||||
|
is_commented = False
|
||||||
|
if isinstance(commentlist, list):
|
||||||
is_commented = any(
|
is_commented = any(
|
||||||
c.get("name") == my_name for c in msg.get("commentlist", []) if isinstance(c, dict)
|
c.get("name") == my_name for c in commentlist if isinstance(c, dict)
|
||||||
)
|
)
|
||||||
if is_commented:
|
if is_commented:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
images = [pic["url1"] for pic in msg.get("pictotal", []) if "url1" in pic]
|
# --- 安全地处理图片列表 ---
|
||||||
|
images = []
|
||||||
|
pictotal = msg.get("pictotal")
|
||||||
|
if isinstance(pictotal, list):
|
||||||
|
images = [
|
||||||
|
pic["url1"] for pic in pictotal if isinstance(pic, dict) and "url1" in pic
|
||||||
|
]
|
||||||
|
|
||||||
|
# --- 安全地处理评论列表 ---
|
||||||
comments = []
|
comments = []
|
||||||
if "commentlist" in msg:
|
if isinstance(commentlist, list):
|
||||||
for c in msg["commentlist"]:
|
for c in commentlist:
|
||||||
|
# 确保评论条目也是字典
|
||||||
|
if isinstance(c, dict):
|
||||||
comments.append(
|
comments.append(
|
||||||
{
|
{
|
||||||
"qq_account": c.get("uin"),
|
"qq_account": c.get("uin"),
|
||||||
"nickname": c.get("name"),
|
"nickname": c.get("name"),
|
||||||
"content": c.get("content"),
|
"content": c.get("content"),
|
||||||
"comment_tid": c.get("tid"),
|
"comment_tid": c.get("tid"),
|
||||||
"parent_tid": c.get("parent_tid"), # API直接返回了父ID
|
"parent_tid": c.get("parent_tid"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user