diff --git a/config/config.yaml b/config/config.yaml index 4945c21..37580ab 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -32,6 +32,7 @@ notification: enable_notification: true # 是否启用通知功能,如果 false,则不发送手机通知 message_batch_size: 4000 # 消息分批大小(字节)(这个配置别动) dingtalk_batch_size: 20000 # 钉钉消息分批大小(字节)(这个配置也别动) + feishu_batch_size: 29000 # 飞书消息分批大小(字节) batch_send_interval: 1 # 批次发送间隔(秒) feishu_message_separator: "━━━━━━━━━━━━━━━━━━━" # feishu 消息分割线 diff --git a/main.py b/main.py index 95fbd39..6bbb0bd 100644 --- a/main.py +++ b/main.py @@ -20,7 +20,7 @@ import requests import yaml -VERSION = "3.0.3" +VERSION = "3.0.4" # === SMTP邮件配置 === @@ -79,6 +79,7 @@ def load_config(): "DINGTALK_BATCH_SIZE": config_data["notification"].get( "dingtalk_batch_size", 20000 ), + "FEISHU_BATCH_SIZE": config_data["notification"].get("feishu_batch_size", 29000), "BATCH_SEND_INTERVAL": config_data["notification"]["batch_send_interval"], "FEISHU_MESSAGE_SEPARATOR": config_data["notification"][ "feishu_message_separator" @@ -2816,6 +2817,8 @@ def split_content_into_batches( if max_bytes is None: if format_type == "dingtalk": max_bytes = CONFIG.get("DINGTALK_BATCH_SIZE", 20000) + elif format_type == "feishu": + max_bytes = CONFIG.get("FEISHU_BATCH_SIZE", 29000) elif format_type == "ntfy": max_bytes = 3800 else: @@ -2835,6 +2838,8 @@ def split_content_into_batches( base_header = f"总新闻数: {total_titles}\n\n" elif format_type == "ntfy": base_header = f"**总新闻数:** {total_titles}\n\n" + elif format_type == "feishu": + base_header = "" elif format_type == "dingtalk": base_header = f"**总新闻数:** {total_titles}\n\n" base_header += f"**时间:** {now.strftime('%Y-%m-%d %H:%M:%S')}\n\n" @@ -2854,6 +2859,10 @@ def split_content_into_batches( base_footer = f"\n\n> 更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}" if update_info: base_footer += f"\n> TrendRadar 发现新版本 **{update_info['remote_version']}**,当前 **{update_info['current_version']}**" + elif format_type == "feishu": + base_footer = f"\n\n更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}" + if update_info: + base_footer += f"\nTrendRadar 发现新版本 {update_info['remote_version']},当前 {update_info['current_version']}" elif format_type == "dingtalk": base_footer = f"\n\n> 更新时间:{now.strftime('%Y-%m-%d %H:%M:%S')}" if update_info: @@ -2867,6 +2876,8 @@ def split_content_into_batches( stats_header = f"📊 热点词汇统计\n\n" elif format_type == "ntfy": stats_header = f"📊 **热点词汇统计**\n\n" + elif format_type == "feishu": + stats_header = f"📊 **热点词汇统计**\n\n" elif format_type == "dingtalk": stats_header = f"📊 **热点词汇统计**\n\n" @@ -2944,6 +2955,13 @@ def split_content_into_batches( ) else: word_header = f"📌 {sequence_display} **{word}** : {count} 条\n\n" + elif format_type == "feishu": + if count >= 10: + word_header = f"🔥 {sequence_display} **{word}** : {count} 条\n\n" + elif count >= 5: + word_header = f"📈 {sequence_display} **{word}** : {count} 条\n\n" + else: + word_header = f"📌 {sequence_display} **{word}** : {count} 条\n\n" elif format_type == "dingtalk": if count >= 10: word_header = ( @@ -2972,6 +2990,10 @@ def split_content_into_batches( formatted_title = format_title_for_platform( "ntfy", first_title_data, show_source=True ) + elif format_type == "feishu": + formatted_title = format_title_for_platform( + "feishu", first_title_data, show_source=True + ) elif format_type == "dingtalk": formatted_title = format_title_for_platform( "dingtalk", first_title_data, show_source=True @@ -3017,6 +3039,10 @@ def split_content_into_batches( formatted_title = format_title_for_platform( "ntfy", title_data, show_source=True ) + elif format_type == "feishu": + formatted_title = format_title_for_platform( + "feishu", title_data, show_source=True + ) elif format_type == "dingtalk": formatted_title = format_title_for_platform( "dingtalk", title_data, show_source=True @@ -3050,6 +3076,8 @@ def split_content_into_batches( separator = f"\n\n" elif format_type == "ntfy": separator = f"\n\n" + elif format_type == "feishu": + separator = f"\n{CONFIG['FEISHU_MESSAGE_SEPARATOR']}\n\n" elif format_type == "dingtalk": separator = f"\n---\n\n" @@ -3071,6 +3099,8 @@ def split_content_into_batches( ) elif format_type == "ntfy": new_header = f"\n\n🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n" + elif format_type == "feishu": + new_header = f"\n{CONFIG['FEISHU_MESSAGE_SEPARATOR']}\n\n🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n" elif format_type == "dingtalk": new_header = f"\n---\n\n🆕 **本次新增热点新闻** (共 {report_data['total_new_count']} 条)\n\n" @@ -3096,6 +3126,8 @@ def split_content_into_batches( source_header = f"{source_data['source_name']} ({len(source_data['titles'])} 条):\n\n" elif format_type == "ntfy": source_header = f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n" + elif format_type == "feishu": + source_header = f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n" elif format_type == "dingtalk": source_header = f"**{source_data['source_name']}** ({len(source_data['titles'])} 条):\n\n" @@ -3114,6 +3146,10 @@ def split_content_into_batches( formatted_title = format_title_for_platform( "telegram", title_data_copy, show_source=False ) + elif format_type == "feishu": + formatted_title = format_title_for_platform( + "feishu", title_data_copy, show_source=False + ) elif format_type == "dingtalk": formatted_title = format_title_for_platform( "dingtalk", title_data_copy, show_source=False @@ -3155,6 +3191,10 @@ def split_content_into_batches( formatted_title = format_title_for_platform( "telegram", title_data_copy, show_source=False ) + elif format_type == "feishu": + formatted_title = format_title_for_platform( + "feishu", title_data_copy, show_source=False + ) elif format_type == "dingtalk": formatted_title = format_title_for_platform( "dingtalk", title_data_copy, show_source=False @@ -3187,6 +3227,8 @@ def split_content_into_batches( failed_header = f"\n\n⚠️ 数据获取失败的平台:\n\n" elif format_type == "ntfy": failed_header = f"\n\n⚠️ **数据获取失败的平台:**\n\n" + elif format_type == "feishu": + failed_header = f"\n{CONFIG['FEISHU_MESSAGE_SEPARATOR']}\n\n⚠️ **数据获取失败的平台:**\n\n" elif format_type == "dingtalk": failed_header = f"\n---\n\n⚠️ **数据获取失败的平台:**\n\n" @@ -3204,7 +3246,9 @@ def split_content_into_batches( current_batch_has_content = True for i, id_value in enumerate(report_data["failed_ids"], 1): - if format_type == "dingtalk": + if format_type == "feishu": + failed_line = f" • {id_value}\n" + elif format_type == "dingtalk": failed_line = f" • **{id_value}**\n" else: failed_line = f" • {id_value}\n" @@ -3358,42 +3402,86 @@ def send_to_feishu( proxy_url: Optional[str] = None, mode: str = "daily", ) -> bool: - """发送到飞书""" + """发送到飞书(支持分批发送)""" headers = {"Content-Type": "application/json"} - - text_content = render_feishu_content(report_data, update_info, mode) - total_titles = sum( - len(stat["titles"]) for stat in report_data["stats"] if stat["count"] > 0 - ) - - now = get_beijing_time() - payload = { - "msg_type": "text", - "content": { - "total_titles": total_titles, - "timestamp": now.strftime("%Y-%m-%d %H:%M:%S"), - "report_type": report_type, - "text": text_content, - }, - } - proxies = None if proxy_url: proxies = {"http": proxy_url, "https": proxy_url} - try: - response = requests.post( - webhook_url, headers=headers, json=payload, proxies=proxies, timeout=30 + # 获取分批内容,使用飞书专用的批次大小 + batches = split_content_into_batches( + report_data, + "feishu", + update_info, + max_bytes=CONFIG.get("FEISHU_BATCH_SIZE", 29000), + mode=mode, + ) + + print(f"飞书消息分为 {len(batches)} 批次发送 [{report_type}]") + + # 逐批发送 + for i, batch_content in enumerate(batches, 1): + batch_size = len(batch_content.encode("utf-8")) + print( + f"发送飞书第 {i}/{len(batches)} 批次,大小:{batch_size} 字节 [{report_type}]" ) - if response.status_code == 200: - print(f"飞书通知发送成功 [{report_type}]") - return True - else: - print(f"飞书通知发送失败 [{report_type}],状态码:{response.status_code}") + + # 添加批次标识 + if len(batches) > 1: + batch_header = f"**[第 {i}/{len(batches)} 批次]**\n\n" + # 将批次标识插入到适当位置(在统计标题之后) + if "📊 **热点词汇统计**" in batch_content: + batch_content = batch_content.replace( + "📊 **热点词汇统计**\n\n", f"📊 **热点词汇统计** {batch_header}" + ) + else: + # 如果没有统计标题,直接在开头添加 + batch_content = batch_header + batch_content + + total_titles = sum( + len(stat["titles"]) for stat in report_data["stats"] if stat["count"] > 0 + ) + now = get_beijing_time() + + payload = { + "msg_type": "text", + "content": { + "total_titles": total_titles, + "timestamp": now.strftime("%Y-%m-%d %H:%M:%S"), + "report_type": report_type, + "text": batch_content, + }, + } + + try: + response = requests.post( + webhook_url, headers=headers, json=payload, proxies=proxies, timeout=30 + ) + if response.status_code == 200: + result = response.json() + # 检查飞书的响应状态 + if result.get("StatusCode") == 0 or result.get("code") == 0: + print(f"飞书第 {i}/{len(batches)} 批次发送成功 [{report_type}]") + # 批次间间隔 + if i < len(batches): + time.sleep(CONFIG["BATCH_SEND_INTERVAL"]) + else: + error_msg = result.get("msg") or result.get("StatusMessage", "未知错误") + print( + f"飞书第 {i}/{len(batches)} 批次发送失败 [{report_type}],错误:{error_msg}" + ) + return False + else: + print( + f"飞书第 {i}/{len(batches)} 批次发送失败 [{report_type}],状态码:{response.status_code}" + ) + return False + except Exception as e: + print(f"飞书第 {i}/{len(batches)} 批次发送出错 [{report_type}]:{e}") return False - except Exception as e: - print(f"飞书通知发送出错 [{report_type}]:{e}") - return False + + print(f"飞书所有 {len(batches)} 批次发送完成 [{report_type}]") + return True def send_to_dingtalk( diff --git a/readme.md b/readme.md index 4d181f0..d3f6505 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,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-v3.0.3-blue.svg)](https://github.com/sansan0/TrendRadar) +[![Version](https://img.shields.io/badge/version-v3.0.4-blue.svg)](https://github.com/sansan0/TrendRadar) [![MCP](https://img.shields.io/badge/MCP-v1.0.1-green.svg)](https://github.com/sansan0/TrendRadar) [![企业微信通知](https://img.shields.io/badge/企业微信-通知-00D4AA?style=flat-square)](https://work.weixin.qq.com/) @@ -522,14 +522,18 @@ GitHub 一键 Fork 即可使用,无需编程基础。 - 统一所有工具的时间参数格式 -### 2025/10/23 - v3.0.3 +### 2025/10/31 - v3.0.4 -- 扩大 ntfy 错误信息显示范围 +- 解决飞书因推送内容过长而产生的错误,实现了分批推送
👉 历史更新 +### 2025/10/23 - v3.0.3 + +- 扩大 ntfy 错误信息显示范围 + ### 2025/10/21 - v3.0.2 @@ -785,7 +789,7 @@ frequency_words.txt 文件增加了一个【必须词】功能,使用 + 号 ## 🚀 快速开始 -> 部署成功后,新闻数据一般一小时后才会更新,如想加快,可参照【第4步】手动测试配置效果 +> 配置完成后,新闻数据一小时后才会更新,如想加快,可参照【第4步】手动测试配置效果 1. **Fork 本项目**到你的 GitHub 账户 diff --git a/version b/version index 282895a..b38ebbf 100644 --- a/version +++ b/version @@ -1 +1 @@ -3.0.3 \ No newline at end of file +3.0.4 \ No newline at end of file