- daily_news 插件内置百度新闻与60s图片获取逻辑,移除对 base.func_news 的业务依赖\n- epic_free 插件内置周五判断与免费游戏抓取逻辑,移除对 base.func_epic 的业务依赖\n- daily_ranking 插件内置排行生成与积分奖励逻辑,不再依赖 MessageStorage 业务封装\n- sehuatang_push 改为引用插件目录内的抓取与PDF生成实现,将核心业务代码迁入插件目录\n- 确保新插件可独立承载自身业务逻辑,平台层仅提供调度与基础设施能力
177 lines
6.6 KiB
Python
177 lines
6.6 KiB
Python
# -*- 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)
|