- daily_news 插件内置百度新闻与60s图片获取逻辑,移除对 base.func_news 的业务依赖\n- epic_free 插件内置周五判断与免费游戏抓取逻辑,移除对 base.func_epic 的业务依赖\n- daily_ranking 插件内置排行生成与积分奖励逻辑,不再依赖 MessageStorage 业务封装\n- sehuatang_push 改为引用插件目录内的抓取与PDF生成实现,将核心业务代码迁入插件目录\n- 确保新插件可独立承载自身业务逻辑,平台层仅提供调度与基础设施能力
187 lines
6.5 KiB
Python
187 lines
6.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
from datetime import datetime
|
||
from typing import Any, Dict, List, Optional, Tuple
|
||
|
||
import requests
|
||
from bs4 import BeautifulSoup
|
||
|
||
from base.plugin_common.message_plugin_interface import MessagePluginInterface
|
||
from base.plugin_common.plugin_interface import PluginStatus
|
||
from utils.robot_cmd.robot_command import GroupBotManager
|
||
|
||
|
||
class EpicFreePlugin(MessagePluginInterface):
|
||
"""Epic 免费游戏自动播报插件。"""
|
||
|
||
FEATURE_KEY = "EPIC"
|
||
FEATURE_DESCRIPTION = "📊 EPIC自动播报 [每周五自动发送]"
|
||
|
||
@property
|
||
def name(self) -> str:
|
||
return "Epic播报"
|
||
|
||
@property
|
||
def version(self) -> str:
|
||
return "1.0.0"
|
||
|
||
@property
|
||
def description(self) -> str:
|
||
return "将 Epic 免费游戏播报从系统任务迁移到插件任务。"
|
||
|
||
@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()
|
||
|
||
def initialize(self, context: Dict[str, Any]) -> bool:
|
||
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": "weekly_free_games_push",
|
||
"name": "Epic免费游戏推送",
|
||
"description": "每周五推送 Epic 当周免费游戏",
|
||
"trigger_type": "every_weekday_time",
|
||
"trigger_config": {"weekday": 4, "time_str": "10:00"},
|
||
"target_scope": "all_enabled_groups",
|
||
"target_config": {},
|
||
"payload": {"force": False},
|
||
"default_enabled": True,
|
||
}
|
||
]
|
||
|
||
async def run_scheduled_action(self, action_key: str, context: Dict[str, Any]) -> Dict[str, Any]:
|
||
if action_key != "weekly_free_games_push":
|
||
return {
|
||
"success": False,
|
||
"summary": f"不支持的动作: {action_key}",
|
||
"detail": {"action_key": action_key},
|
||
}
|
||
if not self.bot:
|
||
return {"success": False, "summary": "bot 未注入", "detail": {}}
|
||
|
||
payload = context.get("payload") or {}
|
||
force = bool(payload.get("force", False))
|
||
if not force and not self._is_friday():
|
||
# 非周五时默认跳过;手动触发可通过 payload.force 强制执行。
|
||
return {"success": True, "summary": "今天不是周五,已跳过 Epic 播报", "detail": {"skipped": True}}
|
||
|
||
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}}
|
||
|
||
try:
|
||
text = self._get_free_games()
|
||
except Exception as e:
|
||
return {"success": False, "summary": f"获取 Epic 免费游戏失败: {e}", "detail": {"error": str(e)}}
|
||
|
||
success_groups = []
|
||
failed_groups = {}
|
||
for gid in target_groups:
|
||
try:
|
||
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"Epic播报完成: 成功{len(success_groups)}群, 失败{len(failed_groups)}群",
|
||
"detail": {
|
||
"target_count": len(target_groups),
|
||
"success_groups": success_groups,
|
||
"failed_groups": failed_groups,
|
||
"force": force,
|
||
},
|
||
}
|
||
|
||
@staticmethod
|
||
def _is_friday() -> bool:
|
||
"""判断是否周五(插件内实现)。"""
|
||
return datetime.today().weekday() == 4
|
||
|
||
@staticmethod
|
||
def _get_free_games() -> str:
|
||
"""抓取 Epic 免费游戏列表(插件内实现)。"""
|
||
url = "https://steamstats.cn/xi"
|
||
headers = {
|
||
"User-Agent": (
|
||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||
"(KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36 Edg/90.0.818.41"
|
||
)
|
||
}
|
||
resp = requests.get(url, headers=headers, timeout=20)
|
||
resp.raise_for_status()
|
||
resp.encoding = resp.apparent_encoding
|
||
soup = BeautifulSoup(resp.text, "html.parser")
|
||
text = "今日喜加一 :https://store.epicgames.com/en-US/free-games\n"
|
||
|
||
tbody = soup.find("tbody")
|
||
if not tbody:
|
||
return text + "未抓取到免费游戏列表"
|
||
|
||
rows = tbody.find_all("tr")
|
||
idx = 1
|
||
for row in rows:
|
||
cols = row.find_all("td")
|
||
if len(cols) < 7:
|
||
continue
|
||
name = (cols[1].string or "").strip()
|
||
gametype = (cols[2].string or "").replace(" ", "").strip()
|
||
start = (cols[3].string or "").replace(" ", "").strip()
|
||
end = (cols[4].string or "").replace(" ", "").strip()
|
||
permanent = (cols[5].string or "").replace(" ", "").strip()
|
||
origin_span = cols[6].find("span")
|
||
origin = (origin_span.string or "").replace(" ", "").strip() if origin_span else ""
|
||
href_value = ""
|
||
for a in cols[6].find_all("a"):
|
||
href_value = a.get("href", "") or href_value
|
||
|
||
text += (
|
||
f"序号:{idx}\n"
|
||
f"游戏名称:{name}\n"
|
||
f"DLC/game:{gametype}\n"
|
||
f"开始时间:{start}\n"
|
||
f"结束时间:{end}\n"
|
||
f"是否永久:{permanent}\n"
|
||
f"平台:{origin}\n"
|
||
f"URL:{href_value}\n"
|
||
)
|
||
idx += 1
|
||
return text
|