# -*- coding: utf-8 -*- from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Tuple from base.plugin_common.message_plugin_interface import MessagePluginInterface from base.plugin_common.plugin_interface import PluginStatus from db.message_storage import MessageStorageDB from db.points_db import PointSource, PointsDBOperator from utils.robot_cmd.robot_command import GroupBotManager from utils.wechat.contact_manager import ContactManager class DailyRankingPlugin(MessagePluginInterface): """每日群消息排行推送插件。""" FEATURE_KEY = "DAILY_SUMMARY" FEATURE_DESCRIPTION = "🕤 每日群发言总结 [每日9:30定时发送]" @property def name(self) -> str: return "每日排行" @property def version(self) -> str: return "1.0.0" @property def description(self) -> str: return "将群消息排行推送从系统任务迁移到插件任务。" @property def author(self) -> str: return "ABOT Team" @property def commands(self) -> List[str]: return [] @property def feature_key(self) -> Optional[str]: return self.FEATURE_KEY @property def feature_description(self) -> Optional[str]: return self.FEATURE_DESCRIPTION def __init__(self): super().__init__() self.feature = self.register_feature() self.message_db: Optional[MessageStorageDB] = None self.points_db: Optional[PointsDBOperator] = None def initialize(self, context: Dict[str, Any]) -> bool: db_manager = context.get("db_manager") if db_manager is None: return False # 排行业务逻辑下沉到插件内,仅复用 DB Operator 作为数据访问层。 self.message_db = MessageStorageDB(db_manager) self.points_db = PointsDBOperator(db_manager) return True def start(self) -> bool: self.status = PluginStatus.RUNNING return True def stop(self) -> bool: self.status = PluginStatus.STOPPED return True def can_process(self, message: Dict[str, Any]) -> bool: return False async def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]: return False, None def get_schedule_actions(self) -> List[Dict[str, Any]]: return [ { "action_key": "daily_message_ranking_push", "name": "群消息排行推送", "description": "每天生成并发送群消息发言排行", "trigger_type": "at_times", "trigger_config": {"time_list": ["09:30"]}, "target_scope": "all_enabled_groups", "target_config": {}, "payload": {}, "default_enabled": True, } ] async def run_scheduled_action(self, action_key: str, context: Dict[str, Any]) -> Dict[str, Any]: if action_key != "daily_message_ranking_push": return { "success": False, "summary": f"不支持的动作: {action_key}", "detail": {"action_key": action_key}, } if not self.bot: return {"success": False, "summary": "bot 未注入", "detail": {}} if not self.message_db or not self.points_db: return {"success": False, "summary": "排行依赖未初始化", "detail": {}} target_groups = [str(g).strip() for g in (context.get("target_groups") or []) if str(g).strip()] if not target_groups: target_groups = [ gid for gid in GroupBotManager.get_group_list() if GroupBotManager.get_group_permission(gid, self.feature).value == "enabled" ] if not target_groups: return {"success": False, "summary": "没有可推送目标群", "detail": {"target_count": 0}} success_groups = [] failed_groups = {} for gid in target_groups: try: ok, text = await self._generate_and_send_ranking(gid) if ok and text: await self.bot.send_text_message(gid, text) success_groups.append(gid) except Exception as e: failed_groups[gid] = str(e) return { "success": len(failed_groups) == 0, "summary": f"每日排行推送完成: 成功{len(success_groups)}群, 失败{len(failed_groups)}群", "detail": { "target_count": len(target_groups), "success_groups": success_groups, "failed_groups": failed_groups, }, } async def _generate_and_send_ranking(self, group_id: str) -> Tuple[bool, str]: """生成并奖励发言排行(插件内实现)。""" if not self.message_db or not self.points_db: return False, "排行依赖未初始化" yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") rows = self.message_db.get_speech_ranking(yesterday, group_id, limit=20) if not rows: return False, f"📊 {yesterday} 没有发言记录" contact_manager = ContactManager.get_instance() ranking_lines = [f"🏆 {yesterday} 发言排行榜 🏆"] for rank, row in enumerate(rows, start=1): wxid = row.get("wx_id") speech_count = int(row.get("speech_count") or 0) display_name = contact_manager.get_group_name(group_id, wxid) or wxid reward = 0 if rank == 1: reward = 30 ranking_lines.append(f"🥇🐲 {rank}.{display_name}: {speech_count}次 🔥 +{reward}积分") elif rank == 2: reward = 20 ranking_lines.append(f"🥈 {rank}.{display_name}: {speech_count}次 ✨ +{reward}积分") elif rank == 3: reward = 10 ranking_lines.append(f"🥉 {rank}.{display_name}: {speech_count}次 👏 +{reward}积分") elif rank <= 10: reward = 5 ranking_lines.append(f"🌟 {rank}.{display_name}: {speech_count}次 +{reward}积分") else: reward = 3 ranking_lines.append(f"👍 {rank}.{display_name}: {speech_count}次 +{reward}积分") if reward > 0: self.points_db.add_points( wxid, group_id, reward, PointSource.OTHER, f"{yesterday}发言排行第{rank}名奖励", ) return True, "\n".join(ranking_lines)