mirror of
https://gitee.com/houhuan/TrendRadar.git
synced 2025-12-21 16:07:15 +08:00
v2.1.0
This commit is contained in:
parent
45b6ee3b15
commit
6fe73d5748
@ -33,6 +33,16 @@ notification:
|
|||||||
batch_send_interval: 1 # 批次发送间隔(秒)
|
batch_send_interval: 1 # 批次发送间隔(秒)
|
||||||
feishu_message_separator: "━━━━━━━━━━━━━━━━━━━" # feishu 消息分割线
|
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,不要公开
|
# 请务必妥善保管好 webhooks,不要公开
|
||||||
# 如果你以 fork 的方式将本项目部署在 GitHub 上,请勿在此填写任何 webhooks,而是将 webhooks 填入 GitHub Secret
|
# 如果你以 fork 的方式将本项目部署在 GitHub 上,请勿在此填写任何 webhooks,而是将 webhooks 填入 GitHub Secret
|
||||||
# 不然轻则手机上收到奇怪的广告推送,重则存在一定的安全隐患
|
# 不然轻则手机上收到奇怪的广告推送,重则存在一定的安全隐患
|
||||||
|
|||||||
132
main.py
132
main.py
@ -15,7 +15,7 @@ import requests
|
|||||||
import yaml
|
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": config_data["notification"][
|
||||||
"feishu_message_separator"
|
"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": {
|
"WEIGHT_CONFIG": {
|
||||||
"RANK_WEIGHT": config_data["weight"]["rank_weight"],
|
"RANK_WEIGHT": config_data["weight"]["rank_weight"],
|
||||||
"FREQUENCY_WEIGHT": config_data["weight"]["frequency_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:
|
class DataFetcher:
|
||||||
"""数据获取器"""
|
"""数据获取器"""
|
||||||
@ -1799,7 +1895,7 @@ def render_html_content(
|
|||||||
<span class="info-value">"""
|
<span class="info-value">"""
|
||||||
|
|
||||||
now = get_beijing_time()
|
now = get_beijing_time()
|
||||||
html += now.strftime('%m-%d %H:%M')
|
html += now.strftime("%m-%d %H:%M")
|
||||||
|
|
||||||
html += """</span>
|
html += """</span>
|
||||||
</div>
|
</div>
|
||||||
@ -1885,8 +1981,14 @@ def render_html_content(
|
|||||||
time_display = title_data.get("time_display", "")
|
time_display = title_data.get("time_display", "")
|
||||||
if time_display:
|
if time_display:
|
||||||
# 简化时间显示格式,将波浪线替换为~
|
# 简化时间显示格式,将波浪线替换为~
|
||||||
simplified_time = time_display.replace(" ~ ", "~").replace("[", "").replace("]", "")
|
simplified_time = (
|
||||||
html += f'<span class="time-info">{html_escape(simplified_time)}</span>'
|
time_display.replace(" ~ ", "~")
|
||||||
|
.replace("[", "")
|
||||||
|
.replace("]", "")
|
||||||
|
)
|
||||||
|
html += (
|
||||||
|
f'<span class="time-info">{html_escape(simplified_time)}</span>'
|
||||||
|
)
|
||||||
|
|
||||||
# 处理出现次数
|
# 处理出现次数
|
||||||
count_info = title_data.get("count", 1)
|
count_info = title_data.get("count", 1)
|
||||||
@ -2511,6 +2613,23 @@ def send_to_webhooks(
|
|||||||
"""发送数据到多个webhook平台"""
|
"""发送数据到多个webhook平台"""
|
||||||
results = {}
|
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)
|
report_data = prepare_report_data(stats, failed_ids, new_titles, id_to_name, mode)
|
||||||
|
|
||||||
feishu_url = CONFIG["FEISHU_WEBHOOK_URL"]
|
feishu_url = CONFIG["FEISHU_WEBHOOK_URL"]
|
||||||
@ -2554,6 +2673,11 @@ def send_to_webhooks(
|
|||||||
if not results:
|
if not results:
|
||||||
print("未配置任何webhook URL,跳过通知发送")
|
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
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
53
readme.md
53
readme.md
@ -7,7 +7,7 @@
|
|||||||
[](https://github.com/sansan0/TrendRadar/stargazers)
|
[](https://github.com/sansan0/TrendRadar/stargazers)
|
||||||
[](https://github.com/sansan0/TrendRadar/network/members)
|
[](https://github.com/sansan0/TrendRadar/network/members)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/sansan0/TrendRadar)
|
[](https://github.com/sansan0/TrendRadar)
|
||||||
|
|
||||||
[](https://work.weixin.qq.com/)
|
[](https://work.weixin.qq.com/)
|
||||||
[](https://telegram.org/)
|
[](https://telegram.org/)
|
||||||
@ -25,7 +25,7 @@
|
|||||||
> 遇到问题提 issues,或【硅基茶水间】公众号留言
|
> 遇到问题提 issues,或【硅基茶水间】公众号留言
|
||||||
|
|
||||||
<details>
|
<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 | 88 | 2025.8.13 下午 | |
|
||||||
| 2*D | 1 | 2025.8.13 上午 | |
|
| 2*D | 1 | 2025.8.13 上午 | |
|
||||||
| S*o | 1 | 2025.8.05 | 支持一下 |
|
| S*o | 1 | 2025.8.05 | 支持一下 |
|
||||||
@ -56,10 +57,11 @@
|
|||||||
| **龙 | 10 | 2025.7.29 | 支持一下 |
|
| **龙 | 10 | 2025.7.29 | 支持一下 |
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>👉 "手机推送通知系列" 挖坑</strong></summary>
|
<summary><strong>👉 "手机推送通知系列" 挖坑(暂时鸽)</strong></summary>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
截图中只支持一个渠道,大家有什么好的建议和想法可以公众号留言,完善好后开源
|
截图中只支持一个渠道,大家有什么好的建议和想法可以公众号留言,完善好后开源
|
||||||
|
这个暂时没有人来和我讨论,我先鸽为敬嘿嘿
|
||||||
|
|
||||||
<img src="_image/next.jpg" width="300" title="github"/>
|
<img src="_image/next.jpg" width="300" title="github"/>
|
||||||
|
|
||||||
@ -89,12 +91,22 @@
|
|||||||
|
|
||||||
### **智能推送策略**
|
### **智能推送策略**
|
||||||
|
|
||||||
三种推送模式:
|
**三种推送模式**:
|
||||||
|
|
||||||
- **📈 投资者/交易员** → 选择 `incremental`,及时获取新增资讯
|
- **📈 投资者/交易员** → 选择 `incremental`,及时获取新增资讯
|
||||||
- **📰 自媒体人/内容创作者** → 选择 `current`,掌握实时热点趋势
|
- **📰 自媒体人/内容创作者** → 选择 `current`,掌握实时热点趋势
|
||||||
- **📋 企业管理者/普通用户** → 选择 `daily`,定时获取完整日报
|
- **📋 企业管理者/普通用户** → 选择 `daily`,定时获取完整日报
|
||||||
|
|
||||||
|
|
||||||
|
**静默推送模式**:
|
||||||
|
|
||||||
|
支持时间窗口控制,避免非工作时间的消息打扰:
|
||||||
|
|
||||||
|
- **时间范围控制**:设定推送时间窗口(如 9:00-18:00),仅在指定时间内推送
|
||||||
|
- **适用场景**:
|
||||||
|
- 时间内每次执行都推送
|
||||||
|
- 时间范围内只推送一次
|
||||||
|
|
||||||
### **精准内容筛选**
|
### **精准内容筛选**
|
||||||
|
|
||||||
设置个人关键词(如:AI、比亚迪、教育政策),只推送相关热点,过滤无关信息
|
设置个人关键词(如:AI、比亚迪、教育政策),只推送相关热点,过滤无关信息
|
||||||
@ -179,8 +191,34 @@ GitHub 一键 Fork 即可使用,无需编程基础。
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
>**升级说明:**
|
>**升级说明:**
|
||||||
|
- **注意**:请通过以下方式更新项目,不要通过 Sync fork 等方式更新
|
||||||
- **小版本更新**:直接在 GitHub 网页编辑器中,用本项目的 `main.py` 代码替换你 fork 仓库中的对应文件
|
- **小版本更新**:直接在 GitHub 网页编辑器中,用本项目的 `main.py` 代码替换你 fork 仓库中的对应文件
|
||||||
- **大版本升级**:从 v1.x 升级到 v2.0 建议删除现有 fork 后重新 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
|
### 2025/08/27 - v2.0.4
|
||||||
|
|
||||||
@ -189,9 +227,6 @@ GitHub 一键 Fork 即可使用,无需编程基础。
|
|||||||
- 如果你以 fork 的方式将本项目部署在 GitHub 上,请将 webhooks 填入 GitHub Secret,而非 config.yaml
|
- 如果你以 fork 的方式将本项目部署在 GitHub 上,请将 webhooks 填入 GitHub Secret,而非 config.yaml
|
||||||
- 如果你已经暴露了 webhooks 或将其填入了 config.yaml,建议删除后重新生成
|
- 如果你已经暴露了 webhooks 或将其填入了 config.yaml,建议删除后重新生成
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><strong>👉 历史更新</strong></summary>
|
|
||||||
|
|
||||||
### 2025/08/06 - v2.0.3
|
### 2025/08/06 - v2.0.3
|
||||||
|
|
||||||
- 优化 github page 的网页版效果,方便移动端使用
|
- 优化 github page 的网页版效果,方便移动端使用
|
||||||
@ -612,7 +647,8 @@ frequency_words.txt 文件增加了一个【必须词】功能,使用 + 号
|
|||||||
- 运行结果将自动保存在仓库的`output`目录中
|
- 运行结果将自动保存在仓库的`output`目录中
|
||||||
- 同时通过配置的机器人发送通知到你的群组
|
- 同时通过配置的机器人发送通知到你的群组
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>👉 自定义监控平台</strong></summary>
|
||||||
|
|
||||||
### 🔧 自定义监控平台
|
### 🔧 自定义监控平台
|
||||||
|
|
||||||
@ -630,6 +666,7 @@ platforms:
|
|||||||
name: "华尔街见闻"
|
name: "华尔街见闻"
|
||||||
# 添加更多平台...
|
# 添加更多平台...
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>👉 Docker 部署</strong></summary>
|
<summary><strong>👉 Docker 部署</strong></summary>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user