This commit is contained in:
sansan 2025-08-30 21:42:35 +08:00
parent 45b6ee3b15
commit 6fe73d5748
4 changed files with 209 additions and 38 deletions

View File

@ -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
# 不然轻则手机上收到奇怪的广告推送,重则存在一定的安全隐患

132
main.py
View File

@ -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:
"""数据获取器"""
@ -1799,7 +1895,7 @@ def render_html_content(
<span class="info-value">"""
now = get_beijing_time()
html += now.strftime('%m-%d %H:%M')
html += now.strftime("%m-%d %H:%M")
html += """</span>
</div>
@ -1885,8 +1981,14 @@ def render_html_content(
time_display = title_data.get("time_display", "")
if time_display:
# 简化时间显示格式,将波浪线替换为~
simplified_time = time_display.replace(" ~ ", "~").replace("[", "").replace("]", "")
html += f'<span class="time-info">{html_escape(simplified_time)}</span>'
simplified_time = (
time_display.replace(" ~ ", "~")
.replace("[", "")
.replace("]", "")
)
html += (
f'<span class="time-info">{html_escape(simplified_time)}</span>'
)
# 处理出现次数
count_info = title_data.get("count", 1)
@ -2511,6 +2613,23 @@ def send_to_webhooks(
"""发送数据到多个webhook平台"""
results = {}
if CONFIG["SILENT_PUSH"]["ENABLED"]:
push_manager = PushRecordManager()
time_range_start = CONFIG["SILENT_PUSH"]["TIME_RANGE"]["START"]
time_range_end = CONFIG["SILENT_PUSH"]["TIME_RANGE"]["END"]
if not push_manager.is_in_time_range(time_range_start, time_range_end):
now = get_beijing_time()
print(f"静默模式:当前时间 {now.strftime('%H:%M')} 不在推送时间范围 {time_range_start}-{time_range_end} 内,跳过推送")
return results
if CONFIG["SILENT_PUSH"]["ONCE_PER_DAY"]:
if push_manager.has_pushed_today():
print(f"静默模式:今天已推送过,跳过本次推送")
return results
else:
print(f"静默模式:今天首次推送")
report_data = prepare_report_data(stats, failed_ids, new_titles, id_to_name, mode)
feishu_url = CONFIG["FEISHU_WEBHOOK_URL"]
@ -2554,6 +2673,11 @@ def send_to_webhooks(
if not results:
print("未配置任何webhook URL跳过通知发送")
# 如果成功发送了任何通知,且启用了每天只推一次,则记录推送
if CONFIG["SILENT_PUSH"]["ENABLED"] and CONFIG["SILENT_PUSH"]["ONCE_PER_DAY"] and any(results.values()):
push_manager = PushRecordManager()
push_manager.record_push(report_type)
return results

View File

@ -7,7 +7,7 @@
[![GitHub Stars](https://img.shields.io/github/stars/sansan0/TrendRadar?style=flat-square&logo=github&color=yellow)](https://github.com/sansan0/TrendRadar/stargazers)
[![GitHub Forks](https://img.shields.io/github/forks/sansan0/TrendRadar?style=flat-square&logo=github&color=blue)](https://github.com/sansan0/TrendRadar/network/members)
[![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg?style=flat-square)](LICENSE)
[![Version](https://img.shields.io/badge/version-v2.0.4-green.svg?style=flat-square)](https://github.com/sansan0/TrendRadar)
[![Version](https://img.shields.io/badge/version-v2.1.0-green.svg?style=flat-square)](https://github.com/sansan0/TrendRadar)
[![企业微信通知](https://img.shields.io/badge/企业微信-通知支持-00D4AA?style=flat-square)](https://work.weixin.qq.com/)
[![Telegram通知](https://img.shields.io/badge/Telegram-通知支持-00D4AA?style=flat-square)](https://telegram.org/)
@ -25,7 +25,7 @@
> 遇到问题提 issues或【硅基茶水间】公众号留言
<details>
<summary>👉 点击查看<strong>致谢名单 (当前 12 个)</strong></summary>
<summary>👉 点击查看致谢名单 (当前 <strong>13</strong> 个)</summary>
### 数据支持
@ -45,6 +45,7 @@
| 点赞人 | 金额 | 日期 | 备注 |
| :-------------------------: | :----: | :----: | :-----------------------: |
| *下 | 1 | 2025.8.30 | |
| 2*D | 88 | 2025.8.13 下午 | |
| 2*D | 1 | 2025.8.13 上午 | |
| S*o | 1 | 2025.8.05 | 支持一下 |
@ -56,10 +57,11 @@
| **龙 | 10 | 2025.7.29 | 支持一下 |
<details>
<summary><strong>👉 "手机推送通知系列" 挖坑</strong></summary>
<summary><strong>👉 "手机推送通知系列" 挖坑(暂时鸽)</strong></summary>
<br>
截图中只支持一个渠道,大家有什么好的建议和想法可以公众号留言,完善好后开源
这个暂时没有人来和我讨论,我先鸽为敬嘿嘿
<img src="_image/next.jpg" width="300" title="github"/>
@ -89,12 +91,22 @@
### **智能推送策略**
三种推送模式:
**三种推送模式**
- **📈 投资者/交易员** → 选择 `incremental`,及时获取新增资讯
- **📰 自媒体人/内容创作者** → 选择 `current`,掌握实时热点趋势
- **📋 企业管理者/普通用户** → 选择 `daily`,定时获取完整日报
**静默推送模式**
支持时间窗口控制,避免非工作时间的消息打扰:
- **时间范围控制**:设定推送时间窗口(如 9:00-18:00仅在指定时间内推送
- **适用场景**
- 时间内每次执行都推送
- 时间范围内只推送一次
### **精准内容筛选**
设置个人关键词AI、比亚迪、教育政策只推送相关热点过滤无关信息
@ -179,8 +191,34 @@ GitHub 一键 Fork 即可使用,无需编程基础。
</details>
>**升级说明:**
- **注意**:请通过以下方式更新项目,不要通过 Sync fork 等方式更新
- **小版本更新**:直接在 GitHub 网页编辑器中,用本项目的 `main.py` 代码替换你 fork 仓库中的对应文件
- **大版本升级**:从 v1.x 升级到 v2.0 建议删除现有 fork 后重新 fork这样更省力且避免配置冲突
- **或者**:根据更新日志的特别说明升级
### 2025/08/30 - v2.1.0
> 感谢各位朋友的支持与厚爱,特别感谢:
>
> **fork 并为项目点 star** 的观众们,你们的认可是我前进的动力
>
> **关注公众号并积极互动** 的读者们,你们的留言和点赞让内容更有温度
>
> **给予资金点赞支持** 的朋友们,你们的慷慨让项目得以持续发展
>
> 下一次**新功能**,大概会是 ai 分析功能(大概(●'◡'●)
**核心改进**
- **推送逻辑优化**:从"每次执行都推送"改为"时间窗口内可控推送"
- **时间窗口控制**:可设定推送时间范围,避免非工作时间打扰
- **推送频率可选**:时间段内支持单次推送或多次推送
**更新提示**
- 本功能默认关闭,需手动开启
- 同时更新 main.py 和 config.yaml
<details>
<summary><strong>👉 历史更新</strong></summary>
### 2025/08/27 - v2.0.4
@ -189,9 +227,6 @@ GitHub 一键 Fork 即可使用,无需编程基础。
- 如果你以 fork 的方式将本项目部署在 GitHub 上,请将 webhooks 填入 GitHub Secret而非 config.yaml
- 如果你已经暴露了 webhooks 或将其填入了 config.yaml建议删除后重新生成
<details>
<summary><strong>👉 历史更新</strong></summary>
### 2025/08/06 - v2.0.3
- 优化 github page 的网页版效果,方便移动端使用
@ -612,7 +647,8 @@ frequency_words.txt 文件增加了一个【必须词】功能,使用 + 号
- 运行结果将自动保存在仓库的`output`目录中
- 同时通过配置的机器人发送通知到你的群组
<details>
<summary><strong>👉 自定义监控平台</strong></summary>
### 🔧 自定义监控平台
@ -630,6 +666,7 @@ platforms:
name: "华尔街见闻"
# 添加更多平台...
```
</details>
<details>
<summary><strong>👉 Docker 部署</strong></summary>

View File

@ -1 +1 @@
2.0.4
2.1.0