feat(message_summary): add group overview stats section
This commit is contained in:
@@ -557,3 +557,37 @@ class MessageStorageDB(BaseDBOperator):
|
|||||||
group_id)
|
group_id)
|
||||||
result = self.execute_query(sql, params)
|
result = self.execute_query(sql, params)
|
||||||
return result[0]['count'] if result else 0
|
return result[0]['count'] if result else 0
|
||||||
|
|
||||||
|
def get_message_stats_by_date_range(self, group_id: str, start_time: datetime, end_time: datetime) -> Dict:
|
||||||
|
"""统计指定时间范围内的群消息概览"""
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
COUNT(*) AS total_count,
|
||||||
|
COUNT(DISTINCT sender) AS participant_count,
|
||||||
|
SUM(CASE WHEN message_type = 1 THEN 1 ELSE 0 END) AS text_count,
|
||||||
|
SUM(CASE WHEN message_type = 3 THEN 1 ELSE 0 END) AS image_count,
|
||||||
|
SUM(CASE WHEN message_type IN (43, 62) THEN 1 ELSE 0 END) AS video_count,
|
||||||
|
SUM(CASE WHEN message_type = 49 THEN 1 ELSE 0 END) AS link_count,
|
||||||
|
SUM(CASE WHEN message_type IN (47, 1048625, 1090519089) THEN 1 ELSE 0 END) AS emoji_count
|
||||||
|
FROM messages
|
||||||
|
WHERE timestamp >= %s
|
||||||
|
AND timestamp <= %s
|
||||||
|
AND group_id = %s
|
||||||
|
AND sender IS NOT NULL
|
||||||
|
AND sender <> ''
|
||||||
|
"""
|
||||||
|
params = (
|
||||||
|
start_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
end_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
group_id,
|
||||||
|
)
|
||||||
|
result = self.execute_query(sql, params, fetch_one=True) or {}
|
||||||
|
return {
|
||||||
|
"total_count": int(result.get("total_count") or 0),
|
||||||
|
"participant_count": int(result.get("participant_count") or 0),
|
||||||
|
"text_count": int(result.get("text_count") or 0),
|
||||||
|
"image_count": int(result.get("image_count") or 0),
|
||||||
|
"video_count": int(result.get("video_count") or 0),
|
||||||
|
"link_count": int(result.get("link_count") or 0),
|
||||||
|
"emoji_count": int(result.get("emoji_count") or 0),
|
||||||
|
}
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
return False, None
|
return False, None
|
||||||
period_start, period_end = self._resolve_manual_summary_range(custom_date)
|
period_start, period_end = self._resolve_manual_summary_range(custom_date)
|
||||||
message_count = self.message_storage.count_messages_by_date_range(group_id, period_start, period_end)
|
message_count = self.message_storage.count_messages_by_date_range(group_id, period_start, period_end)
|
||||||
|
message_stats = self.message_storage.get_message_stats_by_date_range(group_id, period_start, period_end)
|
||||||
if message_count < 100:
|
if message_count < 100:
|
||||||
await self.bot.send_text_message(
|
await self.bot.send_text_message(
|
||||||
group_id,
|
group_id,
|
||||||
@@ -191,6 +192,7 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
period_start=period_start,
|
period_start=period_start,
|
||||||
period_end=period_end,
|
period_end=period_end,
|
||||||
message_count=message_count,
|
message_count=message_count,
|
||||||
|
message_stats=message_stats,
|
||||||
)
|
)
|
||||||
if res:
|
if res:
|
||||||
return True, "异步总结已启动"
|
return True, "异步总结已启动"
|
||||||
@@ -210,11 +212,12 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
period_start: Optional[datetime] = None,
|
period_start: Optional[datetime] = None,
|
||||||
period_end: Optional[datetime] = None,
|
period_end: Optional[datetime] = None,
|
||||||
message_count: Optional[int] = None,
|
message_count: Optional[int] = None,
|
||||||
|
message_stats: Optional[Dict[str, int]] = None,
|
||||||
):
|
):
|
||||||
"""异步生成并发送总结"""
|
"""异步生成并发送总结"""
|
||||||
try:
|
try:
|
||||||
# 生成总结
|
# 生成总结
|
||||||
summary, image_path = await self._generate_summary(chat_content, group_name)
|
summary, image_path = await self._generate_summary(chat_content, group_name, message_stats=message_stats)
|
||||||
|
|
||||||
if image_path:
|
if image_path:
|
||||||
# 图片生成成功,发送图片
|
# 图片生成成功,发送图片
|
||||||
@@ -449,7 +452,12 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
self.LOG.error(f"群总结入库失败: group_id={group_id}, period_key={summary_record['period_key']}")
|
self.LOG.error(f"群总结入库失败: group_id={group_id}, period_key={summary_record['period_key']}")
|
||||||
return saved
|
return saved
|
||||||
|
|
||||||
async def _generate_summary(self, chat_content: str, group_name: str) -> Tuple[str, Optional[str]]:
|
async def _generate_summary(
|
||||||
|
self,
|
||||||
|
chat_content: str,
|
||||||
|
group_name: str,
|
||||||
|
message_stats: Optional[Dict[str, int]] = None,
|
||||||
|
) -> Tuple[str, Optional[str]]:
|
||||||
"""生成总结"""
|
"""生成总结"""
|
||||||
content_compress = chat_content
|
content_compress = chat_content
|
||||||
try:
|
try:
|
||||||
@@ -483,6 +491,7 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
answer = self._clean_summary_output(response.get("text", ""))
|
answer = self._clean_summary_output(response.get("text", ""))
|
||||||
metadata = {"usage": response.get("usage", {}) or {}}
|
metadata = {"usage": response.get("usage", {}) or {}}
|
||||||
spath = ""
|
spath = ""
|
||||||
|
answer = self._prepend_stats_section(answer, message_stats or {})
|
||||||
answer = self._append_usage_info(answer, metadata)
|
answer = self._append_usage_info(answer, metadata)
|
||||||
|
|
||||||
if answer and len(answer.strip()) > 0:
|
if answer and len(answer.strip()) > 0:
|
||||||
@@ -513,6 +522,25 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
|
|
||||||
return "生成总结时出错", None
|
return "生成总结时出错", None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _prepend_stats_section(summary: str, message_stats: Dict[str, int]) -> str:
|
||||||
|
stats = message_stats or {}
|
||||||
|
if not summary or not summary.strip():
|
||||||
|
return summary
|
||||||
|
|
||||||
|
section_lines = [
|
||||||
|
"## 群概览",
|
||||||
|
f"- 总统计条目数:{int(stats.get('total_count') or 0)}",
|
||||||
|
f"- 参与人数:{int(stats.get('participant_count') or 0)}",
|
||||||
|
f"- 文本消息:{int(stats.get('text_count') or 0)}",
|
||||||
|
f"- 图片消息:{int(stats.get('image_count') or 0)}",
|
||||||
|
f"- 视频消息:{int(stats.get('video_count') or 0)}",
|
||||||
|
f"- 链接消息:{int(stats.get('link_count') or 0)}",
|
||||||
|
f"- 表情消息:{int(stats.get('emoji_count') or 0)}",
|
||||||
|
]
|
||||||
|
section = "\n".join(section_lines)
|
||||||
|
return f"{section}\n\n{summary.strip()}"
|
||||||
|
|
||||||
async def daily_summary_job(self):
|
async def daily_summary_job(self):
|
||||||
"""定时任务:每天早上9点总结昨天的聊天信息"""
|
"""定时任务:每天早上9点总结昨天的聊天信息"""
|
||||||
try:
|
try:
|
||||||
@@ -561,6 +589,11 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
self.LOG.info(f"群 {group_id} 昨天有 {message_count} 条消息,开始获取内容")
|
self.LOG.info(f"群 {group_id} 昨天有 {message_count} 条消息,开始获取内容")
|
||||||
|
message_stats = self.message_storage.get_message_stats_by_date_range(
|
||||||
|
group_id,
|
||||||
|
yesterday_start,
|
||||||
|
yesterday_end,
|
||||||
|
)
|
||||||
|
|
||||||
# 获取群成员信息
|
# 获取群成员信息
|
||||||
group_members = ContactManager.get_instance().get_group_members(group_id)
|
group_members = ContactManager.get_instance().get_group_members(group_id)
|
||||||
@@ -592,7 +625,11 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 生成总结
|
# 生成总结
|
||||||
summary, image_path = await self._generate_summary(chat_content, group_name)
|
summary, image_path = await self._generate_summary(
|
||||||
|
chat_content,
|
||||||
|
group_name,
|
||||||
|
message_stats=message_stats,
|
||||||
|
)
|
||||||
|
|
||||||
if image_path:
|
if image_path:
|
||||||
# 图片生成成功,发送图片
|
# 图片生成成功,发送图片
|
||||||
|
|||||||
@@ -551,6 +551,27 @@ class MessageStorage:
|
|||||||
logger.error(f"统计消息数量出错: {e}")
|
logger.error(f"统计消息数量出错: {e}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def get_message_stats_by_date_range(self, group_id, start_time: datetime, end_time: datetime) -> Dict:
|
||||||
|
"""获取指定时间范围内的群消息概览统计"""
|
||||||
|
try:
|
||||||
|
stats = self.message_db.get_message_stats_by_date_range(group_id, start_time, end_time)
|
||||||
|
logger.info(
|
||||||
|
f"群 {group_id} 在 {start_time} 至 {end_time} 的消息概览: "
|
||||||
|
f"total={stats.get('total_count', 0)}, participants={stats.get('participant_count', 0)}"
|
||||||
|
)
|
||||||
|
return stats
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取消息概览统计出错: {e}")
|
||||||
|
return {
|
||||||
|
"total_count": 0,
|
||||||
|
"participant_count": 0,
|
||||||
|
"text_count": 0,
|
||||||
|
"image_count": 0,
|
||||||
|
"video_count": 0,
|
||||||
|
"link_count": 0,
|
||||||
|
"emoji_count": 0,
|
||||||
|
}
|
||||||
|
|
||||||
def _format_messages_optimized(self, messages: list, all_contacts: dict) -> str:
|
def _format_messages_optimized(self, messages: list, all_contacts: dict) -> str:
|
||||||
"""优化的消息格式化方法,减少冗余
|
"""优化的消息格式化方法,减少冗余
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user