mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(feishu): split long messages instead of silent truncate
This commit is contained in:
parent
eaacb3a78f
commit
fcab47ffb9
@ -1184,16 +1184,38 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter {
|
||||
|
||||
// ==================== 消息发送 ====================
|
||||
|
||||
/**
|
||||
* Conservative per-message char ceiling. Feishu's documented limit is on
|
||||
* the encoded JSON body (~150KB), but UTF-8 Chinese is 3 bytes/char and
|
||||
* JSON escape adds further overhead, so a 4000-char chunk is comfortably
|
||||
* under any realistic limit and also gives readable IM chunking instead
|
||||
* of one wall of text. Split at paragraph / line boundaries when possible.
|
||||
*/
|
||||
static final int MAX_TEXT_MESSAGE_CHARS = 4000;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String targetId, String content) {
|
||||
if (httpClient == null) {
|
||||
log.warn("[feishu] Channel not started, cannot send message");
|
||||
return;
|
||||
}
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureTokenValid();
|
||||
String apiBase = getApiBaseUrl();
|
||||
List<String> chunks = splitTextForFeishu(content, MAX_TEXT_MESSAGE_CHARS);
|
||||
if (chunks.size() > 1) {
|
||||
log.info("[feishu] Splitting message into {} chunks ({} chars total) before send",
|
||||
chunks.size(), content.length());
|
||||
}
|
||||
for (String chunk : chunks) {
|
||||
sendOneTextChunk(targetId, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendOneTextChunk(String targetId, String content) {
|
||||
String apiBase = getApiBaseUrl();
|
||||
try {
|
||||
String jsonBody = objectMapper.writeValueAsString(Map.of(
|
||||
"receive_id", targetId,
|
||||
@ -1212,7 +1234,7 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter {
|
||||
if (response.statusCode() != 200) {
|
||||
log.warn("[feishu] Send message failed: status={}, body={}", response.statusCode(), response.body());
|
||||
} else {
|
||||
log.debug("[feishu] Message sent to chat_id={}", targetId);
|
||||
log.debug("[feishu] Message sent to chat_id={} ({} chars)", targetId, content.length());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -1220,6 +1242,52 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a possibly-oversized message into chunks no larger than
|
||||
* {@code maxChars}, preferring paragraph (\n\n) then line (\n) then
|
||||
* whitespace boundaries. Hard-cuts as a last resort so we never silently
|
||||
* drop content.
|
||||
*/
|
||||
static List<String> splitTextForFeishu(String content, int maxChars) {
|
||||
if (content == null || content.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
if (content.length() <= maxChars) {
|
||||
return List.of(content);
|
||||
}
|
||||
List<String> chunks = new ArrayList<>();
|
||||
int idx = 0;
|
||||
int n = content.length();
|
||||
while (idx < n) {
|
||||
int end = Math.min(idx + maxChars, n);
|
||||
if (end < n) {
|
||||
int boundary = -1;
|
||||
int paragraphCut = content.lastIndexOf("\n\n", end);
|
||||
if (paragraphCut > idx + maxChars / 2) {
|
||||
boundary = paragraphCut + 2;
|
||||
}
|
||||
if (boundary < 0) {
|
||||
int lineCut = content.lastIndexOf('\n', end);
|
||||
if (lineCut > idx + maxChars / 2) {
|
||||
boundary = lineCut + 1;
|
||||
}
|
||||
}
|
||||
if (boundary < 0) {
|
||||
int spaceCut = content.lastIndexOf(' ', end);
|
||||
if (spaceCut > idx + maxChars / 2) {
|
||||
boundary = spaceCut + 1;
|
||||
}
|
||||
}
|
||||
if (boundary > idx) {
|
||||
end = boundary;
|
||||
}
|
||||
}
|
||||
chunks.add(content.substring(idx, end));
|
||||
idx = end;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendContentParts(String targetId, List<MessageContentPart> parts) {
|
||||
if (httpClient == null) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user