Skip to content

Commit a2b9939

Browse files
committed
v3.0.4
1 parent 39a6384 commit a2b9939

File tree

4 files changed

+128
-35
lines changed

4 files changed

+128
-35
lines changed

config/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ notification:
3232
enable_notification: true # 是否启用通知功能,如果 false,则不发送手机通知
3333
message_batch_size: 4000 # 消息分批大小(字节)(这个配置别动)
3434
dingtalk_batch_size: 20000 # 钉钉消息分批大小(字节)(这个配置也别动)
35+
feishu_batch_size: 29000 # 飞书消息分批大小(字节)
3536
batch_send_interval: 1 # 批次发送间隔(秒)
3637
feishu_message_separator: "━━━━━━━━━━━━━━━━━━━" # feishu 消息分割线
3738

main.py

Lines changed: 118 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import yaml
2121

2222

23-
VERSION = "3.0.3"
23+
VERSION = "3.0.4"
2424

2525

2626
# === SMTP邮件配置 ===
@@ -79,6 +79,7 @@ def load_config():
7979
"DINGTALK_BATCH_SIZE": config_data["notification"].get(
8080
"dingtalk_batch_size", 20000
8181
),
82+
"FEISHU_BATCH_SIZE": config_data["notification"].get("feishu_batch_size", 29000),
8283
"BATCH_SEND_INTERVAL": config_data["notification"]["batch_send_interval"],
8384
"FEISHU_MESSAGE_SEPARATOR": config_data["notification"][
8485
"feishu_message_separator"
@@ -2816,6 +2817,8 @@ def split_content_into_batches(
28162817
if max_bytes is None:
28172818
if format_type == "dingtalk":
28182819
max_bytes = CONFIG.get("DINGTALK_BATCH_SIZE", 20000)
2820+
elif format_type == "feishu":
2821+
max_bytes = CONFIG.get("FEISHU_BATCH_SIZE", 29000)
28192822
elif format_type == "ntfy":
28202823
max_bytes = 3800
28212824
else:
@@ -2835,6 +2838,8 @@ def split_content_into_batches(
28352838
base_header = f"总新闻数: {total_titles}\n\n"
28362839
elif format_type == "ntfy":
28372840
base_header = f"**总新闻数:** {total_titles}\n\n"
2841+
elif format_type == "feishu":
2842+
base_header = ""
28382843
elif format_type == "dingtalk":
28392844
base_header = f"**总新闻数:** {total_titles}\n\n"
28402845
base_header += f"**时间:** {now.strftime('%Y-%m-%d %H:%M:%S')}\n\n"
@@ -2854,6 +2859,10 @@ def split_content_into_batches(
28542859
base_footer = f"\n\n> 更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}"
28552860
if update_info:
28562861
base_footer += f"\n> TrendRadar 发现新版本 **{update_info['remote_version']}**,当前 **{update_info['current_version']}**"
2862+
elif format_type == "feishu":
2863+
base_footer = f"\n\n<font color='grey'>更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}</font>"
2864+
if update_info:
2865+
base_footer += f"\n<font color='grey'>TrendRadar 发现新版本 {update_info['remote_version']},当前 {update_info['current_version']}</font>"
28572866
elif format_type == "dingtalk":
28582867
base_footer = f"\n\n> 更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}"
28592868
if update_info:
@@ -2867,6 +2876,8 @@ def split_content_into_batches(
28672876
stats_header = f"📊 热点词汇统计\n\n"
28682877
elif format_type == "ntfy":
28692878
stats_header = f"📊 **热点词汇统计**\n\n"
2879+
elif format_type == "feishu":
2880+
stats_header = f"📊 **热点词汇统计**\n\n"
28702881
elif format_type == "dingtalk":
28712882
stats_header = f"📊 **热点词汇统计**\n\n"
28722883

@@ -2944,6 +2955,13 @@ def split_content_into_batches(
29442955
)
29452956
else:
29462957
word_header = f"📌 {sequence_display} **{word}** : {count}\n\n"
2958+
elif format_type == "feishu":
2959+
if count >= 10:
2960+
word_header = f"🔥 <font color='grey'>{sequence_display}</font> **{word}** : <font color='red'>{count}</font> 条\n\n"
2961+
elif count >= 5:
2962+
word_header = f"📈 <font color='grey'>{sequence_display}</font> **{word}** : <font color='orange'>{count}</font> 条\n\n"
2963+
else:
2964+
word_header = f"📌 <font color='grey'>{sequence_display}</font> **{word}** : {count}\n\n"
29472965
elif format_type == "dingtalk":
29482966
if count >= 10:
29492967
word_header = (
@@ -2972,6 +2990,10 @@ def split_content_into_batches(
29722990
formatted_title = format_title_for_platform(
29732991
"ntfy", first_title_data, show_source=True
29742992
)
2993+
elif format_type == "feishu":
2994+
formatted_title = format_title_for_platform(
2995+
"feishu", first_title_data, show_source=True
2996+
)
29752997
elif format_type == "dingtalk":
29762998
formatted_title = format_title_for_platform(
29772999
"dingtalk", first_title_data, show_source=True
@@ -3017,6 +3039,10 @@ def split_content_into_batches(
30173039
formatted_title = format_title_for_platform(
30183040
"ntfy", title_data, show_source=True
30193041
)
3042+
elif format_type == "feishu":
3043+
formatted_title = format_title_for_platform(
3044+
"feishu", title_data, show_source=True
3045+
)
30203046
elif format_type == "dingtalk":
30213047
formatted_title = format_title_for_platform(
30223048
"dingtalk", title_data, show_source=True
@@ -3050,6 +3076,8 @@ def split_content_into_batches(
30503076
separator = f"\n\n"
30513077
elif format_type == "ntfy":
30523078
separator = f"\n\n"
3079+
elif format_type == "feishu":
3080+
separator = f"\n{CONFIG['FEISHU_MESSAGE_SEPARATOR']}\n\n"
30533081
elif format_type == "dingtalk":
30543082
separator = f"\n---\n\n"
30553083

@@ -3071,6 +3099,8 @@ def split_content_into_batches(
30713099
)
30723100
elif format_type == "ntfy":
30733101
new_header = f"\n\n🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n"
3102+
elif format_type == "feishu":
3103+
new_header = f"\n{CONFIG['FEISHU_MESSAGE_SEPARATOR']}\n\n🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n"
30743104
elif format_type == "dingtalk":
30753105
new_header = f"\n---\n\n🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n"
30763106

@@ -3096,6 +3126,8 @@ def split_content_into_batches(
30963126
source_header = f"{source_data['source_name']} ({len(source_data['titles'])} 条):\n\n"
30973127
elif format_type == "ntfy":
30983128
source_header = f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n"
3129+
elif format_type == "feishu":
3130+
source_header = f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n"
30993131
elif format_type == "dingtalk":
31003132
source_header = f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n"
31013133

@@ -3114,6 +3146,10 @@ def split_content_into_batches(
31143146
formatted_title = format_title_for_platform(
31153147
"telegram", title_data_copy, show_source=False
31163148
)
3149+
elif format_type == "feishu":
3150+
formatted_title = format_title_for_platform(
3151+
"feishu", title_data_copy, show_source=False
3152+
)
31173153
elif format_type == "dingtalk":
31183154
formatted_title = format_title_for_platform(
31193155
"dingtalk", title_data_copy, show_source=False
@@ -3155,6 +3191,10 @@ def split_content_into_batches(
31553191
formatted_title = format_title_for_platform(
31563192
"telegram", title_data_copy, show_source=False
31573193
)
3194+
elif format_type == "feishu":
3195+
formatted_title = format_title_for_platform(
3196+
"feishu", title_data_copy, show_source=False
3197+
)
31583198
elif format_type == "dingtalk":
31593199
formatted_title = format_title_for_platform(
31603200
"dingtalk", title_data_copy, show_source=False
@@ -3187,6 +3227,8 @@ def split_content_into_batches(
31873227
failed_header = f"\n\n⚠️ 数据获取失败的平台:\n\n"
31883228
elif format_type == "ntfy":
31893229
failed_header = f"\n\n⚠️ **数据获取失败的平台:**\n\n"
3230+
elif format_type == "feishu":
3231+
failed_header = f"\n{CONFIG['FEISHU_MESSAGE_SEPARATOR']}\n\n⚠️ **数据获取失败的平台:**\n\n"
31903232
elif format_type == "dingtalk":
31913233
failed_header = f"\n---\n\n⚠️ **数据获取失败的平台:**\n\n"
31923234

@@ -3204,7 +3246,9 @@ def split_content_into_batches(
32043246
current_batch_has_content = True
32053247

32063248
for i, id_value in enumerate(report_data["failed_ids"], 1):
3207-
if format_type == "dingtalk":
3249+
if format_type == "feishu":
3250+
failed_line = f" • <font color='red'>{id_value}</font>\n"
3251+
elif format_type == "dingtalk":
32083252
failed_line = f" • **{id_value}**\n"
32093253
else:
32103254
failed_line = f" • {id_value}\n"
@@ -3358,42 +3402,86 @@ def send_to_feishu(
33583402
proxy_url: Optional[str] = None,
33593403
mode: str = "daily",
33603404
) -> bool:
3361-
"""发送到飞书"""
3405+
"""发送到飞书(支持分批发送)"""
33623406
headers = {"Content-Type": "application/json"}
3407+
proxies = None
3408+
if proxy_url:
3409+
proxies = {"http": proxy_url, "https": proxy_url}
33633410

3364-
text_content = render_feishu_content(report_data, update_info, mode)
3365-
total_titles = sum(
3366-
len(stat["titles"]) for stat in report_data["stats"] if stat["count"] > 0
3411+
# 获取分批内容,使用飞书专用的批次大小
3412+
batches = split_content_into_batches(
3413+
report_data,
3414+
"feishu",
3415+
update_info,
3416+
max_bytes=CONFIG.get("FEISHU_BATCH_SIZE", 29000),
3417+
mode=mode,
33673418
)
33683419

3369-
now = get_beijing_time()
3370-
payload = {
3371-
"msg_type": "text",
3372-
"content": {
3373-
"total_titles": total_titles,
3374-
"timestamp": now.strftime("%Y-%m-%d %H:%M:%S"),
3375-
"report_type": report_type,
3376-
"text": text_content,
3377-
},
3378-
}
3420+
print(f"飞书消息分为 {len(batches)} 批次发送 [{report_type}]")
33793421

3380-
proxies = None
3381-
if proxy_url:
3382-
proxies = {"http": proxy_url, "https": proxy_url}
3422+
# 逐批发送
3423+
for i, batch_content in enumerate(batches, 1):
3424+
batch_size = len(batch_content.encode("utf-8"))
3425+
print(
3426+
f"发送飞书第 {i}/{len(batches)} 批次,大小:{batch_size} 字节 [{report_type}]"
3427+
)
33833428

3384-
try:
3385-
response = requests.post(
3386-
webhook_url, headers=headers, json=payload, proxies=proxies, timeout=30
3429+
# 添加批次标识
3430+
if len(batches) > 1:
3431+
batch_header = f"**[第 {i}/{len(batches)} 批次]**\n\n"
3432+
# 将批次标识插入到适当位置(在统计标题之后)
3433+
if "📊 **热点词汇统计**" in batch_content:
3434+
batch_content = batch_content.replace(
3435+
"📊 **热点词汇统计**\n\n", f"📊 **热点词汇统计** {batch_header}"
3436+
)
3437+
else:
3438+
# 如果没有统计标题,直接在开头添加
3439+
batch_content = batch_header + batch_content
3440+
3441+
total_titles = sum(
3442+
len(stat["titles"]) for stat in report_data["stats"] if stat["count"] > 0
33873443
)
3388-
if response.status_code == 200:
3389-
print(f"飞书通知发送成功 [{report_type}]")
3390-
return True
3391-
else:
3392-
print(f"飞书通知发送失败 [{report_type}],状态码:{response.status_code}")
3444+
now = get_beijing_time()
3445+
3446+
payload = {
3447+
"msg_type": "text",
3448+
"content": {
3449+
"total_titles": total_titles,
3450+
"timestamp": now.strftime("%Y-%m-%d %H:%M:%S"),
3451+
"report_type": report_type,
3452+
"text": batch_content,
3453+
},
3454+
}
3455+
3456+
try:
3457+
response = requests.post(
3458+
webhook_url, headers=headers, json=payload, proxies=proxies, timeout=30
3459+
)
3460+
if response.status_code == 200:
3461+
result = response.json()
3462+
# 检查飞书的响应状态
3463+
if result.get("StatusCode") == 0 or result.get("code") == 0:
3464+
print(f"飞书第 {i}/{len(batches)} 批次发送成功 [{report_type}]")
3465+
# 批次间间隔
3466+
if i < len(batches):
3467+
time.sleep(CONFIG["BATCH_SEND_INTERVAL"])
3468+
else:
3469+
error_msg = result.get("msg") or result.get("StatusMessage", "未知错误")
3470+
print(
3471+
f"飞书第 {i}/{len(batches)} 批次发送失败 [{report_type}],错误:{error_msg}"
3472+
)
3473+
return False
3474+
else:
3475+
print(
3476+
f"飞书第 {i}/{len(batches)} 批次发送失败 [{report_type}],状态码:{response.status_code}"
3477+
)
3478+
return False
3479+
except Exception as e:
3480+
print(f"飞书第 {i}/{len(batches)} 批次发送出错 [{report_type}]:{e}")
33933481
return False
3394-
except Exception as e:
3395-
print(f"飞书通知发送出错 [{report_type}]{e}")
3396-
return False
3482+
3483+
print(f"飞书所有 {len(batches)} 批次发送完成 [{report_type}]")
3484+
return True
33973485

33983486

33993487
def send_to_dingtalk(

readme.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
[![GitHub Stars](https://img.shields.io/github/stars/sansan0/TrendRadar?style=flat-square&logo=github&color=yellow)](https://github.com/sansan0/TrendRadar/stargazers)
1212
[![GitHub Forks](https://img.shields.io/github/forks/sansan0/TrendRadar?style=flat-square&logo=github&color=blue)](https://github.com/sansan0/TrendRadar/network/members)
1313
[![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg?style=flat-square)](LICENSE)
14-
[![Version](https://img.shields.io/badge/version-v3.0.3-blue.svg)](https://github.com/sansan0/TrendRadar)
14+
[![Version](https://img.shields.io/badge/version-v3.0.4-blue.svg)](https://github.com/sansan0/TrendRadar)
1515
[![MCP](https://img.shields.io/badge/MCP-v1.0.1-green.svg)](https://github.com/sansan0/TrendRadar)
1616

1717
[![企业微信通知](https://img.shields.io/badge/企业微信-通知-00D4AA?style=flat-square)](https://work.weixin.qq.com/)
@@ -522,14 +522,18 @@ GitHub 一键 Fork 即可使用,无需编程基础。
522522
- 统一所有工具的时间参数格式
523523

524524

525-
### 2025/10/23 - v3.0.3
525+
### 2025/10/31 - v3.0.4
526526

527-
- 扩大 ntfy 错误信息显示范围
527+
- 解决飞书因推送内容过长而产生的错误,实现了分批推送
528528

529529

530530
<details>
531531
<summary><strong>👉 历史更新</strong></summary>
532532

533+
### 2025/10/23 - v3.0.3
534+
535+
- 扩大 ntfy 错误信息显示范围
536+
533537

534538
### 2025/10/21 - v3.0.2
535539

@@ -785,7 +789,7 @@ frequency_words.txt 文件增加了一个【必须词】功能,使用 + 号
785789
786790
## 🚀 快速开始
787791
788-
> 部署成功后,新闻数据一般一小时后才会更新,如想加快,可参照【第4步】手动测试配置效果
792+
> 配置完成后,新闻数据一小时后才会更新,如想加快,可参照【第4步】手动测试配置效果
789793
790794
1. **Fork 本项目**到你的 GitHub 账户
791795

version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.0.3
1+
3.0.4

0 commit comments

Comments
 (0)