From 6fe73d574896de445456307fc02b5d6eb15fd3ac Mon Sep 17 00:00:00 2001 From: sansan <77180927+sansan0@users.noreply.github.com> Date: Sat, 30 Aug 2025 21:42:35 +0800 Subject: [PATCH] v2.1.0 --- config/config.yaml | 10 +++ main.py | 182 +++++++++++++++++++++++++++++++++++++-------- readme.md | 53 +++++++++++-- version | 2 +- 4 files changed, 209 insertions(+), 38 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index aa4dbc9..83f62aa 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -33,6 +33,16 @@ notification: batch_send_interval: 1 # 批次发送间隔(秒) feishu_message_separator: "━━━━━━━━━━━━━━━━━━━" # feishu 消息分割线 + silent_push: + enabled: false # 是否启用静默推送模式,如果 true,则启用 + # 因为我们白嫖的 github 服务器执行时间不稳定,所以时间范围要根据实际尽可能大一点,留足 2 小时 + # 如果你想寻求稳定的按时的推送,建议部署在个人的服务器上 + time_range: + start: "20:00" # 推送时间范围开始(北京时间) + end: "22:00" # 推送时间范围结束(北京时间) + once_per_day: true # 每天在时间范围内只推送一次,如果 false,则时间范围内每次执行都推送一次 + push_record_retention_days: 7 # 推送记录保留天数 + # 请务必妥善保管好 webhooks,不要公开 # 如果你以 fork 的方式将本项目部署在 GitHub 上,请勿在此填写任何 webhooks,而是将 webhooks 填入 GitHub Secret # 不然轻则手机上收到奇怪的广告推送,重则存在一定的安全隐患 diff --git a/main.py b/main.py index 30f00bc..6e5c434 100644 --- a/main.py +++ b/main.py @@ -15,7 +15,7 @@ import requests import yaml -VERSION = "2.0.4" +VERSION = "2.1.0" # === 配置管理 === @@ -47,6 +47,27 @@ def load_config(): "FEISHU_MESSAGE_SEPARATOR": config_data["notification"][ "feishu_message_separator" ], + "SILENT_PUSH": { + "ENABLED": config_data["notification"] + .get("silent_push", {}) + .get("enabled", False), + "TIME_RANGE": { + "START": config_data["notification"] + .get("silent_push", {}) + .get("time_range", {}) + .get("start", "08:00"), + "END": config_data["notification"] + .get("silent_push", {}) + .get("time_range", {}) + .get("end", "22:00"), + }, + "ONCE_PER_DAY": config_data["notification"] + .get("silent_push", {}) + .get("once_per_day", True), + "RECORD_RETENTION_DAYS": config_data["notification"] + .get("silent_push", {}) + .get("push_record_retention_days", 7), + }, "WEIGHT_CONFIG": { "RANK_WEIGHT": config_data["weight"]["rank_weight"], "FREQUENCY_WEIGHT": config_data["weight"]["frequency_weight"], @@ -216,6 +237,81 @@ def html_escape(text: str) -> str: ) +# === 推送记录管理 === +class PushRecordManager: + """推送记录管理器""" + + def __init__(self): + self.record_dir = Path("output") / ".push_records" + self.ensure_record_dir() + self.cleanup_old_records() + + def ensure_record_dir(self): + """确保记录目录存在""" + self.record_dir.mkdir(parents=True, exist_ok=True) + + def get_today_record_file(self) -> Path: + """获取今天的记录文件路径""" + today = get_beijing_time().strftime("%Y%m%d") + return self.record_dir / f"push_record_{today}.json" + + def cleanup_old_records(self): + """清理过期的推送记录""" + retention_days = CONFIG["SILENT_PUSH"]["RECORD_RETENTION_DAYS"] + current_time = get_beijing_time() + + for record_file in self.record_dir.glob("push_record_*.json"): + try: + date_str = record_file.stem.replace("push_record_", "") + file_date = datetime.strptime(date_str, "%Y%m%d") + file_date = pytz.timezone("Asia/Shanghai").localize(file_date) + + if (current_time - file_date).days > retention_days: + record_file.unlink() + print(f"清理过期推送记录: {record_file.name}") + except Exception as e: + print(f"清理记录文件失败 {record_file}: {e}") + + def has_pushed_today(self) -> bool: + """检查今天是否已经推送过""" + record_file = self.get_today_record_file() + + if not record_file.exists(): + return False + + try: + with open(record_file, "r", encoding="utf-8") as f: + record = json.load(f) + return record.get("pushed", False) + except Exception as e: + print(f"读取推送记录失败: {e}") + return False + + def record_push(self, report_type: str): + """记录推送""" + record_file = self.get_today_record_file() + now = get_beijing_time() + + record = { + "pushed": True, + "push_time": now.strftime("%Y-%m-%d %H:%M:%S"), + "report_type": report_type, + } + + try: + with open(record_file, "w", encoding="utf-8") as f: + json.dump(record, f, ensure_ascii=False, indent=2) + print(f"推送记录已保存: {report_type} at {now.strftime('%H:%M:%S')}") + except Exception as e: + print(f"保存推送记录失败: {e}") + + def is_in_time_range(self, start_time: str, end_time: str) -> bool: + """检查当前时间是否在指定时间范围内""" + now = get_beijing_time() + current_time = now.strftime("%H:%M") + return start_time <= current_time <= end_time + + # === 数据获取 === class DataFetcher: """数据获取器""" @@ -1778,18 +1874,18 @@ def render_html_content(
@@ -89,12 +91,22 @@
### **智能推送策略**
-三种推送模式:
+**三种推送模式**:
- **📈 投资者/交易员** → 选择 `incremental`,及时获取新增资讯
- **📰 自媒体人/内容创作者** → 选择 `current`,掌握实时热点趋势
- **📋 企业管理者/普通用户** → 选择 `daily`,定时获取完整日报
+
+**静默推送模式**:
+
+支持时间窗口控制,避免非工作时间的消息打扰:
+
+- **时间范围控制**:设定推送时间窗口(如 9:00-18:00),仅在指定时间内推送
+- **适用场景**:
+ - 时间内每次执行都推送
+ - 时间范围内只推送一次
+
### **精准内容筛选**
设置个人关键词(如:AI、比亚迪、教育政策),只推送相关热点,过滤无关信息
@@ -179,8 +191,34 @@ GitHub 一键 Fork 即可使用,无需编程基础。