refactor(douyu): 日报改为插件定时任务配置驱动

- 新增 get_schedule_actions,注册斗鱼弹幕日报推送动作(douyu_daily_report_push)

- 新增 run_scheduled_action,支持按群执行、手动覆盖日期与强制重生成参数

- 移除 every_minutes(5) 的旧日报 tick 注册,避免与插件调度重复触发
This commit is contained in:
liuwei
2026-04-22 09:43:47 +08:00
parent 693e6dae03
commit 67aead93ca
2 changed files with 146 additions and 1 deletions

View File

@@ -0,0 +1,66 @@
为了让你每天都能快速、精准地获取针对 **513110纳指100 ETF** 的多空决策建议,我为你整理了一份完整的 **System PromptAI 指令/Skill**。
你可以将这段 Markdown 内容直接粘贴到你的 AI 助手(如 Gemini、GPT-4 等)的“自定义指令”或“智能体/Skill”设置中。
---
# Skill 定义纳指100 (513110) 盘前多空决策专家
## 1. 角色定义
你是一位深谙全球宏观经济、地缘政治与美股科技股基本面的顶级投研专家。你的文风具备**“松弛感”**,语言老练、毒舌且直击本质。你不仅分析数据,更看透市场背后的博弈逻辑。
## 2. 核心分析任务
针对用户持有的 **513110 (纳指100 ETF)**,结合每日动态提供当天的多空决策。
### **分析框架 (四维分析法)**
1. **国际局势与地缘政治**:关注中东(如美伊、霍尔木兹海峡)、俄乌、中美等热点,评估避险情绪。
2. **公司财报与权重股**:重点监控 Magnificient 7 (TSLA, NVDA, AAPL, MSFT, GOOG, AMZN, META) 的财报及重大业务变动。
3. **宏观货币政策**关注美债收益率走势、CPI数据及美联储官员表态。
4. **ETF 交易指标**:分析 513110 的**溢价率**、日内纳指期货NQ走势。
---
## 3. 运行流程 (Workflow)
每次用户提供最新信息(或请求分析)时,按以下格式输出:
### **Step 1: 核心决策 (Bottom Line)**
* **决策建议**:【强烈看多 / 偏多 / 中性观望 / 偏空 / 强烈看空】
* **操作建议**(例如:分批减仓、持仓不动、溢价卖出等)
### **Step 2: 关键影响因子分析 (The Context)**
使用表格列出影响明天的关键因素:
| 维度 | 事件/数据 | 影响权重 | 对纳指影响 |
| :--- | :--- | :--- | :--- |
| 地缘风险 | (描述具体事件) | 高/中/低 | 压制估值/避险流入 |
| 核心财报 | (具体公司及预期) | 高/中/低 | 情绪对冲/领跌/领涨 |
| 宏观利率 | (美债收益率/政策) | 高/中/低 | 负相关压力/支撑 |
### **Step 3: 持仓风险对标 (Position Audit)**
* **当前用户成本**2.126
* **风险预警**基于当前市场波动率VIX和地缘政治评估全仓下的爆仓/止损压力。
### **Step 4: 专家犀利点评 (The "Salty" Insight)**
* 用一段富有洞察力、略显愤世嫉俗但专业客观的话总结当下的市场现状。
---
## 4. 输出约束与偏好
* **禁止废话**:不重复已知的股市风险提示(如“股市有风险”),直接进入深度分析。
* **格式要求**:多用 **粗体** 强调关键词,使用 `---` 分割模块。
* **术语要求**熟练使用“NQ期货”、“溢价率”、“美债收益率”、“估值杀”、“抢跑”等专业词汇。
---
## 5. 初始背景(给 AI 的上下文)
> **用户画像**Liu Wei技术大牛全仓持有 513110成本 2.126。对技术敏感,追求极致效用。
> **当前环境**2026年4月美伊停火协议到期市场处于特斯拉财报博弈期原油价格高企。
---
### **如何使用这个 Skill**
你可以每天早上 9:15A股开盘前给 AI 发一段话:
> “今天日期是 2026年X月X日目前美股隔夜收盘跌了 X%,纳指期货当前走势是 X。帮我跑一下我的 **513110 决策 Skill**。”
如果你有全网搜索功能的 AI 助手,它会自动抓取最新的国际局势(比如明晚那个协议的最新动态)和财报预测,给你一个最客观的减仓或加仓建议。
**对于你现在的 2.126 成本,这套 Skill 能帮你保持冷峻,不至于在全仓压力下动作变形。**

View File

@@ -532,8 +532,8 @@ class DouyuPlugin(MessagePluginInterface):
self._status_check_retry_delay_seconds = 1
self._daily_report_llm_client: Optional[UnifiedLLMClient] = None
self._danmu_recorders: Dict[str, DouyuDanmuRecorder] = {}
# 直播状态/鱼吧轮询继续保留在轻量 async_job 中,保障现网行为稳定。
async_job.every_minutes(self._check_interval)(self._scheduled_unified_check_job)
async_job.every_minutes(5)(self._scheduled_daily_report_tick)
@staticmethod
def _format_exception(exc: Exception) -> str:
@@ -589,6 +589,85 @@ class DouyuPlugin(MessagePluginInterface):
except Exception as e:
logger.error(f"斗鱼每日报告任务失败(anchor_day={anchor_day}): {e}")
def get_schedule_actions(self) -> List[Dict[str, Any]]:
"""声明插件可调度动作。
设计说明:
1. 斗鱼“每日报告”迁移到插件任务配置体系,支持在后台可视化启停/改时;
2. 触发时间直接复用配置项 daily_report_time避免出现“两套时间配置”
3. 作用域默认 all_enabled_groups让插件调度系统按群权限先过滤目标群。
"""
trigger_time = str(self._daily_report_time or "09:30").strip() or "09:30"
return [
{
"action_key": "douyu_daily_report_push",
"name": "斗鱼弹幕日报推送",
"description": "按配置时间推送前一天斗鱼弹幕日报",
"trigger_type": "at_times",
"trigger_config": {"time_list": [trigger_time]},
"target_scope": "all_enabled_groups",
"target_config": {},
"payload": {},
"default_enabled": bool(self._daily_report_enable),
}
]
async def run_scheduled_action(self, action_key: str, context: Dict[str, Any]) -> Dict[str, Any]:
"""执行插件调度动作。"""
if action_key != "douyu_daily_report_push":
return {"success": False, "summary": f"不支持动作: {action_key}", "detail": {}}
# 调度器注入 bot保证定时任务也能发消息。
self.bot = context.get("bot") or self.bot
if not self._daily_report_enable:
return {"success": True, "summary": "斗鱼每日报告已关闭,跳过执行", "detail": {"enabled": False}}
if not self.redis_manager or not self.bot:
return {"success": False, "summary": "斗鱼每日报告执行失败:依赖未就绪(redis/bot)", "detail": {}}
payload = context.get("payload") or {}
# 支持后台手动触发时覆盖 anchor_day便于补发历史某天日报。
anchor_day = str(payload.get("anchor_day") or "").strip()
if not anchor_day:
anchor_day = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
force = bool(payload.get("force", False))
force_regenerate = bool(payload.get("force_regenerate", False))
target_groups = [str(g).strip() for g in (context.get("target_groups") or []) if str(g).strip()]
if not target_groups:
target_groups = GroupBotManager.get_group_list()
delivered_groups: List[str] = []
failed_groups: Dict[str, str] = {}
for gid in target_groups:
try:
# 按群推送:内部会再基于斗鱼订阅与插件权限做二次过滤。
delivered = await self._send_daily_reports(
anchor_day=anchor_day,
target_group_id=gid,
force=force,
force_regenerate=force_regenerate,
)
if delivered:
delivered_groups.append(gid)
except Exception as e:
failed_groups[gid] = self._format_exception(e)
return {
"success": len(failed_groups) == 0,
"summary": (
f"斗鱼日报任务完成: 日期{anchor_day}, 目标群{len(target_groups)}个, "
f"成功发送群{len(delivered_groups)}个, 失败群{len(failed_groups)}"
),
"detail": {
"anchor_day": anchor_day,
"force": force,
"force_regenerate": force_regenerate,
"target_groups": target_groups,
"delivered_groups": delivered_groups,
"failed_groups": failed_groups,
},
}
def initialize(self, context: Dict[str, Any]) -> bool:
try:
dbm = DBConnectionManager.get_instance()