refactor(feishu): drop dead helper, allow configurable card header, tidy regex

This commit is contained in:
matevip 2026-05-20 10:13:53 +08:00
parent 56ab7b3095
commit 8d91197a46
2 changed files with 23 additions and 10 deletions

View File

@ -19,7 +19,7 @@ final class FeishuCardFormatter {
private static final Pattern HEADER = Pattern.compile("(?m)^#{1,6}\\s");
private static final Pattern TABLE_SEP = Pattern.compile("(?m)^\\|[\\s|:-]+\\|\\s*$");
private static final Pattern JSON_CODE_BLOCK =
Pattern.compile("(?s)```(?:json)?\\s*([\\[{][\\s\\S]*?[\\]\\}])\\s*```");
Pattern.compile("(?s)```(?:json)?\\s*([\\[{][\\s\\S]*?[\\]}])\\s*```");
private FeishuCardFormatter() {}
@ -68,24 +68,35 @@ final class FeishuCardFormatter {
.count();
}
private static String extractJsonCodeBlock(String s) {
Matcher m = JSON_CODE_BLOCK.matcher(s);
return m.find() ? m.group(1).strip() : null;
}
// ==================== 渲染层 ====================
/** Default markdown card header used when the channel doesn't override it. */
static final String DEFAULT_MARKDOWN_HEADER = "AI 助手";
static Map<String, Object> render(String content, ContentFormat format) {
return render(content, format, DEFAULT_MARKDOWN_HEADER);
}
/**
* Renders the card for the given content/format pair. {@code markdownHeader}
* controls the title shown above markdown cards pass null or blank to
* suppress the header entirely. JSON and long-text/plain-text layouts ignore
* the header (they have never carried one).
*/
static Map<String, Object> render(String content, ContentFormat format, String markdownHeader) {
return switch (format) {
case JSON -> renderJson(content);
case MARKDOWN -> renderMarkdown(content);
case MARKDOWN -> renderMarkdown(content, markdownHeader);
case LONG_TEXT, PLAIN_TEXT -> renderLongText(content);
};
}
private static Map<String, Object> renderMarkdown(String content) {
private static Map<String, Object> renderMarkdown(String content, String headerText) {
Map<String, Object> header = (headerText == null || headerText.isBlank())
? null
: Map.of("title", Map.of("tag", "plain_text", "content", headerText));
return cardOf(
Map.of("title", Map.of("tag", "plain_text", "content", "AI 助手")),
header,
List.of(Map.of(
"tag", "div",
"text", Map.of("tag", "lark_md", "content", content)

View File

@ -54,6 +54,7 @@ import java.util.concurrent.TimeUnit;
* - stale_event_threshold_seconds: 过滤旧事件阈值默认 300 禁用
* - card_format: 卡片格式化模式 "auto"默认| "always" | "never"
* auto: 根据内容自动检测always: 全部包卡片never: 全部纯文本降级/调试用
* - card_header: Markdown 卡片 header 文案默认 "AI 助手"设为空串可隐藏 header
* - require_mention: 群聊中是否需要 @机器人 才响应默认 false
* true: 仅当消息中 @了机器人才处理通过飞书 mentions 字段精确判断无需配置 botPrefix
*
@ -1413,7 +1414,8 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter {
boolean preferCard = "always".equals(cardFormat) || fmt != FeishuCardFormatter.ContentFormat.PLAIN_TEXT;
if (preferCard) {
boolean sent = sendCard(targetId, FeishuCardFormatter.render(content, fmt));
String cardHeader = getConfigString("card_header", FeishuCardFormatter.DEFAULT_MARKDOWN_HEADER);
boolean sent = sendCard(targetId, FeishuCardFormatter.render(content, fmt, cardHeader));
if (sent) return;
// Card path bailed (oversized payload, null guard, or network error)
// fall through to text so the user doesn't get nothing.