From c3bfe32fa3e6154b69f7fcc29a253e70a65e08be Mon Sep 17 00:00:00 2001 From: ooodc <549496103@qq.com> Date: Sat, 6 Jun 2026 10:24:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E9=95=BF=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=88=86=E5=9D=97=E5=8F=91=E9=80=81=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=20WeChat=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E4=BC=A0=E9=80=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/channels/wechat.rs | 87 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/src/channels/wechat.rs b/src/channels/wechat.rs index 79c2e63..1c21f83 100644 --- a/src/channels/wechat.rs +++ b/src/channels/wechat.rs @@ -5,7 +5,7 @@ use std::sync::{ Arc, atomic::{AtomicBool, Ordering}, }; -use std::time::UNIX_EPOCH; +use std::time::{Duration, UNIX_EPOCH}; use async_trait::async_trait; use futures_util::FutureExt; @@ -313,15 +313,37 @@ impl Channel for WechatChannel { let mut text_sent = false; if !text.is_empty() { - self.bot.send(&msg.chat_id, &text).await.map_err(|error| { - ChannelError::SendError(format!("WeChat text send failed: {}", error)) - })?; - tracing::info!( - channel = %self.name, - chat_id = %msg.chat_id, - content_len = text.len(), - "WeChat text message sent" - ); + let chunks = split_text(&text, MAX_WECHAT_CHUNK_CHARS); + if chunks.len() > 1 { + tracing::info!( + channel = %self.name, + chat_id = %msg.chat_id, + total_chars = text.len(), + chunk_count = chunks.len(), + "WeChat: splitting long message into chunks" + ); + } + for (i, chunk) in chunks.iter().enumerate() { + if i > 0 { + tokio::time::sleep(Duration::from_millis(CHUNK_SEND_INTERVAL_MS)).await; + } + self.bot.send(&msg.chat_id, chunk).await.map_err(|error| { + ChannelError::SendError(format!( + "WeChat text send failed (chunk {}/{}): {}", + i + 1, + chunks.len(), + error + )) + })?; + tracing::info!( + channel = %self.name, + chat_id = %msg.chat_id, + chunk = i + 1, + total_chunks = chunks.len(), + content_len = chunk.len(), + "WeChat text message sent" + ); + } text_sent = true; } @@ -356,6 +378,51 @@ impl Channel for WechatChannel { } } +/// Split text into chunks suitable for WeChat delivery. +/// Prefers splitting at paragraph breaks, then newlines, then sentence boundaries. +const MAX_WECHAT_CHUNK_CHARS: usize = 2000; +const CHUNK_SEND_INTERVAL_MS: u64 = 500; + +fn split_text(text: &str, limit: usize) -> Vec { + if text.len() <= limit { + return vec![text.to_string()]; + } + let mut chunks = Vec::new(); + let mut remaining = text; + while !remaining.is_empty() { + if remaining.len() <= limit { + chunks.push(remaining.to_string()); + break; + } + let end = remaining.floor_char_boundary(limit); + let window = &remaining[..end]; + let cut = window + .rfind("\n\n") + .filter(|&i| i > end * 3 / 10) + .map(|i| i + 2) + .or_else(|| { + window + .rfind('\n') + .filter(|&i| i > end * 3 / 10) + .map(|i| i + 1) + }) + .or_else(|| { + window + .rfind('。') + .filter(|&i| i > end * 3 / 10) + .map(|i| i + 3) + }) + .unwrap_or(end); + chunks.push(remaining[..cut].to_string()); + remaining = &remaining[cut..]; + } + if chunks.is_empty() { + vec![String::new()] + } else { + chunks + } +} + #[cfg(test)] mod tests { use super::*;