feat: 实现长消息分块发送功能,优化 WeChat 消息传递
This commit is contained in:
parent
fb90641774
commit
c3bfe32fa3
@ -5,7 +5,7 @@ use std::sync::{
|
|||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
@ -313,15 +313,37 @@ impl Channel for WechatChannel {
|
|||||||
let mut text_sent = false;
|
let mut text_sent = false;
|
||||||
|
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
self.bot.send(&msg.chat_id, &text).await.map_err(|error| {
|
let chunks = split_text(&text, MAX_WECHAT_CHUNK_CHARS);
|
||||||
ChannelError::SendError(format!("WeChat text send failed: {}", error))
|
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!(
|
tracing::info!(
|
||||||
channel = %self.name,
|
channel = %self.name,
|
||||||
chat_id = %msg.chat_id,
|
chat_id = %msg.chat_id,
|
||||||
content_len = text.len(),
|
chunk = i + 1,
|
||||||
|
total_chunks = chunks.len(),
|
||||||
|
content_len = chunk.len(),
|
||||||
"WeChat text message sent"
|
"WeChat text message sent"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
text_sent = true;
|
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<String> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user