diff --git a/db/message_storage.py b/db/message_storage.py index 662ab1d..ac69d7a 100644 --- a/db/message_storage.py +++ b/db/message_storage.py @@ -557,3 +557,37 @@ class MessageStorageDB(BaseDBOperator): group_id) result = self.execute_query(sql, params) 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), + } diff --git a/plugins/message_summary/main.py b/plugins/message_summary/main.py index 1f40572..c1525ff 100644 --- a/plugins/message_summary/main.py +++ b/plugins/message_summary/main.py @@ -160,6 +160,7 @@ class MessageSummaryPlugin(MessagePluginInterface): return False, None 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_stats = self.message_storage.get_message_stats_by_date_range(group_id, period_start, period_end) if message_count < 100: await self.bot.send_text_message( group_id, @@ -191,6 +192,7 @@ class MessageSummaryPlugin(MessagePluginInterface): period_start=period_start, period_end=period_end, message_count=message_count, + message_stats=message_stats, ) if res: return True, "异步总结已启动" @@ -210,11 +212,12 @@ class MessageSummaryPlugin(MessagePluginInterface): period_start: Optional[datetime] = None, period_end: Optional[datetime] = None, message_count: Optional[int] = None, + message_stats: Optional[Dict[str, int]] = None, ): """异步生成并发送总结""" 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: # 图片生成成功,发送图片 @@ -449,7 +452,12 @@ class MessageSummaryPlugin(MessagePluginInterface): self.LOG.error(f"群总结入库失败: group_id={group_id}, period_key={summary_record['period_key']}") 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 try: @@ -483,6 +491,7 @@ class MessageSummaryPlugin(MessagePluginInterface): answer = self._clean_summary_output(response.get("text", "")) metadata = {"usage": response.get("usage", {}) or {}} spath = "" + answer = self._prepend_stats_section(answer, message_stats or {}) answer = self._append_usage_info(answer, metadata) if answer and len(answer.strip()) > 0: @@ -513,6 +522,25 @@ class MessageSummaryPlugin(MessagePluginInterface): 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): """定时任务:每天早上9点总结昨天的聊天信息""" try: @@ -561,6 +589,11 @@ class MessageSummaryPlugin(MessagePluginInterface): continue 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) @@ -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: # 图片生成成功,发送图片 diff --git a/utils/wechat/message_to_db.py b/utils/wechat/message_to_db.py index 46096fe..e5045fb 100644 --- a/utils/wechat/message_to_db.py +++ b/utils/wechat/message_to_db.py @@ -551,6 +551,27 @@ class MessageStorage: logger.error(f"统计消息数量出错: {e}") 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: """优化的消息格式化方法,减少冗余