feat(api): 整合消息统计接口并增加来源筛选

将 `/messages/bot_stats_by_chat` 的功能合并到 `/messages/stats_by_chat` 接口中,以减少冗余并统一API。

- 为 `/messages/stats_by_chat` 接口新增 `source` 查询参数,允许按 'user' (用户) 或 'bot' (机器人) 筛选消息来源。
- `group_by_user` 参数现在仅在 `source='user'` 时生效。
- 对内部逻辑进行了重构,以支持新的筛选功能并提升代码可读性。

BREAKING CHANGE: 移除了 `/messages/bot_stats_by_chat` 接口。其功能已整合到 `/messages/stats_by_chat` 接口中,可通过设置查询参数 `source='bot'` 来实现。
This commit is contained in:
minecraft1024a
2025-11-14 13:00:44 +08:00
committed by Windpicker-owo
parent c9f1e07ebb
commit f29418e75d

View File

@@ -58,115 +58,108 @@ async def get_message_stats(
@router.get("/messages/stats_by_chat")
async def get_message_stats_by_chat(
days: int = Query(1, ge=1, description="指定查询过去多少天的数据"),
group_by_user: bool = Query(False, description="是否按用户进行分组统计"),
source: Literal["user", "bot"] = Query("user", description="筛选消息来源: 'user' (用户发送的), 'bot' (BOT发送的)"),
group_by_user: bool = Query(False, description="是否按用户进行分组统计 (仅当 source='user' 时有效)"),
format: bool = Query(False, description="是否格式化输出,包含群聊和用户信息"),
):
"""
获取BOT在指定天数内按聊天流或按用户统计的消息数据。
获取在指定天数内按聊天会话统计的消息数据。
可根据消息来源 (用户或BOT) 进行筛选。
"""
try:
# --- 1. 数据准备 ---
# 计算查询的时间范围
end_time = time.time()
start_time = end_time - (days * 24 * 3600)
# 从数据库获取指定时间范围内的所有消息
messages = await message_api.get_messages_by_time(start_time, end_time)
bot_qq = str(global_config.bot.qq_account)
messages = [msg for msg in messages if msg.get("user_id") != bot_qq]
# --- 2. 消息筛选 ---
# 根据 source 参数筛选消息来源
if source == "user":
# 筛选出用户发送的消息(即非机器人发送的消息)
messages = [msg for msg in messages if msg.get("user_id") != bot_qq]
else: # source == "bot"
# 筛选出机器人发送的消息
messages = [msg for msg in messages if msg.get("user_id") == bot_qq]
# --- 3. 数据统计 ---
stats = {}
# 如果统计来源是用户
if source == "user":
# 遍历用户消息进行统计
for msg in messages:
chat_id = msg.get("chat_id", "unknown")
user_id = msg.get("user_id")
# 初始化聊天会话的统计结构
if chat_id not in stats:
stats[chat_id] = {"total_stats": {"total": 0}, "user_stats": {}}
# 累加总消息数
stats[chat_id]["total_stats"]["total"] += 1
# 如果需要按用户分组,则进一步统计每个用户的消息数
if group_by_user:
if user_id not in stats[chat_id]["user_stats"]:
stats[chat_id]["user_stats"][user_id] = 0
stats[chat_id]["user_stats"][user_id] += 1
# 如果不按用户分组,则简化统计结果,只保留总数
if not group_by_user:
stats = {chat_id: data["total_stats"] for chat_id, data in stats.items()}
# 如果统计来源是机器人
else:
# 遍历机器人消息进行统计
for msg in messages:
chat_id = msg.get("chat_id", "unknown")
# 初始化聊天会话的统计结构
if chat_id not in stats:
stats[chat_id] = 0
# 累加机器人发送的消息数
stats[chat_id] += 1
for msg in messages:
chat_id = msg.get("chat_id", "unknown")
user_id = msg.get("user_id")
# --- 4. 格式化输出 ---
# 如果 format 参数为 False直接返回原始统计数据
if not format:
return stats
if chat_id not in stats:
stats[chat_id] = {"total_stats": {"total": 0}, "user_stats": {}}
# 获取聊天管理器以查询会话信息
chat_manager = get_chat_manager()
formatted_stats = {}
# 遍历统计结果进行格式化
for chat_id, data in stats.items():
stream = chat_manager.streams.get(chat_id)
chat_name = f"未知会话 ({chat_id})"
# 尝试获取更友好的会话名称(群名或用户名)
if stream:
if stream.group_info and stream.group_info.group_name:
chat_name = stream.group_info.group_name
elif stream.user_info and stream.user_info.user_nickname:
chat_name = stream.user_info.user_nickname
stats[chat_id]["total_stats"]["total"] += 1
# 如果是机器人消息统计,直接格式化
if source == "bot":
formatted_stats[chat_id] = {"chat_name": chat_name, "count": data}
continue
if group_by_user:
if user_id not in stats[chat_id]["user_stats"]:
stats[chat_id]["user_stats"][user_id] = 0
# 如果是用户消息统计,进行更复杂的格式化
formatted_data = {
"chat_name": chat_name,
"total_stats": data if not group_by_user else data["total_stats"],
}
# 如果按用户分组,则添加用户信息
if group_by_user and "user_stats" in data:
formatted_data["user_stats"] = {}
for user_id, count in data["user_stats"].items():
person_id = person_api.get_person_id("qq", user_id)
person_info = await person_api.get_person_info(person_id)
nickname = person_info.get("nickname", "未知用户")
formatted_data["user_stats"][user_id] = {"nickname": nickname, "count": count}
formatted_stats[chat_id] = formatted_data
stats[chat_id]["user_stats"][user_id] += 1
if not group_by_user:
stats = {chat_id: data["total_stats"] for chat_id, data in stats.items()}
if format:
chat_manager = get_chat_manager()
formatted_stats = {}
for chat_id, data in stats.items():
stream = chat_manager.streams.get(chat_id)
chat_name = "未知会话"
if stream:
if stream.group_info and stream.group_info.group_name:
chat_name = stream.group_info.group_name
elif stream.user_info and stream.user_info.user_nickname:
chat_name = stream.user_info.user_nickname
else:
chat_name = f"未知会话 ({chat_id})"
formatted_data = {
"chat_name": chat_name,
"total_stats": data if not group_by_user else data["total_stats"],
}
if group_by_user and "user_stats" in data:
formatted_data["user_stats"] = {}
for user_id, count in data["user_stats"].items():
person_id = person_api.get_person_id("qq", user_id)
nickname = await person_api.get_person_value(person_id, "nickname", "未知用户")
formatted_data["user_stats"][user_id] = {"nickname": nickname, "count": count}
formatted_stats[chat_id] = formatted_data
return formatted_stats
return stats
return formatted_stats
except Exception as e:
# 统一异常处理
logger.error(f"获取消息统计时发生错误: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/messages/bot_stats_by_chat")
async def get_bot_message_stats_by_chat(
days: int = Query(1, ge=1, description="指定查询过去多少天的数据"),
format: bool = Query(False, description="是否格式化输出,包含群聊和用户信息"),
):
"""
获取BOT在指定天数内按聊天流统计的已发送消息数据。
"""
try:
end_time = time.time()
start_time = end_time - (days * 24 * 3600)
messages = await message_api.get_messages_by_time(start_time, end_time)
bot_qq = str(global_config.bot.qq_account)
# 筛选出机器人发送的消息
bot_messages = [msg for msg in messages if msg.get("user_id") == bot_qq]
stats = {}
for msg in bot_messages:
chat_id = msg.get("chat_id", "unknown")
if chat_id not in stats:
stats[chat_id] = 0
stats[chat_id] += 1
if format:
chat_manager = get_chat_manager()
formatted_stats = {}
for chat_id, count in stats.items():
stream = chat_manager.streams.get(chat_id)
chat_name = f"未知会话 ({chat_id})"
if stream:
if stream.group_info and stream.group_info.group_name:
chat_name = stream.group_info.group_name
elif stream.user_info and stream.user_info.user_nickname:
chat_name = stream.user_info.user_nickname
formatted_stats[chat_id] = {"chat_name": chat_name, "count": count}
return formatted_stats
return stats
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))