2020import 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
33993487def send_to_dingtalk (
0 commit comments